在 OSX 中用 0xFF 填充文件会导致 C3BF

在 OSX 中用 0xFF 填充文件会导致 C3BF

此命令将在Linux中填充文件0xff

dd if=/dev/zero ibs=1k count=100 | tr "\000" "\377" >paddedFile.bin

当我在 OSX 中运行它时,结果不同。

$ dd if=/dev/zero ibs=1k count=100 | tr "\000" "\377" >paddedFile.bin
100+0 records in
200+0 records out
102400 bytes transferred in 0.000781 secs (131104008 bytes/sec)
$ hexdump -C paddedFile.bin
00000000  c3 bf c3 bf c3 bf c3 bf  c3 bf c3 bf c3 bf c3 bf  
|................|
*
00032000

这里发生了什么?

答案1

开门见山。

这一切都取决于你运行终端会话时设置的LANG或值LC_ALLtr。Linux 将它们设置为,C而 macOS 则将其设置为类似en_US.UTF-8。当然,这en_US可能是其他本地语言,例如en_UK(英国英语),但关键是[something].UTF-8设置而不是纯 ASCII viaC是导致这种情况的原因。

更多细节。

似乎tr在 macOS 中,当获取 时,会将 转换0xff为 UTF8 等效的 ,c3bf而不是纯 ASCII 0xff。 此处对此进行了解释此处为 Apple 社区支持主题

Linux 不像 Mac 那样在终端中处理 Unicode。如果您将“LANG”环境变量设置为“C”(Linux 上可能如此),它就会正常工作。否则,所有这些高位都将被解释为 Unicode 字符。

并且使用该LANG技巧有效!只需执行以下操作;我刚刚在 macOS 10.13.6 (High Sierra) 上进行了亲自测试。

首先,记下现有的LANG值,如下所示:

echo $LANG

我看到的输出是:

en_US.UTF-8

现在将LANG值设置为C如下形式:

LANG=C

然后再次运行该命令:

dd if=/dev/zero ibs=1k count=100 | tr "\000" "\377" >paddedFile.bin

现在这些hexdump值看起来应该是这样的:

hexdump -C paddedFile.bin
00000000  ff ff ff ff ff ff ff ff  ff ff ff ff ff ff ff ff  |................|
*
00019000

要重置该LANG值,只需关闭该终端会话或运行此命令:

LANG=en_US.UTF-8

或者 - 正如评论中指出的那样 - 您可以LANG在调用之前直接在命令行选项中设置该值,tr如下所示:

dd if=/dev/zero ibs=1k count=100 | LANG=C tr "\000" "\377" >paddedFile.bin

你甚至可以使用LC_ALL它来代替,LANG因为LANG它无论如何都是从这里派生出来的,LC_ALL就像这样:

dd if=/dev/zero ibs=1k count=100 | LC_ALL=C tr "\000" "\377" >paddedFile.bin

答案2

问题是tr,Linux 上的 GNU 实际上并没有多字节字符的概念,而是一次按字节工作。

tr手册页和在线文档都提到了字符,但这有点简单化。TODO源代码包中的文件提到了此项(摘自coreutils 8.30):

调整 wc、tr、fmt 等工具(大多数文本工具)以支持多字节。问题是我想避免重复重要的逻辑块,同时还希望在单字节模式下运行时只产生最小(最好是“无”)成本。

在 Linux 系统上 — 即使使用 UTF-8 语言环境 ( en_US.UTF-8) — GNUtr也会将 替换ä为两个“字符”( 的 UTF-8 表示形式ä有两个字节):

linux$ echo 'ä' | tr 'ä' 'x'
xx

同样,将ä和混合ö也会产生有趣的结果,因为它们的 UTF-8 表示形式共享一个共同的字节:

linux$ echo 'ö' | tr ä x
x�

或者反过来(x这里不适用):

linux$ echo ab | tr ab äx
ä

在您的情况下,GNUtr将其\377视为原始字节值。

Mac 上的则tr不同,它知道多字节字符的概念并采取相应的行动:

mac$ echo 'ä' | tr ä x
x

mac$ echo ab | tr ab äx
äx

数值为 0377(U+00ff)的字符的 UTF-8 表示形式是两个字节c3 bf,因此这就是您得到的。

tr让逐字节工作最简单的方法是让它使用 C 语言环境,而不是 UTF-8 语言环境。这又产生了有趣的行为:

$ echo 'ä' | LC_ALL=C tr 'ä' 'x'
xx

就您而言,您可以使用:

... | LC_ALL=C tr "\000" "\377"

或者你可以使用像 Perl 之类的东西来生成这些\xff字节:

perl -e 'printf "\377" x 1000 for 1..100'

相关内容