You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

BASH中printf与od的Unicode字符表示差异及反向转换疑问

关于printf与od处理Unicode字符的两个原理性疑问

问题1:为何printf与od对Unicode字符的数值表示不一致?

对于ASCII字符,printf和od的十进制、八进制、十六进制输出完全一致,示例如下:

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

核心原因

两者处理的是完全不同的对象:

  • printf "%d" "'$unicode_char"读取的是Unicode码点——也就是该字符在Unicode标准中对应的唯一整数ID(比如🐕的码点是U+1F415,十进制就是128021)。
  • od是直接读取字符在UTF-8编码下的字节序列,然后对字节做数值转换:
    • 带1的参数(比如-t d1/-t o1)是逐个输出每个字节的数值;
    • 不带1的参数(比如-t d/-t o)是把多个字节按机器的字节序(通常是小端)合并成一个整数输出。
      ASCII字符的码点和UTF-8编码是单字节一一对应,所以两者输出一致;但Unicode字符(尤其是超出基本多语言平面的字符)在UTF-8中是多字节编码,自然和码点数值完全不同。

问题2:为何printf能反向转换od的结果,却无法转换自身输出的部分结果?

示例如下:

printf "%o" "'$unicode_char"
# 输出:372025(printf无法直接转换这个结果)
echo -n $unicode_char | od -A n -t o1
# 输出: 360 237 220 225(结果不同,但printf可正确反向转换)
printf %b '\360\237\220\225'
# 输出:🐕(转换成功)

printf "%x" "'$unicode_char"
# 输出:1f415(printf可转换自身输出的该结果)
printf "\U$(printf %08x 0x1f415)"
# 输出:🐕(转换成功)
echo -n $unicode_char | od -A n -t x1
# 输出: f0 9f 90 95(结果不同,但printf可正确反向转换)
printf %b '\xf0\x9f\x90\x95'
# 输出:🐕(转换成功)

核心原因

这是printf的转义规则决定的:

  1. 处理od的结果:od输出的是UTF-8字节的八进制/十六进制值,printf %b '\360\237\220\225'是把每个3位八进制转义(或两位十六进制转义)解析成单个字节,这些字节拼接起来就是🐕的UTF-8编码,终端会自动把这个字节序列解析为对应的Unicode字符。
  2. 处理自身输出的码点:
    • 十六进制码点:printf支持\UXXXXXXXX语法(需要8位十六进制补零),所以\U0001F415能直接识别为Unicode码点并输出对应字符;
    • 八进制码点:printf没有直接识别多字节八进制码点的语法——它的八进制转义最多只认3位(对应单字节),所以372025这个5位的八进制码点值,无法被printf直接解析为对应字符。

内容的提问来源于stack exchange,提问作者KiriSakow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 05:55:24