如何从 UTF-8 文件中删除 BOM?

m13*_*13r 113 command-line unicode files

我有一个带有 BOM 的 UTF-8 编码文件,想删除 BOM。是否有任何 linux 命令行工具可以从文件中删除 BOM?

$ file test.xml
test.xml:  XML 1.0 document, UTF-8 Unicode (with BOM) text, with very long lines
Run Code Online (Sandbox Code Playgroud)

CSM*_*CSM 120

如果您不确定文件是否包含 UTF-8 BOM,那么这(假设 GNU 实现sed)将删除 BOM(如果它存在),或者如果它不存在则不进行任何更改。

sed '1s/^\xEF\xBB\xBF//' < orig.txt > new.txt
Run Code Online (Sandbox Code Playgroud)

您还可以使用以下-i选项覆盖现有文件:

sed -i '1s/^\xEF\xBB\xBF//' orig.txt
Run Code Online (Sandbox Code Playgroud)

如果您使用的是 BSD 版本sed(例如 macOS),那么您需要让 bash 进行转义:

 sed $'1s/\xef\xbb\xbf//' < orig.txt > new.txt
Run Code Online (Sandbox Code Playgroud)

  • 这在 utf8 语言环境中可能不起作用,但在 c 或 posix 之前添加语言环境覆盖将始终有效。 (4认同)
  • 要修复它以与启用 unicode 的 sed 一起使用,请执行 LC_ALL=C sed '1s/^\xEF\xBB\xBF//' (4认同)
  • @hildred 我已经用 `en_US.UTF-8` 语言环境对其进行了测试并且它有效。什么时候会失败? (3认同)
  • @m13r,这取决于 sed 和编译选项的版本。在失败的情况下,带有 Unicode 字符类的非常新版本的 sed 会将三个字节序列作为与三个字符序列不匹配的单个字符引入。但是,在这种情况下,您可以进行 16 位字符匹配。然而,这是一个新功能,并不普遍存在。如果你想测试,我建议编译最新版本。 (2认同)
  • @mazunki,`1s/` 表示只搜索第一行;其他线路不受影响。`^` 表示仅匹配(第一)行的开头。`\xEF\xBB\xBF` 是 UTF-8 BOM(转义的十六进制字符串)。`//` 表示什么都不替换。我可以在末尾添加 `1`(对于 `1s/^xEF\xBB\xBF//1`),这意味着只匹配该行上第一次出现的模式。但是由于搜索是用`^` 锚定的,所以这不会有任何区别。如果文件在第一行的开头没有 BOM,则模式将不匹配,因此不会进行任何更改。 (2认同)

Sté*_*las 102

BOM 在 UTF-8 中没有意义。这些通常是由 Microsoft 操作系统上的虚假软件错误添加的。

dos2unix 将删除它并处理 Windows 文本文件的其他特性。

