Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/wix/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Postgresql 将文本表示法中的十六进制数转换为十进制数_Postgresql_Types_Casting_Hex - Fatal编程技术网

Postgresql 将文本表示法中的十六进制数转换为十进制数

Postgresql 将文本表示法中的十六进制数转换为十进制数,postgresql,types,casting,hex,Postgresql,Types,Casting,Hex,我正在尝试使用PostgreSQL 9.1将十六进制转换为十进制 使用此查询: SELECT to_number('DEADBEEF', 'FMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'); 我得到以下错误: ERROR: invalid input syntax for type numeric: " " 我做错了什么?您有两个直接的问题: 不懂十六进制 X在to_number格式字符串中没有任何意义,任何没有意义的内容显然意味着“跳过字符”

我正在尝试使用PostgreSQL 9.1将十六进制转换为十进制

使用此查询:

SELECT to_number('DEADBEEF', 'FMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
我得到以下错误:

ERROR:  invalid input syntax for type numeric: " "

我做错了什么?

您有两个直接的问题:

  • 不懂十六进制
  • X
    to_number
    格式字符串中没有任何意义,任何没有意义的内容显然意味着“跳过字符”
  • 我没有(2)的权威性理由,只是经验证据:

    => SELECT to_number('123', 'X999');
     to_number 
    -----------
            23
    (1 row)
    
    => SELECT to_number('123', 'XX999');
     to_number 
    -----------
             3
    
    文档中提到了双引号模式的行为:

    到日期
    到编号
    到时间戳
    中,双引号字符串跳过字符串中包含的输入字符数,例如
    “XX”
    跳过两个输入字符

    但非格式化字符的非引号字符的行为似乎未指定

    在任何情况下,
    to_number
    都不是将十六进制转换为数字的正确工具,您可以这样说:

    select x'deadbeef'::int;
    
    因此,也许对您来说效果更好:

    CREATE OR REPLACE FUNCTION hex_to_int(hexval varchar) RETURNS integer AS $$
    DECLARE
        result  int;
    BEGIN
        EXECUTE 'SELECT x' || quote_literal(hexval) || '::int' INTO result;
        RETURN result;
    END;
    $$ LANGUAGE plpgsql IMMUTABLE STRICT;
    
    然后:

    **为了避免整数溢出错误中出现这样的负数,请使用bigint而不是int来容纳更大的十六进制数(如IP地址)。

    没有动态SQL的方法 没有从表示中的十六进制数转换为数字类型,但我们可以将其用作航路点。有从位字符串(
    bit(n)
    )到整数类型(
    int2
    int4
    int8
    )的未记录的强制转换-内部表示是二进制兼容的

    这依赖于位类型输入的一些未记录的行为 转换器,但我看不出有什么理由认为它会坏。可能是 更大的问题是它需要PG>=8.3,因为没有文本 在那之前先投点球

    整数
    最多8个十六进制数字 最多可将8个十六进制数字转换为
    位(32)
    ,然后强制转换为(标准4字节整数):

    Postgres使用有符号整数类型,因此十六进制数
    '7fffffff'
    上溢出为负整数。这仍然是一种有效的、独特的表述,但含义不同。如果这很重要,请切换到
    bigint
    ;见下文

    对于8个以上的十六进制数字,最不重要的字符(超出右侧)将被截断

    位字符串中的4位编码1个十六进制数字。已知长度的十六进制数可以直接转换到相应的
    位(n)
    。或者,如图所示,用前导零(
    0
    )填充长度未知的十六进制数,并转换为
    位(32)
    。具有7个十六进制数字和
    int
    或8个数字和
    bigint
    的示例:

    SELECT ('x'|| 'deafbee')::bit(28)::int
         , ('x'|| 'deadbeef')::bit(32)::bigint;
    
    int4 | int8
    -----------+------------
    233503726 | 3735928559
    
    bigint
    最多16个十六进制数字 最多可将16个十六进制数字转换为
    位(64)
    ,然后强制转换为(
    int8
    ,8字节整数)-在上半部分再次溢出为负数:

    SELECT ('x' || lpad(hex, 16, '0'))::bit(64)::bigint AS int8_val
    FROM  (
       VALUES
          ('ff'::text)
        , ('7fffffff')
        , ('80000000')
        , ('deadbeef')
        , ('7fffffffffffffff')
        , ('8000000000000000')     -- overflow into negative number
        , ('ffffffffffffffff')
        , ('ffffffffffffffff123')  -- too long
       ) t(hex);
    uuid
    用于最多32个十六进制数字 Postgres数据类型不是数字类型。但它是标准Postgres中最有效的类型,最多可以存储32个十六进制数字,只占用16字节的存储空间。有一个从
    text
    uuid
    直接强制转换(不需要
    位(n)
    作为航路点),但只需要32个十六进制数字

    SELECT lpad(hex, 32, '0')::uuid AS uuid_val
    FROM  (
       VALUES ('ff'::text)
            , ('deadbeef')
            , ('ffffffffffffffff')
            , ('ffffffffffffffffffffffffffffffff')
            , ('ffffffffffffffffffffffffffffffff123') -- too long
       ) t(hex);
    如您所见,标准输出是一个十六进制数字字符串,带有UUID的典型分隔符

    md5散列 这对于存储md5哈希值特别有用:

    SELECT md5('Store hash for long string, maybe for index?')::uuid AS md5_hash;
    
    md5\u散列
    --------------------------------------
    02e10e94-e895-616e-8e23-bb7f8025da42
    
    见:


    如果其他人坚持使用PG8.2,这里有另一种方法

    bigint版本:

    create or replace function hex_to_bigint(hexval text) returns bigint as $$
    select
      (get_byte(x,0)::int8<<(7*8)) |
      (get_byte(x,1)::int8<<(6*8)) |
      (get_byte(x,2)::int8<<(5*8)) |
      (get_byte(x,3)::int8<<(4*8)) |
      (get_byte(x,4)::int8<<(3*8)) |
      (get_byte(x,5)::int8<<(2*8)) |
      (get_byte(x,6)::int8<<(1*8)) |
      (get_byte(x,7)::int8)
    from (
      select decode(lpad($1, 16, '0'), 'hex') as x
    ) as a;
    $$
    language sql strict immutable;
    
    create or replace function hex_to_int(hexval text) returns int as $$
    select
      (get_byte(x,0)::int<<(3*8)) |
      (get_byte(x,1)::int<<(2*8)) |
      (get_byte(x,2)::int<<(1*8)) |
      (get_byte(x,3)::int)
    from (
      select decode(lpad($1, 8, '0'), 'hex') as x
    ) as a;
    $$
    language sql strict immutable;
    
    创建或替换函数hex_to_bigint(hexval text)将bigint返回为$$
    选择
    (获取字节(x,0)::int8
    在内部,
    pg bignum
    使用SSL库来处理大数字。这种方法没有其他答案中提到的缺点,也没有数字。plpgsql也不会减慢它的速度。它速度快,可以处理任何大小的数字。测试用例取自Erwin的答案进行比较

    CREATE EXTENSION bignum;
    
    SELECT hex, bn_in_hex(hex::cstring) 
    FROM   (
       VALUES ('ff'::text)
            , ('7fffffff')
            , ('80000000')
            , ('deadbeef')
            , ('7fffffffffffffff')
            , ('8000000000000000')
            , ('ffffffffffffffff')
            , ('ffffffffffffffff123')
       ) t(hex);
    
             hex         |        bn_in_hex        
    ---------------------+-------------------------
     ff                  | 255
     7fffffff            | 2147483647
     80000000            | 2147483648
     deadbeef            | 3735928559
     7fffffffffffffff    | 9223372036854775807
     8000000000000000    | 9223372036854775808
     ffffffffffffffff    | 18446744073709551615
     ffffffffffffffff123 | 75557863725914323415331
    (8 rows)
    

    您可以使用
    bn\u in_hex('deadbeef'):text::numeric
    将类型设置为numeric,这是一个使用
    numeric
    的版本,因此它可以处理任意大的十六进制字符串:

    create function hex_to_decimal(hex_string text)
    returns text
    language plpgsql immutable as $pgsql$
    declare
        bits bit varying;
        result numeric := 0;
        exponent numeric := 0;
        chunk_size integer := 31;
        start integer;
    begin
        execute 'SELECT x' || quote_literal(hex_string) INTO bits;
        while length(bits) > 0 loop
            start := greatest(1, length(bits) - chunk_size);
            result := result + (substring(bits from start for chunk_size)::bigint)::numeric * pow(2::numeric, exponent);
            exponent := exponent + chunk_size;
            bits := substring(bits from 1 for greatest(0, length(bits) - chunk_size));
        end loop;
        return trunc(result, 0);
    end
    $pgsql$;
    
    例如:

    =# select hex_to_decimal('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
    32592575621351777380295131014550050576823494298654980010178247189670100796213387298934358015
    

    你能用这个现代答案重新评估这个问题吗?用
    pg bignum
    Saviour!这正是我所需要的。有趣的是,遗憾的是大多数托管DBs只允许有限的已批准扩展列表。不幸的是,这个解决方案不在官方软件包中(如中所述)我发现这在可靠性、x平台可安装性等方面有点风险。它也没有很好的文档记录。我认为bignum真的属于core!哈哈,奇怪,
    to_number
    支持最愚蠢的东西,比如罗马数字、序数后缀等等——上一次有人需要它是什么时候了。:)但没有十六进制?!
    create function hex_to_decimal(hex_string text)
    returns text
    language plpgsql immutable as $pgsql$
    declare
        bits bit varying;
        result numeric := 0;
        exponent numeric := 0;
        chunk_size integer := 31;
        start integer;
    begin
        execute 'SELECT x' || quote_literal(hex_string) INTO bits;
        while length(bits) > 0 loop
            start := greatest(1, length(bits) - chunk_size);
            result := result + (substring(bits from start for chunk_size)::bigint)::numeric * pow(2::numeric, exponent);
            exponent := exponent + chunk_size;
            bits := substring(bits from 1 for greatest(0, length(bits) - chunk_size));
        end loop;
        return trunc(result, 0);
    end
    $pgsql$;
    
    =# select hex_to_decimal('ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff');
    32592575621351777380295131014550050576823494298654980010178247189670100796213387298934358015