Wie mache ich tr auf Nicht-ASCII-Zeichen (Unicode-Zeichen) aufmerksam?

36

Ich versuche, einige Zeichen aus der Datei zu entfernen (UTF-8). Ich benutze trfür diesen Zweck:

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

Die Datei enthält einige fremde Zeichen (wie "Латвийская" oder "àé"). trscheint sie nicht zu verstehen: es behandelt sie als Nicht-Alpha und entfernt sie auch.

Ich habe versucht, einige meiner Ländereinstellungen zu ändern:

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

Leider hat keines davon funktioniert.

Wie kann ich trUnicode verstehen lassen?

MatthewRock
quelle

Antworten:

29

Das ist eine bekannte ( 1 , 2 , 3 , 4 , 5 , 6 ) Einschränkung der GNU-Implementierung von tr.

Es ist nicht so sehr so, dass es keine Fremd- , Nicht-Englisch- oder Nicht-ASCII-Zeichen unterstützt, aber dass es keine Mehrbyte-Zeichen unterstützt.

Diese kyrillischen Zeichen werden in Ordnung behandelt, wenn sie im Zeichensatz iso8859-5 (Einzelbyte pro Zeichen) geschrieben sind (und Ihr Gebietsschema diesen Zeichensatz verwendet hat). Ihr Problem ist jedoch, dass Sie UTF-8 verwenden, wenn es sich nicht um ASCII handelt Zeichen werden in 2 oder mehr Bytes codiert.

GNU hat einen Plan (siehe auch ), um das zu beheben und die Arbeit ist im Gange, aber noch nicht da.

FreeBSD oder Solaris trhaben das Problem nicht.


In der Zwischenzeit können Sie für die meisten Anwendungsfälle von trGNU sed oder GNU awk verwenden, die Multibyte-Zeichen unterstützen.

Zum Beispiel Ihr:

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

könnte geschrieben werden:

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

oder:

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

So konvertieren Sie zwischen Groß- und Kleinschreibung ( tr '[:upper:]' '[:lower:]'):

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

(das list ein Kleinbuchstabe L, nicht die 1Ziffer).

oder:

gawk '{print tolower($0)}'

Für die Portabilität perlist eine andere Alternative:

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

Wenn Sie wissen, dass die Daten in einem Einzelbyte-Zeichensatz dargestellt werden können, können Sie sie in diesem Zeichensatz verarbeiten:

(export LC_ALL=ru_RU.iso88595
 iconv -f utf-8 |
   tr -cs '[:alpha:][:space:]' ' ' |
   iconv -t utf-8) < Russian-file.utf8
Stéphane Chazelas
quelle
1
Ich habe Ihre Frage wegen Informationen über tr akzeptiert. Ich habe das Problem gelöst und die Frage, wie es zu lösen ist, gestrichen (also finden Leute, die nach tr suchen, nur Informationen über tr, keine willkürlichen Probleme). Wenn Sie bitte auch die Lösung entfernen könnten, da sie nicht mehr benötigt wird, wäre ich Ihnen dankbar.
MatthewRock
3
@MatthewRock Ich habe es beibehalten, aber umformuliert und allgemeiner formuliert, da es für Leute mit dem gleichen Problem nützlich wäre, ein Wort zu verlieren.
Stéphane Chazelas
Woher wissen Sie, dass Kyrillisch (normalerweise) in ISO 8859-5 codiert ist? Haben Sie jemals einen russischen Text in etwas anderem als Unicode gesehen?
Incnis Mrsi
9
@IncnisMrsi, hier geht es nur darum, dass ISO 8859-5 einer dieser Einzelbyte-Zeichensätze mit diesen kyrillischen Zeichen ist. Ob es weit verbreitet ist oder nicht, spielt hier keine Rolle. Wenn Sie ein Gebietsschema mit KOI-R oder Windows-1251-Zeichensatz haben, verwenden Sie es stattdessen.
Stéphane Chazelas
@IncnisMrsi Russisch im Web wird fast immer in UTF-8 (oder gelegentlich in Windows-1251) codiert, aber nur, weil wir den Schmerz vieler Einzelbyte-Codierungen von Anfang an gespürt haben. Hier ist eine alte (circa 1998) Webseite mit einem (nicht funktionsfähigen) Codierungsumschalter: sch57.ru/collect .
Alex Shpilkin