dos2unix test.xml
Run Code Online (Sandbox Code Playgroud)

  • 我同意 UTF-8 编码的 BOM 没有意义,但不管你信不信,有很多人认为这是一个有助于区分 UTF-8 与其他 8 位编码的好主意。所以这是一个品味问题。Windows 记事本故意添加了 BOM。 (19认同)
  • 当上下文只是关于如何删除它的问题时,它是否有意义有什么关系?根据维基百科,记事本要求 BOM 将文件识别为 UTF-8,Google Docs 在将文件导出为文本时也会添加它。我怀疑他们都是_错误_做的。 (18认同)
  • @JohanMyréen 是的,但称它们为 UTF-8 是不正确的。它们不是 UTF-8 文件。它们是 UTF-8-with-BOM 文件,这是另一种文件格式。我想那些 Windows 怪胎不会很高兴获得称为 MSOffice 文件的 ODT 文件:) (4认同)
  • 回复:_`“BOM 在 UTF-8 中没有意义”`_,至少在波斯语中是有意义的,因为如果没有 BOM,它们就不会显示为波斯语,因为我总是在开头添加 BOM 字节*nix 环境中的波斯语上下文文件,以便能够在 Windows 环境(如 Excel 或记事本等)中正确显示其波斯语内容。 (4认同)
  • 有没有办法不转换行尾而只用`dos2unix`删除BOM? (2认同)
  • @m13r 然后使用 [sed script in this answer](https://unix.stackexchange.com/a/381263/232326)。这将仅删除 bom(如果存在),其他任何内容都不会更改。 (2认同)
  • @JohanMyréen 有人认为用蓝色蜡笔画红线是个好主意,但这并不能改变用蓝色蜡笔画的线是蓝色的事实,即使你称它为红色。 (2认同)

Jos*_*ter 61

使用 VIM

  1. 在 VIM 中打开文件:

     vi text.xml
    
    Run Code Online (Sandbox Code Playgroud)
  2. 删除 BOM 编码:

     :set nobomb
    
    Run Code Online (Sandbox Code Playgroud)
  3. 保存并退出:

     :wq
    
    Run Code Online (Sandbox Code Playgroud)

对于非交互式解决方案,请尝试以下命令行:

 vi text.xml
Run Code Online (Sandbox Code Playgroud)

这应该删除 BOM,保存文件并退出,所有这些都来自命令行。


m13*_*13r 33

可以使用以下tail命令从文件中删除 BOM :

tail -c +4 withBOM.txt > withoutBOM.txt
Run Code Online (Sandbox Code Playgroud)

  • @deviantfan 这就是为什么如果你想跳过它,你需要从第 4 个字节开始。 (12认同)
  • `tail` 使用基于 1 的索引?!卧槽! (12认同)
  • @CodesInChaos, `tail -c -1` 或 `tail -c 1`(通常用于 `tail` 的内容)是从最后一个字节开始的内容,`tail -c +1` 从第一个字节开始。`tail -c 0`/`tail -c +0` 会更不直观。 (6认同)
  • 为什么是4?BOM 有 3 个字节。 (2认同)
  • @deviantfan:`(dd bs=1 count=3 of=/dev/null; cat) &lt;输入&gt;输出`。或者使用 GNU `(head -c3 &gt;/dev/null; cat)` -- 即使在 UTF8 或其他非单字节语言环境中;GNU head 执行'char'=byte。 (2认同)

Nom*_*mal 7

您可以使用

LANG=C LC_ALL=C sed -e 's/\r$// ; 1 s/^\xef\xbb\xbf//' -i -- filename
Run Code Online (Sandbox Code Playgroud)

从文件开头删除字节顺序标记(如果有),并将任何 CR LF 换行符仅转换为 LF。该LANG=C LC_ALL=C告诉你希望命令在默认的C语言环境(也被称为默认POSIX环境),其中,形成字节顺序标记的三个字节被视为字节运行shell。该-i选项就地sed的手段。如果使用-i.old,则 sed 会将原始文件另存为filename.old,并将新文件(带有修改,如果有)另存为filename.


我个人喜欢把它作为~/bin/fix-ms; 例如,作为

#!/bin/dash
export LANG=C LC_ALL=C
if [ $# -gt 0 ]; then
    for FILE in "$@" ; do
        sed -e 's/\r$// ; 1 s/^\xef\xbb\xbf//' -i -- "$FILE" || exit 1
    done
else
    exec sed -e 's/\r$// ; 1 s/^\xef\xbb\xbf//'
fi
Run Code Online (Sandbox Code Playgroud)

因此,如果我需要将其应用于所有 C 源文件和头文件(例如,我在 MS-DOS 时代的旧代码!),我只需运行

find . -name '*.[CHch]' -print0 | xargs -r0 ~/bin/ms-fix
Run Code Online (Sandbox Code Playgroud)

或者,如果我只是想看这样一个文件,不修改它,我可以运行

~/bin/ms-fix < filename | less
Run Code Online (Sandbox Code Playgroud)

<U+FEFF>在我的 UTF-8 终端中看不到丑陋的一面。


小智 7

为此,我经常使用 vim one-liner:

vim --clean -c 'se nobomb|wq' filename

vim --clean -c 'bufdo se nobomb|wqa' filename1 filename2 ...
Run Code Online (Sandbox Code Playgroud)