我正在 zsh 中读取二进制文件的前四个字节。前 3 个字节是0x00
,第 4 个字节是0x01
。我通过将文件作为字符串读入,然后对其进行拆分和切片,创建了这些字节的数组,结果如下:
> echo -n "${(j::)bytes}" | xxd
00000000: 0000 0001 ....
现在我尝试从最右边(第 4 个字节)开始使用适当的幂将它们相加以得到总和。问题是,当我尝试在算术评估或整数变量中使用第 4 个字节时:
integer -i 10 b=${bytes[-1]} # This is the offending line
sum=$((sum + b * (256 ** i)))
我收到以下错误:
bad math expression: illegal character: ^A
也就是0x01
。
我如何告诉 zsh 将字节解释为数字?我注意到(#)
扩展标志,它强制将表达式解释为数字,然后解释为字符,但我遇到了相反的问题。它似乎被解释0x01
为一个字符。
更新:
我怀疑问题在于数组的类型是字符(如果 zsh 集合有类型?)因为我使用(s::)
整个文件的字符串创建了它,但我不确定如何将数组元素转换回整数。我试过
b=$(printf '%d' "$bytes[4]")
但这给了我一个类似的错误。我似乎可以通过以下任何一种方法重现:
printf '%d' $'^A'
integer b=$'^A'
其中由两个文字字符和^A
组成。^
A
答案1
内置%d
运算符printf
会将字符转换为其数值,但它需要一个前导引号来区分输入参数和算术表达式:
>> one=$'\1'
>> typeset -p one
typeset one=$'\C-A'
>> printf '%d\n' "'$one" # leading single quote
1
>> printf '%d\n' "' " # quote and space
32
>> printf '%d\n' '"A' # leading double-quote
65
>> # your input values:
>> bytes=($'\0' $'\C-@' ${(#):-0} $'\C-A')
>> typeset -p bytes
typeset -a bytes=( $'\C-@' $'\C-@' $'\C-@' $'\C-A' )
>> integer b=$(printf '%d' "'$bytes[4]")
>> print "value in b: $b"
value in b: 1
请注意,中的值b
是一个字符串,即使它被声明为integer
。整数类型集只会改变变量在赋值和扩展中的处理方式 - 它不会改变内部表示。
要转换整个字节数组,请使用-v
(分配变量) 选项printf
:
>> setopt nomultibyte
>> typeset -a bin=($'\C-@' $'\C-C' ' ' 'A' '~' ${(#):-240})
>> typeset -a nums
>> printf -v nums %d \'${^bin}
>> typeset -p nums
typeset -a nums=( 0 3 32 65 126 240 )
部分作品:
typeset -a nums
- 输出数组。这被声明为一个数组,以便printf
将每个结果分配给单独的数组元素。从 文档:-v 选项使输出存储为参数的值姓名,而不是打印的。如果姓名是一个数组,并且在使用参数时重复使用格式字符串,那么每次使用格式字符串时都会使用一个数组元素。
\'${^bin}
- 这将在数组中的每个元素前加上单引号,使用${^...}
rc_expand_param选项。setopt nomultibyte
- 这样,shell 操作就会将每个字节视为单个字符。处理单字节数组时,这并非绝对必要,但它可以确保显示和赋值时的一致性。