我正在尝试从文件(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