BASH/shell中的Unicode字符表示:printf vs od



我有两个相关的"为什么">--而不是"如何">--问题:

问题1

printfod为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不可能那么容易逆转,因为。。。

  1. 八进制数字与字节的对齐不如十六进制。对于单个字节(28=256),两个八进制数字不足(8²=64),三个八进制位数过多(8³=512)。因此,如果将4个字节打印为单个八进制数(printf %o),则某些数字包含来自两个字节的信息。因此,不能简单地通过对现有数字进行分组来将八进制数拆分为4个八进制数(每个字节一个)。相反,你必须转换成256进制,然后将每个256进制的数字再次转换成8进制——就像你有一个大的十进制数字一样。od就是这么做的;)
    你可以说,这部分是"<为什么printf可以读取十进制数字,而不能读取5进制数字>">
  2. 生成的字节仍然必须用UTF-8编码,这样您的终端才能识别它们。对于printf %b '360237220225',这一部分是不必要的,因为UTF-8编码从一开始就没有被解码到其代码点

然而,您可以将的代码点的八进制表示转换回:

printf "\U$(printf %08x 0372025)"  # leading 0 = octal number

相关内容

  • 没有找到相关文章

最新更新