我有两个相关的"为什么">--而不是"如何">--问题:
问题1
而printf
和od
为ASCII字符产生相同的十进制、八进制和十六进制表示——
ascii_char=A
printf "%d" "'$ascii_char"
65
echo -n $ascii_char | od -A n -t d1
65
echo -n $ascii_char | od -A n -t u1
65
printf "%o" "'$ascii_char"
101
echo -n $ascii_char | od -A n -t o1
101
printf "%x" "'$ascii_char"
41
echo -n $ascii_char | od -A n -t x1
41
--为什么它们在某种程度上不会为Unicode字符生成相同的表示?
unicode_char=
printf "%d" "'$unicode_char"
128021
echo -n $unicode_char | od -A n -t d1
-16 -97 -112 -107
echo -n $unicode_char | od -A n -t d
-1785683984
echo -n $unicode_char | od -A n -t u1
240 159 144 149
echo -n $unicode_char | od -A n -t u
2509283312
printf "%o" "'$unicode_char"
372025
echo -n $unicode_char | od -A n -t o1
360 237 220 225
echo -n $unicode_char | od -A n -t o
22544117760
printf "%x" "'$unicode_char"
1f415
echo -n $unicode_char | od -A n -t x1
f0 9f 90 95
echo -n $unicode_char | od -A n -t x
95909ff0
问题2
虽然Unicode字符的od
结果与printf
不同,但为什么printf
仍然知道如何将od
结果转换回字符,而printf
无法转换回自己的结果?
printf "%o" "'$unicode_char"
372025 # printf cannot convert back its own result
echo -n $unicode_char | od -A n -t o1
360 237 220 225 # looks different, but printf can convert it back correctly
printf %b '360237220225'
# success
printf "%x" "'$unicode_char"
1f415 # printf can convert back this result
printf "U$(printf %08x 0x1f415)"
# success
echo -n $unicode_char | od -A n -t x1
f0 9f 90 95 # looks different, but printf can convert it back correctly
printf %b 'xf0x9fx90x95'
# success
正如评论中所指出的,您看到的差异是Unicode代码点与其UTF-8编码之间的差异。
printf
打印代码点,请参阅POSIX文档了解printf ... "' "
:
如果前导字符是单引号或双引号,则该值应为单引号或两引号后面的字符的基础代码集中的数值。
无论您选择UTF-8、UTF-16、UTF-32。。。od
则不知道字符代码集。od
只打印字节/字(=-t
字节的组),即使编码恰好与其代码点相同,这些字节/字也始终被编码(例如,ASCII编码中的ASCII字符或UTF-8编码中的ASCII字符)。
具有代码点12802。UTF-8试图用单字节对代码点进行编码(因此UTF-8,因为1字节=8位),但12802>28=256不适合单个字节,因此数字被拆分为多个字节,这些字节被标记为特殊字节以防止混淆。每个字节上的这些特殊标记导致od
的不同输出。
如果您转换为UTF-32,每个代码点都将适合一个单词,允许您使用od
来显示代码点:
# Assuming little endian system. For big endian systems use UTF-32BE.
echo -n | iconv -t UTF-32LE | od -An -tu4
128021
问题2
使用printf %b '360237220225'
,您可以(手动)从od
反转oct转储,以便将的原始UTF-8编码打印到终端。这里,printf
根本不关心字符集或编码;终端是解释编码的终端。
printf %o '
=372025
不可能那么容易逆转,因为。。。
- 八进制数字与字节的对齐不如十六进制。对于单个字节(28=256),两个八进制数字不足(8²=64),三个八进制位数过多(8³=512)。因此,如果将4个字节打印为单个八进制数(
printf %o
),则某些数字包含来自两个字节的信息。因此,不能简单地通过对现有数字进行分组来将八进制数拆分为4个八进制数(每个字节一个)。相反,你必须转换成256进制,然后将每个256进制的数字再次转换成8进制——就像你有一个大的十进制数字一样。od
就是这么做的;)
你可以说,这部分是"<为什么printf
可以读取十进制数字,而不能读取5进制数字>"> - 生成的字节仍然必须用UTF-8编码,这样您的终端才能识别它们。对于
printf %b '360237220225'
,这一部分是不必要的,因为UTF-8编码从一开始就没有被解码到其代码点
然而,您可以将的代码点的八进制表示转换回:
printf "\U$(printf %08x 0372025)" # leading 0 = octal number