使用awk去除字节顺序标记

107

如何编写一个 awk 脚本(假设是一行代码),用于删除 BOM

要求如下:

  • 打印第一行之后的每一行 (NR > 1)
  • 对于第一行:如果以 #FE #FF#FF #FE 开头,则删除这些并打印其余部分
5个回答

127

使用 GNU sed(在 Linux 或 Cygwin 上):

# Removing BOM from all text files in current directory:
sed -i '1 s/^\xef\xbb\xbf//' *.txt

在FreeBSD操作系统上:

sed -i .bak '1 s/^\xef\xbb\xbf//' *.txt

使用GNU或FreeBSD sed 的优点是:-i 参数表示“原地”,可以在不需要重定向或奇怪技巧的情况下更新文件。

在Mac上:

另一个答案中的这个awk解决方案有效,但上面的sed命令无效。至少在Mac(Sierra)上,sed文档没有提到支持十六进制转义字符,如\xef

任何程序都可以通过将其连接到sponge工具来实现类似的技巧:

awk '…' INFILE | sponge INFILE

5
我在 Mac OS X 上精确尝试了第二个命令,结果显示“成功”,但实际上替换并未发生。 - Hakanai
1
值得注意的是,这些命令替换了一个特定的字节序列,即可能的字节顺序标记之一。也许你的文件有不同的BOM序列。(我无法提供其他帮助,因为我没有Mac) - Denilson Sá Maia
3
当我在使用含有0xef 0xbb 0xbf字符集作为BOM的文件上,在OS X操作系统中尝试执行第二个命令时,实际上并没有进行替换。 - John Wiseman
在OSX操作系统中,我只能通过perl来完成这个任务,具体可以参考这里:https://dev59.com/92ox5IYBdhLWcg3wmFWV#9101056 - Ian
在 OS X El Capitan 10.11.6 上,这个方法不起作用,但官方答案 https://dev59.com/t3NA5IYBdhLWcg3wKacx#1068700 可以正常工作。 - Heath Borders

118

试试这个:

awk 'NR==1{sub(/^\xef\xbb\xbf/,"")}{print}' INFILE > OUTFILE

在第一条记录(行)中,移除BOM字符。打印每条记录。

或者稍微简短一些,利用awk的默认操作是打印记录:

awk 'NR==1{sub(/^\xef\xbb\xbf/,"")}1' INFILE > OUTFILE

1是最短的条件,总是评估为真,因此每个记录都会被打印。

享受吧!

-- 补充 --

Unicode字节顺序标记(BOM)FAQ包括以下表格,列出了每种编码的确切BOM字节:

Bytes         |  Encoding Form
--------------------------------------
00 00 FE FF   |  UTF-32, big-endian
FF FE 00 00   |  UTF-32, little-endian
FE FF         |  UTF-16, big-endian
FF FE         |  UTF-16, little-endian
EF BB BF      |  UTF-8

因此,您可以看到如何将\xef\xbb\xbf与上表中的EF BB BFUTF-8 BOM字节对应。


1
似乎子语句中间的点太多了(至少,我的awk会抱怨)。除此之外,这正是我所寻找的,谢谢! - Boldewyn
5
然而,这种解决方案仅适用于UTF-8编码的文件。对于其他编码,如UTF-16,请参考维基百科上相应的BOM表示:http://en.wikipedia.org/wiki/Byte_order_mark - Boldewyn
2
所以:awk '{if(NR==1)sub(/^\xef\xbb\xbf/,"");print}' INFILE > OUTFILE,确保INFILE和OUTFILE不同! - Steve Clay
1
如果你使用 perl -i.orig -pe 's/^\x{FFFE}//' badfile,你可以依靠你的 PERL_UNICODE 和/或 PERLIO 环境变量来确定编码方式。PERL_UNICODE=SD 适用于 UTF-8;对于其他编码,你需要使用 PERLIO。 - tchrist
1
也许稍微简短一点的版本:awk 'NR==1{sub(/^\xef\xbb\xbf/,"")}1' - TrueY
显示剩余4条评论

