使用 POSIX 工具正確處理帶有 BOM 的 Unicode 文件
今天嘗試使用
grep
時,我遇到了 Unicode 文件(在本例中為 UTF-8 )中的字節順序標記 (BOM)的熟悉問題。具體來說,我試圖找到一個以 pattern開頭的文件,但當然將 BOM 視為三個單獨的字元,如果第一行以 . 開頭,則與文件的第一行不匹配。我什至嘗試更新正則表達式以忽略空格(),但無濟於事。XYZ``grep '^XYZ'``grep``XYZ``'^[[:space:]]*XYZ'
其他問題涉及轉換文件或專門針對 BOM,但我想知道 POSIX 工具是否具有正確處理 Unicode 文件的通用選項。如果
grep
正確處理 Unicode 文件,它將認為文件內容在 BOM 之後開始並XYZ
在第一行匹配,就像任何其他行一樣。
Unicode 聯盟有一個常見問題解答,其中包括我應該如何處理 BOM。這部分包括:
如果已知文本數據流是純 Unicode 文本(但不是哪個字節序),則 BOM 可用作簽名。如果沒有 BOM,則應將文本解釋為大端。
和
如果數據流的精確類型已知(例如 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) …
大多數 POSIX 工具對字節而不是字元進行操作。Unicode 信號對他們來說毫無意義,因此它會像任何其他數據一樣被對待。