如何使tr意识到非ASCII字符?


36

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

Answers:


29

这是一个公知的(123456)的GNU实现的限制tr

它不支持不支持外来,非英语或非ASCII字符,但不支持多字节字符。

如果使用iso8859-5(每个字符单字节)字符集(并且您的语言环境使用该字符集)编写这些西里尔字母,就可以了,但是您的问题是您使用的是UTF-8(非ASCII)字符编码为2个或更多字节。

GNU的有一个计划(见)以修复工作正在进行中,但还没有。

FreeBSD或Solaris tr没问题。


同时,对于的大多数使用情况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

1
由于有关tr的信息,我已经接受了您的问题。我已经解决了这个问题,并删除了有关如何解决它的问题(因此,寻找tr的人只会找到有关tr的信息,而不会发现一些任意问题)。如果您也可以删除解决方案,因为不再需要它,我将很感激。
MatthewRock 2015年

3
@MatthewRock我保留了它,但改写了它,并使其更加通用,因为绕个字对有相同问题的人很有用。
斯特凡Chazelas

您从哪里知道西里尔字母(习惯上)是用ISO 8859-5编码的?您是否看到过除Unicode之外的其他任何俄语文本?
Incnis Mrsi 2015年

9
@IncnisMrsi,这里最重要的是ISO 8859-5是具有西里尔字母的单字节字符集之一。在这里是否广泛使用都无关紧要。如果您的语言环境带有KOI-R或window-1251字符集,则请改用它。
斯特凡Chazelas

网络上的@IncnisMrsi俄语几乎总是以UTF-8编码(或偶尔以Windows-1251编码),但这只是因为我们早就感觉到许多单字节编码的痛苦。这是一个古老的网页(大约1998年),带有一个(非功能性的)编码切换器:sch57.ru/collect
Alex Shpilkin '18
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.