Character-Encoding

tr 不替換撇號

  • May 10, 2017

我想將此文件中的所有撇號轉換為X

Bob's book
Bob’s book
Bob′s book  # (Might look the same but actually different)

第一個撇號按預期替換:

$ cat file | tr "'" "X"
BobXs book
Bob’s book
Bob′s book

但是另外兩種撇號,奇怪的事情發生了:

$ cat file | tr "’" "X"
Bob's book
BobXXXs book
BobXX�s book

$ cat file | tr "′" "X"
Bob's book
BobXX�s book
BobXXXs book

如何讓它發揮作用?

tr以字節為單位工作,這意味著它不適用於 UTF-8 等多字節編碼。我知道的唯一解決方案是找到一個tr支持 Unicode 的版本,或者切換到sed或其他一些可以進行字元串替換的工具。

對我來說,只要您的作業系統配置為使用 utf-8 程式碼頁, tr 就適用於 ascii 和 utf-8 文件。

這是我的範例 #1(Solaris 11):

$ locale
LANG=en_US.UTF-8
LC_CTYPE="en_US.UTF-8"
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_ALL=

如您所見,作業系統配置為使用 utf-8。我在 utf-8 程式碼頁中創建了這兩個文件:

$ cat file
Bob’s Bob′s Bob's

$ cat apos
’′'

然後我得到了預期的結果,像這樣替換所有 apos:

$ cat file | tr "$(cat apos)" "xxx"
Bobxs Bobxs Bobxs

這是我的範例 #2(Solaris 10):

$ locale
LANG=
LC_CTYPE="C"
LC_NUMERIC="C"
LC_TIME="C"
LC_COLLATE="C"
LC_MONETARY="C"
LC_MESSAGES="C"
LC_ALL=

在這裡,您可以看到此作業系統配置為處理簡單的 ASCII,而不是 utf-8,因此您可能會遇到使用 tr 處理具有多字節字元的 utf-8 文件的問題。但是有解決方法。只要 tr 命令允許輸入字元的八進製表示,那麼您可以使用八進製表示替換指定字元的所有字節。

在您的情況下,您有:

char  hex        octal
’     E2 80 99   \342\200\231
′     E2 80 B2   \342\200\262
'     27         \47

第一個和第二個 apos 由三個字節表示。第三個是標準ascii(一個字節)。

因此,如果您想替換第一個 apos,您可以使用:

$ cat file | tr "\342\200\231" "\0\0x"
Bobxs Bob▒s Bob's

第二:

$ cat file | tr "\342\200\262" "\0\0x"
Bob▒s Bobxs Bob's

第三:

$ cat file | tr "\47" "x"
Bob’s Bob′s Bobxs

要一次性替換所有內容,您可以使用:

$ cat file | tr "\342\200\231\262\47" "\0\0xxx"
Bobxs Bobxs Bobxs

當然它並不完美,只要它會替換文件中所有出現的字節\342、\200、\231、\262,因此包含這些字節的其他多字節字元將被破壞。但是,如果您的文件不包含任何其他多字節字元,它將起作用。

引用自:https://unix.stackexchange.com/questions/48567