使用“mawk”将十六进制转换为十进制超出范围

使用“mawk”将十六进制转换为十进制超出范围

当十六进制数字相对较小时,我可以使用

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=16bc,指示实用程序读取十六进制数字。 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 万行十六进制。

相关内容