42

不是awk,但更简单:

tail -c +4 UTF8 > UTF8.nobom

检查BOM:

hd -n 3 UTF8

如果存在BOM,你会看到:00000000 ef bb bf ...


6
BOM(字节顺序标记)在UTF-16中占用2个字节,在UTF-32中占用4个字节,当然,UTF-8中本来就不应该存在BOM。 - tchrist
2
@KarolyHorvath 是的,确切地说。不建议使用它。它会破坏东西。编码应该由更高级别的协议指定。 - tchrist
7
@KarolyHorvath 我的意思是它会“破坏很多程序”。这不就是我说的吗?当您在UTF-16或UTF-32编码中打开流时,解码器知道不要计算BOM。当您使用UTF-8时,解码器将BOM呈现为数据。这是无数程序中的语法错误。即使Java的解码器也是按设计方式运行的! UTF-8文件上的BOM位置不正确,非常麻烦:它们是一个错误!它们会破坏很多东西。即使只是cat file1.utf8 file2.utf8 file3.utf3 > allfiles.utf8也会出问题。永远不要在UTF-8上使用BOM。句号。 - tchrist
6
在OS X(截至10.8.2)上无法使用“hd”,因此,要检查UTF-8 BOM,您可以使用以下命令:“head -c 3 file | od -t x1”。 - mklement0
1
如果需要,也可以使用以下代码:if [[ "file a.txt | grep -o 'with BOM'" == "BOM" ]]; - Benoit Duffez
显示剩余4条评论

21
除了将CRLF换行符转换为LF,dos2unix还会删除BOM:
dos2unix *.txt

dos2unix 还会将带BOM的UTF-16文件(但没有BOM的UTF-16文件除外)转换为无BOM的UTF-8:

$ printf '\ufeffä\n'|iconv -f utf-8 -t utf-16be>bom-utf16be
$ printf '\ufeffä\n'|iconv -f utf-8 -t utf-16le>bom-utf16le
$ printf '\ufeffä\n'>bom-utf8
$ printf 'ä\n'|iconv -f utf-8 -t utf-16be>utf16be
$ printf 'ä\n'|iconv -f utf-8 -t utf-16le>utf16le
$ printf 'ä\n'>utf8
$ for f in *;do printf '%11s %s\n' $f $(xxd -p $f);done
bom-utf16be feff00e4000a
bom-utf16le fffee4000a00
   bom-utf8 efbbbfc3a40a
    utf16be 00e4000a
    utf16le e4000a00
       utf8 c3a40a
$ dos2unix -q *
$ for f in *;do printf '%11s %s\n' $f $(xxd -p $f);done
bom-utf16be c3a40a
bom-utf16le c3a40a
   bom-utf8 c3a40a
    utf16be 00e4000a
    utf16le e4000a00
       utf8 c3a40a

3
我知道这个问题是针对Unix/Linux的,但我认为提到一个适用于Unix挑战者(在Windows上,带有UI)的好选择还是值得一提的。
我在WordPress项目中遇到了同样的问题(BOM导致RSS订阅和页面验证出现问题),不得不查找所有文件夹中的所有文件,以找到其中一个带有BOM的文件。我发现了一个名为Replace Pioneer的应用程序,并在其中进行了以下操作:

批处理运行器 -> 搜索(以查找子文件夹中的所有文件) -> 替换模板 -> 二进制删除BOM(有一个现成的搜索和替换模板可供使用)。

这不是最优雅的解决方案,需要安装一个程序,这是一个缺点。但是一旦我知道周围发生了什么,它就像魔术般地工作了(并找到了大约2300个文件中的3个带有BOM的文件)。


1
当我发现你的解决方案时,我感到非常高兴,但是我没有在公司电脑上安装软件的特权。今天花了很多时间,直到我找到了替代方法:使用带有PythonScript插件的Notepad ++。http://superuser.com/questions/418515/how-to-find-all-files-in-directory-that-contain-utf-8-bom-byte-order-mark/914116#914116 无论如何,还是谢谢! - Hoàng Long

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接