将hex文本表示转换为十进制数

我想使用PostgreSQL 9.1将hex转换为十进制

与此查询:

SELECT to_number('DEADBEEF', 'FMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX'); 

我得到以下错误:

 ERROR: invalid input syntax for type numeric: " " 

我究竟做错了什么?

你有两个直接的问题:

  1. to_number不理解hex。
  2. Xto_number格式string中没有任何意义,任何没有含义的东西显然意味着“跳过字符”。

(2)我没有权威的理由,只是经validation据:

 => SELECT to_number('123', 'X999'); to_number ----------- 23 (1 row) => SELECT to_number('123', 'XX999'); to_number ----------- 3 

文档提到了双引号模式应该如何performance:

to_dateto_numberto_timestamp ,双引号string会跳过string中包含的input字符数,例如"XX"跳过两个input字符。

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

在任何情况下, to_number不是hex转换为数字的正确工具,你想要这样说:

 select x'deadbeef'::int; 

所以这个函数可能对你更好:

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

然后:

 => select hex_to_int('DEADBEEF'); hex_to_int ------------ -559038737 ** (1 row) 

**要避免整数溢出错误这样的负数,请使用bigint而不是int来容纳较大的hex数(如IP地址)。

没有dynamicSQL的方法

最大。 8个hex数字

text表示中不会有hex数字转换为数字types,但是我们可以使用bit(n)作为航点。 位串中的4位编码1个hex数字。 从位串bit(32) (最多8个hex数字)到integer (标准的4字节整数)有一个未公开的转换 – 内部表示是二进制兼容的。

 SELECT ('x' || lpad(hex, 8, '0'))::bit(32)::int AS int_val FROM ( VALUES ('1'::text) ,('f') ,('100') ,('7fffffff') ,('80000000') ,('deadbeef') ,('ffffffff') ) AS t(hex); 

结果:

  int_val ------------ 1 15 256 2147483647 -2147483648 -559038737 -1 

4个字节足以将所有hex数字编码为8位数字,但Postgres中的integer是带符号的types,所以'7fffffff'之上'7fffffff'hex数字溢出到一个负的int数字中。 这仍然是一个独特的代表,但意义是不同的。 如果这个问题切换到bigint ,请看下面。

对于长度未知的hex数字,我们需要填充前导零,如下所示,将其转换为bit(32) 。 对于已知长度的数字,我们可以调整长度说明符。 具有7个hex数字和int或8个数字和bigint示例:

 SELECT ('x'|| 'deafbee')::bit(28)::int , ('x'|| 'deadbeef')::bit(32)::bigint; int4 | int8 -----------+------------ 233503726 | 3735928559 

最大。 hex数字

使用bigintint8字节整数)最多16个hex数字 – 溢出到上半部分的负数:

 SELECT ('x' || lpad(hex, 16, '0'))::bit(64)::bigint AS int8_val FROM ( VALUES ('ff'::text) , ('7fffffff') , ('80000000') , ('deadbeef') , ('7fffffffffffffff') , ('8000000000000000') , ('ffffffffffffffff') , ('ffffffffffffffff123') -- too long ) t(hex); 

结果:

  int8_val --------------------- 255 2147483647 2147483648 3735928559 9223372036854775807 -9223372036854775808 -1 -1 

对于16位以上的hex数字,最不重要的字符(超出右边)被截断

这个angular色依赖于无证的行为 ,我在这里引用汤姆·莱恩的话 :

这是依赖于位型input转换器的一些无法certificate的行为,但我没有理由期望会中断。 一个可能更大的问题是,它需要PG> = 8.3,因为在此之前没有要投射的文本。

用于最大的UUID 32位hex数字

Postgres的uuid数据types不是一个数字types ,所以这偏离了问题。 但它是标准Postgres中最高效的types,最多可以存储32位hex数字,只占用16个字节的存储空间。 有一个直接强制转换 ,但正好需要 32位hex数字。

 SELECT lpad(hex, 32, '0')::uuid AS uuid_val FROM ( VALUES ('ff'::text) , ('deadbeef') , ('ffffffffffffffff') , ('ffffffffffffffffffffffffffffffff') , ('ffffffffffffffffffffffffffffffff123') -- too long ) t(hex); 

结果:

  uuid_val -------------------------------------- 00000000-0000-0000-0000-0000000000ff 00000000-0000-0000-0000-0000deadbeef 00000000-0000-0000-ffff-ffffffffffff ffffffff-ffff-ffff-ffff-ffffffffffff ffffffff-ffff-ffff-ffff-ffffffffffff 

正如你所看到的,标准输出是一个hex数字的string,典型的UUID分隔符。

MD5散列

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

 SELECT md5('Store hash for long string, maybe for index?')::uuid AS md5_hash 

结果:

  md5_hash -------------------------------------- 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; 

int版本:

 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;