tr analog für Unicode-Zeichen?

7

Ich brauche ein internationalisiertes Dienstprogramm, das dasselbe tut wie tr: holt Zeichen aus dem Stream und ersetzt sie durch ein entsprechendes Zeichen. Keine spezielle Falllösung wie von unten nach oben, sondern eine allgemeine Falllösung ist erforderlich. sedWenn möglich ohne Gorillion- Anrufe.

Beachten Sie, dass trdies unter Linux nicht funktioniert: Es übersetzt Bytes, keine Zeichen. Dies schlägt bei Multibyte-Codierungen fehl.

$ tr --version | head -n 1
tr (GNU coreutils) 8.23
$ echo $LC_CTYPE
en_US.UTF-8
$ echo 'Ångstrom' | tr Æ Œ         
Ņngstrom
Fedoraman
quelle
Das hier gegebene Beispiel von Gilles zeigt dieses Problem mit FreeBSD trin einem UTF-8-Gebietsschema nicht.
JdeBP

Antworten:

9

GNU sedarbeitet mit Multi-Byte-Zeichen. Damit:

$ echo é½Æ | sed 'y/é½Æ/ABŒ/'
ABŒ

Es ist nicht so sehr, dass GNU trnicht internationalisiert wurde, sondern dass es keine Multi-Byte-Zeichen unterstützt (wie die Nicht-ASCII-Zeichen in UTF-8-Gebietsschemas). GNU trwürde damit arbeiten Æ, Œsolange sie wie im iso8859-15-Zeichensatz Einzelbyte waren.

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

In jedem Fall hat das nichts mit Linux zu tun, es geht um die trImplementierung auf dem System. Ob dieses System Linux als Kernel verwendet oder trfür Linux erstellt wurde oder die Linux-Kernel-API verwendet, ist nicht relevant, da dieser Teil der trFunktionalität im Benutzerbereich stattfindet.

Busybox trund GNU trsind am häufigsten in Distributionen von Software zu finden, die für Linux entwickelt wurden, und unterstützen keine Multi-Byte-Zeichen. Es gibt jedoch auch andere, die auf Linux portiert wurden, wie die trdes Erbstück-Toolchests (von OpenSolaris portiert) oder von ast- öffne das zu tun.

Beachten Sie, dass sed‚s ynicht Bereiche unterstützt wie a-z. Beachten Sie außerdem, dass das Skript, sed 'y/é½Æ/ABŒ/'das im UTF-8-Zeichensatz geschrieben ist, nicht mehr wie erwartet funktioniert, wenn es in einem Gebietsschema aufgerufen wird, in dem UTF-8 nicht der Zeichensatz ist.

Eine Alternative könnte sein perl:

perl -Mopen=locale -Mutf8 -pe 'y/a-zé½Æ/A-ZABŒ/'

Oben wird erwartet, dass sich der Perl-Code in UTF-8 befindet, aber er verarbeitet die Eingabe in der Codierung des Gebietsschemas (und die Ausgabe in derselben Codierung). Wenn es in einem UTF-8-Gebietsschema aufgerufen wird, wird ein UTF-8 Æ(0xc3 0x86) in ein UTF-8 Œ(0xc5 0x92) und in einem ISO8859-15-Format, jedoch für 0xc6 -> 0xbc, transliteriert.

In den meisten Shells sollte es in Ordnung sein, diese UTF-8-Zeichen in einfachen Anführungszeichen zu haben, auch wenn das Skript in einem Gebietsschema aufgerufen wird, in dem UTF-8 nicht der Zeichensatz ist (eine Ausnahme ist, yashdie sich beschweren würde, wenn diese Bytes keine gültigen Zeichen bilden im Gebietsschema). Wenn Sie jedoch andere Anführungszeichen als einfache Anführungszeichen verwenden, kann dies zu Problemen führen. Zum Beispiel,

perl -Mopen=locale -Mutf8 -pe "y/♣\`/&'/"

würde in einem Gebietsschema fehlschlagen, in dem der Zeichensatz BIG5-HKSCS ist, da die Codierung von \(0x5c) auch in einigen anderen Zeichen enthalten ist (wie α: 0xa3 0x5c, und die UTF-8-Codierung von endet zufällig in 0xa3).

Erwarten Sie auf keinen Fall Dinge wie

perl -Mopen=locale -Mutf8 -pe 'y/Á-Ź/A-Z/'

akute Akzente zu entfernen. Das obige ist eigentlich nur

perl -Mopen=locale -Mutf8 -pe 'y/\x{c1}-\x{179}/\x{41}-\x{5a}/'

Das heißt, der Bereich basiert auf den Unicode-Codepunkten. So reicht nicht sein nützlich außerhalb von sehr gut definierten Sequenzen , die in der „geschehen sein Recht wie“ Ordnung in Unicode A-Z, 0-9.

Wenn Sie akute Akzente entfernen möchten, müssen Sie erweiterte Tools wie Folgendes verwenden:

perl -Mopen=locale -MUnicode::Normalize -pe '
  $_ = NFKD($_); s/\x{301}//g; $_ = NFKC($_)'

Verwenden Sie Unicode-Normalisierungsformulare, um Zeichen zu zerlegen, die akuten Akzente (hier das Kombinationsformular U+0301) zu entfernen und neu zu komponieren.

Ein weiteres nützliches Tool zu transkribieren Unicode ist uconvvon ICU . Zum Beispiel könnte das Obige auch geschrieben werden als:

uconv -x '::NFKD; \u0301>; ::NFKC;'

Würde aber nur mit UTF-8-Daten funktionieren. Sie würden brauchen:

iconv -t utf-8 | uconv -x '::NFKD; \u0301>; ::NFKC;' | iconv -f utf-8

Um Daten im Gebietsschema des Benutzers verarbeiten zu können.

Stéphane Chazelas
quelle
Die Perl-Lösung scheint nur teilweise zu funktionieren: echo 'été à la plage' | perl -Mopen=locale -MUnicode::Normalize -pe '$_ = NFKD($_); s/\x{301}//g; $_ = NFKC($_)'Shows ete à la plage, dh das é wurde geändert, aber nicht das à.
David Faure
Ah, ich verstehe, ich muss auch \ x {300} entfernen. Entschuldigung für den Lärm. Hoffe es hilft jemandem eines Tages;)
David Faure
1
@DavidFaure oder s/\pM//gum jede Kombinationsmarke zu entfernen, wenn Sie dies möchten.
Stéphane Chazelas
1

In Bash können Sie die Parametererweiterung verwenden .

ÅErfolgreich ersetzen :

$ string='Hello Ångstrom'
$ a='Å'
$ b='Œ'
$ printf '%s\n' "${string//${a}/${b}}"
Hello Œngstrom

Der Versuch zu ersetzen Æ, der nicht Teil der Zeichenfolge ist:

$ string='Hello Ångstrom'
$ a='Æ'
$ b='Œ'
$ printf '%s\n' "${string//${a}/${b}}"
Hello Ångstrom
nxnev
quelle
0

Es kann Ihr Kodierungsschema sein. Versuchen Sie es wie folgt durch iconv:

echo Ångstrom | iconv -f UTF-8 | tr 'Å' 'Œ'

Kommt raus mit: Œngstrom

Marshall Whittaker
quelle