当十六进制数字相对较小时,我可以使用
echo 0xFF| mawk '{ printf "%d\n", $1}'
将十六进制转换为十进制。
当十六进制数很大时,mawk
不再起作用,例如
echo 0x8110D248 | mawk '{ printf "%d\n", $1 }'
输出2147483647
(这是错误的,2147483647
相当于0x7FFFFFFF
)。
如何转换更大的数字?
我有很多数字(每行一个数字,超过10M)需要处理,例如:每个0xFF\n 0x1A\n 0x25\n
.如何让它在这种场合发挥作用?经过xargs
?有没有更好的方法呢?xargs
真的很慢。
答案1
用于任意大数字的更好命令是bc
。这是执行转换的函数
hextodec() {
local hex="${1#0x}"
printf "ibase=16; %s\n" "${hex^^}" | bc
}
hextodec 0x8110D248
2165363272
我在这里使用了一些看起来很奇怪的功能,它们在使用变量时操纵它们的值:
"${1#0x}"
-"$1"
正如您所期望的,这引用了函数的第一个参数。是#
一个修饰符(man bash
例如,请参阅 ,或阅读POSIX) 从值的前面删除以下表达式。例如,0xab12
将返回为ab12
"${hex^^}"
- 此引用"$hex"
但返回其值,其中字母字符映射为大写。 (这是一个bash
扩展,因此请读取man bash
但不是 POSIX。)例如,12ab34
将返回为12AB34
在这两种情况下,{ … }
大括号将修饰符绑定到变量;"$hex^^"
只会返回$hex
变量的值,后跟两个向上箭头/插入符字符
答案2
您的问题来自于mawk
内部使用带符号的 32 位整数,因此不能表示大于 2 31 -1 (即 2147483647)的整数。
要流式传输具有任意大小的十六进制数字的文件,请将每个文件转换为十进制:
{
echo 'ibase=16'
sed -e 'y/xabcdef/XABCDEF/' -e 's/^0X//'
} | bc
首先发送ibase=16
到bc
,指示实用程序读取十六进制数字。 thensed
用于将十六进制字母转换为大写并删除0x
文件中数字的任何前缀。我们这样做是因为需要没有前缀且带有大写字母的bc
十六进制数字。0x
file
给定一个包含内容的文件
0xFF
0x1A
0x25
0x7FFFFFFF
0x8110D248
0x8110D2487FFFFFFF
...管道将输出
255
26
37
2147483647
2165363272
9300164439347036159
您还可以将管道重写为通用流 shell 函数:
hex2dec () {
{ echo 'ibase=16'; sed -e 'y/xabcdef/XABCDEF/' -e 's/^0X//'; } | bc
}
hex2dec <file
# Or...
some-process-producing-data |
some-filter-extracting-hexadecimal-numbers |
hex2dec |
while IFS= read -r number
# use the decimal number in "$number" here.
done
答案3
我们可以使用 perl 的 hex() 内置函数,按照 @Stephane Chazelas 的建议,使用 bignum 模块执行十六进制 -> 十进制转换:
$ perl -Mbignum -lpe '$_=hex' file
如果所有十六进制数字都以 0x 或 0X 为前缀,那么我们甚至可以使用内置的 oct:
$ perl -Mbignum -lpe '$_=oct' file
我们可以使用 GNU dc 桌面计算器,它可以从文件中读取数据。但在调用 dc 之前,我们根据 dc 的需要转向大写的十六进制表示法,并从十六进制字符串中去掉前导 0x。然后我们将输入基数设置为 16 (16i) 并仅打印数字(默认为基数 10)
< file \
tr a-fx A-FX |
cut -d "X" -f 2 |
dc -e "16i [q]sq
[?z0=qpcz0=?]s?
l?x
"
使用 int() 内置函数并将 16 作为第二个参数,会将十六进制数字字符串作为第一个参数转换为等效的十进制。
python3 -c 'import sys
with open(sys.argv[1]) as f:
for l in f:
print(int(l.strip(),16))
' file
答案4
关于声明
mawk
...因此不代表大于 2^31-1 的整数(即 2147483647。)
在@他们的回答: 这个说法是错误的。您绝对可以读入并打印出高达双精度无符号整数限制的十六进制,即 2 53 -1 :
echo '0x1234BEEF9CAFE7' \
\
| mawk '{ CONVFMT=OFMT="%.20g";
print a=+( substr($0,1,length($0)-8)),\
b=+(("0x")(substr($0,1+length($0)-8))),\
c=2^32*a+b,\
sprintf("0x%X%.8X",a,b) }'
1193150 4020023271 5124544249245671 0x1234BEEF9CAFE7
或者更安全的方法 - 将其拆分为 16 7在高侧,16 6在低侧:
echo '0x1234BEEF9CAFE7' | mawk '{ CONVFMT=OFMT="%.20g"; OFS="\f"; n=length(x=$0)
print a=+( substr(x,1,n-6)),
b=+(("0x")(substr(x,1+n-6))),
c=2^24*a+b,
sprintf("0x%X%.6X",a,b) }'
305446639
10268647
5124544249245671
0x1234BEEF9CAFE7
这是更通用的版本,您可以简单地调整ndigits
参数以获取低端所需的十六进制数字数量:
echo '0x1234BEEF9CAFE7' \
\
| mawk '{ CONVFMT=OFMT="%.20g";
OFS="\f";
w=length(x=$0); ndigits=7; # enter 6, 7, or 8
print a=+( substr(x,1,w-ndigits)),
b=+(("0x")(substr(x,1+w-ndigits))),
c=(16^ndigits)*a+b,
sprintf("0x%X%.*X",a,ndigits,b) }'
19090414
261926887
5124544249245671
0x1234BEEF9CAFE7
10M 行应该很快。我创建了一个包含 5578 万行十六进制的随机测试文件,每行有 13 个十六进制数字:
% echo; ( time ( pvE0 < test_hex_list_01.txt| mawk 'BEGIN { CONVFMT=OFMT="%.20g"; const16=16^(ndigits=7) } { printf("%.f\n",const16*(a=+substr(x=$0,1,(w=length(x))-ndigits))+(b=+("0x"substr(x,1+w-ndigits))),a,ndigits,b) }' )) | pvE9 > test_hex_list_01_output_mawk_short.txt ; echo
out9: 2.22MiB 0:00:00 [22.1MiB/s] [22.1MiB/s] [<=> ]
in0: 851MiB 0:00:39 [21.5MiB/s] [21.5MiB/s] [=================>] 100%
out9: 887MiB 0:00:39 [22.6MiB/s] [22.4MiB/s] [ <=> ]
( pvE 0.1 in0 < test_hex_list_01.txt | mawk ; ) 39.20s user 0.94s system 101% cpu 39.64 out9: 887MiB 0:00:39 [22.4MiB/s] [22.4MiB/s] [ <=> ]
% awkwc5 < test_hex_list_01.txt
rows = 55,779,444. | UTF8 chars = 892,471,099. | bytes = 892,471,099.
仅仅用了 39.2 秒就完成了近 5600 万行十六进制。