tr 不替换撇号

tr 不替换撇号

我想将此文件中的所有撇号转换为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

如何让它发挥作用?

答案1

tr以字节为单位工作,这意味着它不能正常工作于 UTF-8 等多字节编码。我所知道的唯一解决方案是找到tr支持 Unicode 的版本,或者切换到sed其他可以进行字符串替换的工具。

答案2

对我来说,只要您的操作系统配置为使用 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 文件的问题。但有解决方法。由于long tr命令允许输入字符的八进制表示,那么您可以使用八进制表示替换指定字符的所有字节。

在你的情况下,你有:

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

第一和第二个 apos 由三个字节表示。第三个是标准 ascii(一字节)。

因此,如果你想替换第一个 pos,你可以使用:

$ 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,因此包含这些字节的其他多字节字符将被破坏。但如果您的文件不包含任何其他多字节字符,它将起作用。

相关内容