将文本表示形式的十六进制转换为十进制数
我尝试使用 PostgreSQL 9.1
通过此查询将十六进制转换为十进制:
SELECT to_number('DEADBEEF', 'FMXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX');
我收到以下错误:
ERROR: invalid input syntax for type numeric: " "
我做错了什么?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(11)
这是将十六进制转换为字符串的流行方法...然后您可以检查它是否是数字类型
返回
Here is a poper way to convert hex to string... then you can check whether it's a numric type or not
returns
这是另一个使用数字的版本,因此它可以处理任意大的十六进制字符串:
例如:
例如:
Here is a other version which uses numeric, so it can handle arbitrarily large hex strings:
For example:
For example:
在未来的 PostgreSQL 版本中,当/如果 Dean Rasheed 的补丁 0001-Add-non-decimal-integer-support-to-type-numeric.patch 被提交时,这可以被简化:
In a future PostgreSQL version, when/if Dean Rasheed's patch 0001-Add-non-decimal-integer-support-to-type-numeric.patch gets committed, this can be simplified:
Postgres 16 或更高版本
.. 接受以下形式的非十进制数字常量(十六进制、八进制、二进制):
与其他 "数字常量",默认为不会从级数中溢出的最小数字数据类型
整数
→bigint
→数字
。所以:这隐式适用于从
text
到int
/bigint
/numeric
的转换,所以现在您可以:Zegarek 在补充答案中指出了这一点。
Postgres 15 或更早版本
使用
float
作为垫脚石我后来才意识到浮点类型(
float
,real
)至少从那时起就支持相同的语法Postgres 9.4,可能更长:我们可以将其用作垫脚石:
简单而快速,但要警惕非常大的数字的舍入错误。
float
最多只能精确表示 15 位十进制数字。这可以可靠地转换为最多 12 个十六进制数字。准确地说,最多为十六进制38d7ea4c67fff
。除此之外,最低有效数字设置为 0。此外,Postgres 16 语法更简单、更快。
原始答案
text
中没有十六进制数字的转换 表示为数字类型,但我们可以使用bit(n)
作为航路点。有从位字符串 (bit(n)
) 到整数类型 (int2
、int4
、< code>int8) - 内部表示是二进制兼容的。 引用汤姆·莱恩:整数
最大。 8 个十六进制数字最多可以将 8 个十六进制数字转换为
bit(32)
,然后强制转换为integer
(标准 4 字节整数):Postgres 使用有符号整数类型,因此十六进制数字上面的
'7fffffff'
溢出为负整数数字。这仍然是一个有效的、唯一的表示,但含义有所不同。如果这很重要,请切换到bigint;见下文。对于超过 8 个十六进制数字,最不重要的字符(超出右侧)将被截断。
位串中的 4 位 编码 1 个十六进制数字。已知长度的十六进制数可以直接转换为相应的
bit(n)
。或者,按照演示的方式用前导零 (0
) 填充未知长度的十六进制数字,并将其转换为bit(32)
。 7 个十六进制数字和int
或 8 个数字和bigint
的示例:bigint
最大。 16 个十六进制数字最多可以将 16 个十六进制数字转换为
bit(64)
,然后强制转换为bigint
(int8
,8 字节整数) - 溢出到负数上半次:相关运算
逆运算
要转换回
integer
或bigint
,请使用内置(重载)函数to_hex()
:uuid
最大。 32 个十六进制数字Postgres
uuid
数据类型不是数字类型。但它是标准 Postgres 中最有效的类型,最多可存储 32 个十六进制数字,仅占用 16 个字节的存储空间。有从text
到uuid
的直接转换(不需要bit(n)
作为路径点),但是正好需要 32 个十六进制数字。正如您所看到的,标准输出是一串十六进制数字,带有典型的 UUID 分隔符。
md5 哈希
这对于存储md5 哈希特别有用:
请参阅:
Postgres 16 or newer
.. accepts non-decimal numeric constants (hexadecimal, octal, binary) in the form:
Like other "Numeric constants", it defaults to the smallest numeric data type that won't overflow from the progression
integer
→bigint
→numeric
. So:This implicitly applies to the cast from
text
toint
/bigint
/numeric
, so now you can:Zegarek pointed that out in an added answer.
Postgres 15 or older
Use
float
as stepping stoneI only realized later that floating point types (
float
,real
) support the same syntax at least since Postgres 9.4, probably longer:We can use that as stepping stone:
Simple and fast, but be wary of rounding errors with very big numbers.
float
can only represent up 15 decimal digits precisely. That translates to up to 12 hex digits reliably. Up to hex38d7ea4c67fff
, to be precise. Beyond that, least significant digits are set to 0.Also, Postgres 16 syntax is simpler and faster.
Original answer
There is no cast from hex numbers in
text
representation to a numeric type, but we can usebit(n)
as waypoint. There are undocumented casts from bit strings (bit(n)
) to integer types (int2
,int4
,int8
) - the internal representation is binary compatible. Quoting Tom Lane:integer
for max. 8 hex digitsUp to 8 hex digits can be converted to
bit(32)
and then coerced tointeger
(standard 4-byte integer):Postgres uses a signed integer type, so hex numbers above
'7fffffff'
overflow into negative integer numbers. This is still a valid, unique representation but the meaning is different. If that matters, switch tobigint
; see below.For more than 8 hex digits the least significant characters (excess to the right) get truncated.
4 bits in a bit string encode 1 hex digit. Hex numbers of known length can be cast to the respective
bit(n)
directly. Alternatively, pad hex numbers of unknown length with leading zeros (0
) as demonstrated and cast tobit(32)
. Example with 7 hex digits andint
or 8 digits andbigint
:bigint
for max. 16 hex digitsUp to 16 hex digits can be converted to
bit(64)
and then coerced tobigint
(int8
, 8-byte integer) - overflowing into negative numbers in the upper half again:Related operations
Inverse
To convert back either
integer
orbigint
, use the built-in (overloaded) functionto_hex()
:uuid
for max. 32 hex digitsThe Postgres
uuid
data type is not a numeric type. But it's the most efficient type in standard Postgres to store up to 32 hex digits, only occupying 16 bytes of storage. There is a direct cast fromtext
touuid
(no need forbit(n)
as waypoint), but exactly 32 hex digits are required.As you can see, standard output is a string of hex digits with typical separators for UUID.
md5 hash
This is particularly useful to store md5 hashes:
See:
您有两个直接问题:
to_number
不理解十六进制。X
在to_number
格式字符串中没有任何含义,任何没有含义的内容显然都意味着“跳过一个字符”。我没有对(2)的权威论证,只是经验证据:
文档提到了双引号模式应该如何表现:
但不格式化字符的非引号字符的行为似乎未指定。
无论如何,
to_number
都不是将十六进制转换为数字的正确工具,您想说这样的话:所以也许这个函数会更好地为您工作:
然后:
** 为了避免整数溢出错误中出现这样的负数,请使用 bigint 而不是 int容纳更大的十六进制数字(如 IP 地址)。
You have two immediate problems:
to_number
doesn't understand hexadecimal.X
doesn't have any meaning in ato_number
format string and anything without a meaning apparently means "skip a character".I don't have an authoritative justification for (2), just empirical evidence:
The documentation mentions how double quoted patterns are supposed to behave:
but the behavior of non-quoted characters that are not formatting characters appears to be unspecified.
In any case,
to_number
isn't the right tool for converting hex to numbers, you want to say something like this:so perhaps this function will work better for you:
Then:
** To avoid negative numbers like this from integer overflow error, use bigint instead of int to accommodate larger hex numbers (like IP addresses).
pg-bignum
在内部,
pg-bignum
使用 SSL 库处理大数。此方法没有其他数字答案中提到的缺点。 plpgsql 也没有减慢它的速度。它速度很快,并且适用于任何大小的数量。测试用例取自 Erwin 的答案进行比较,您可以使用 bn_in_hex('deadbeef')::text::numeric 将类型获取为数字。
pg-bignum
Internally,
pg-bignum
uses the SSL library for big numbers. This method has none of the drawbacks mentioned in the other answers with numeric. Nor is it slowed down by plpgsql. It's fast and it works with a number of any size. Test case taken from Erwin's answer for comparison,You can get the type to numeric using
bn_in_hex('deadbeef')::text::numeric
.这是一个使用
numeric
的版本,因此它可以处理任意大的十六进制字符串:例如:
Here is a version which uses
numeric
, so it can handle arbitrarily large hex strings:For example:
如果其他人被 PG8.2 困住了,这里有另一种方法。
bigint 版本:
int 版本:
If anybody else is stuck with PG8.2, here is another way to do it.
bigint version:
int version:
David Wolever 的函数 hex_to_Decimal 有错误。
这是该函数的固定版本:
这里有很多将 HEX 转换为 Number 的示例,但大多数都对 HEX 的长度有限制。
我需要解决“篮子”上不同数据库(Oracle 和 Postgres)中相同记录的均匀分布问题。
在这种情况下,不同数据库中的相同记录应该落入相同的处理“篮子”中。记录的标识符是字符串。
Oracle有一个ORA_HASH函数。但Postgres中没有这样的功能。通过使用相同的 md5 缓存函数解决了该问题,该函数生成 32 个符号的十六进制长度。只有上述功能有帮助,感谢 David Wolever。
对于 Oracle,过滤条件为:
MOD(TO_NUMBER(standard_hash(ID, 'MD5'), 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'), ${processCount}) = ${processNumber}
对于 Postgres - <代码>hex_to_Decimal(MD5(ID)) % ${进程数} = ${进程数}
David Wolever's function hex_to_Decimal has a mistake.
Here is the fixed version of the function:
There are many examples of the HEX translation into Number here, but most of them have a restriction on the length of HEX.
I needed to solve the problem of a uniform distribution of identical records in different databases (Oracle and Postgres) on the "baskets".
In this case, the same records in different databases should fall into the same "baskets" of processing. The identifier of the records is string.
Oracle has an ORA_HASH function. But there is no such function in Postgres. The problem was solved by using the same md5 caching functions, which generates a 32-symbol hex long. Only the above function helped, thanks to David Wolever.
For Oracle, the filtering condition turned out to be:
MOD(TO_NUMBER(standard_hash(ID, 'MD5'), 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'), ${processCount}) = ${processNumber}
For Postgres -
hex_to_Decimal(MD5(ID)) % ${Processcount} = ${Processnumber}
从 PostgreSQL 16.0 开始,在其前面添加
'0x'
并表示它是一个数字
:demo at db<>fiddle
当输入(或当没人看时注入/插值),可以跳过
||
和引号'
:也适用于八进制和二进制:
Since PostgreSQL 16.0, it's enough to add
'0x'
in front of it and say it's anumeric
:demo at db<>fiddle
When typing (or injecting/interpolating when noone's looking),
||
and quotes'
can be skipped:Works for octal and binary, too:
这是另一种实现:
它是一种简单的实现,可以逐位工作,使用
position()
函数计算输入字符串中每个字符的数值。与hex_to_decimal2()
相比,它的好处是速度更快(对于md5()
生成的十六进制字符串来说,速度快 4 倍左右)。Here is another implementation:
It is a straightforward one that works digit by digit, using the
position()
function to compute the numeric value of each character in the input string. Its benefit overhex_to_decimal2()
is that it seems to be much faster (4x or so formd5()
-generated hex strings).