如何让 tr 识别非 ascii(unicode) 字符?

如何让 tr 识别非 ascii(unicode) 字符?

我正在尝试从文件(UTF-8)中删除一些字符。我用于tr此目的:

tr -cs '[[:alpha:][:space:]]' ' ' <testdata.dat 

文件包含一些外来字符(例如“Латвийская”或“àé”)。tr似乎不理解它们:它将它们视为非 alpha 并也将其删除。

我尝试更改一些区域设置:

LC_CTYPE=C LC_COLLATE=C tr -cs '[[:alpha:][:space:]]' ' ' <testdata.dat
LC_CTYPE=ru_RU.UTF-8 LC_COLLATE=C tr -cs '[[:alpha:][:space:]]' ' ' <testdata.dat
LC_CTYPE=ru_RU.UTF-8 LC_COLLATE=ru_RU.UTF-8 tr -cs '[[:alpha:][:space:]]' ' ' <testdata.dat

不幸的是,这些都不起作用。

我怎样才能tr理解Unicode?

答案1

这是一个已知的(1,2,3,4,5,6) GNU 实现的限制tr

并不是说不支持外国的、非英语或非 ASCII 字符,但它不支持多字节字符。

如果以 iso8859-5(每个字符一个字节)字符集(并且您的语言环境使用该字符集)编写,这些西里尔字符将被正确处理,但您的问题是您使用的是非 ASCII 的 UTF-8字符以 2 个或更多字节编码。

GNU 有一个计划(看)来解决这个问题,工作正在进行中,但还没有实现。

FreeBSD 或 Solaristr没有这个问题。


同时,对于 的大多数用例tr,您可以使用支持多字节字符的 GNU sed 或 GNU awk。

例如,您的:

tr -cs '[[:alpha:][:space:]]' ' '

可以写成:

gsed -E 's/( |[^[:space:][:alpha:]])+/ /'

或者:

gawk -v RS='( |[^[:space:][:alpha:]])+' '{printf "%s", sep $0; sep=" "}'

要在小写和大写 ( tr '[:upper:]' '[:lower:]') 之间进行转换:

gsed 's/[[:upper:]]/\l&/g'

(这l是小写字母L,而不是1数字)。

或者:

gawk '{print tolower($0)}'

为了便携性,perl还有另一种选择:

perl -Mopen=locale -pe 's/([^[:space:][:alpha:]]| )+/ /g'
perl -Mopen=locale -pe '$_=lc$_'

如果您知道数据可以用单字节字符集表示,那么您可以用该字符集处理它:

(export LC_ALL=ru_RU.iso88595
 iconv -f utf-8 |
   tr -cs '[:alpha:][:space:]' ' ' |
   iconv -t utf-8) < Russian-file.utf8

答案2

只需使用 GNU sed (具有适当的LANG环境变量,例如en_US.UTF-8):

% sed 'y/123/abc/; y/āōī/456/' <<< test123ingmāōī
testabcingm456

相关内容