尝试使用grep
时,我遇到了熟悉的问题的字节顺序标记 (BOM)在 Unicode 文件中(在本例中为 UTF-8)。具体来说,我试图找到一个以XYZ
模式开头的文件grep '^XYZ'
,但当然grep
将 BOM 视为三个单独的字符,并且如果第一行以 开头,则与文件的第一行不匹配XYZ
。我什至尝试更新正则表达式以忽略空格('^[[:space:]]*XYZ'
),但无济于事。
其他问题已处理转换文件或者专门针对 BOM,但我想知道 POSIX 工具是否有一个通用选项来正确处理 Unicode 文件。如果grep
正确处理 Unicode 文件,它会认为文件内容在 BOM 之后开始,XYZ
并像任何其他行一样在第一行匹配。
答案1
Unicode 联盟有一个常见问题解答,其中包括我该如何处理 BOM。这部分内容包括:
如果已知文本数据流是纯 Unicode 文本(但不知道哪种字节序),则可以使用 BOM 作为签名。如果没有 BOM,则文本应解释为 big-endian。
和
如果数据流的精确类型已知(例如 Unicode big-endian 或 Unicode Little-endian),则不应使用 BOM。特别是,每当数据流声明为 UTF-16BE、UTF-16LE、UTF-32BE 或 UTF-32LE 时,都不得使用 BOM。
请注意,UTF-8 是总是已知的字节序,因为它没有字节序。因此,只要您知道文本是 UTF-8,“就不应该使用 BOM”。
即使不必要地使用 BOM,也会cat
返回不正确的结果,因为除第一个文件外,所有文件的 BOM 都将被视为零宽度不间断空格。但是,UNIX 的强大之处在于过滤器。
对于单个文件或流上的操作,sed "1s/^$(printf '\357\273\277')//"
管道中将剥离 BOM(如果存在),而使所有其他流保持不变。
对于多个文件的操作,带有进程替换的 shell(如 Bash,但遗憾的是不是 POSIX shell)很有用:
sb() { sed "1s/$(printf '\357\273\277')//" "$@" ; }
cat <(sb file1) <(sb file2) …
答案2
大多数 POSIX 工具对字节进行操作,而不是对字符进行操作。 Unicode 信号对他们来说毫无意义,因此它将像任何其他数据一样对待。
答案3
从另一个答案,听起来我正在处理 BOM 签名不正确的文件。
所以,答案是POSIX 工具已经正确处理 Unicode (UTF-8) 文件。
如果你的 Unicode 不好,他们当然不能正确处理它,但你可以使用其他问题中的 BOM 定位处理多余的 BOM 签名。