Kann mit UTF-8 nicht `cut -c` (` --characters`) verwendet werden?

15

Der Befehl cutverfügt über eine Option -c, mit der Zeichen anstelle von Bytes bearbeitet werden können -b. Aber das scheint im en_US.UTF-8Gebietsschema nicht zu funktionieren :

Das zweite Byte gibt das zweite ASCII-Zeichen an (das in UTF-8 genauso codiert ist):

$ printf 'ABC' | cut -b 2          
B

gibt aber nicht das zweite von drei griechischen Nicht-ASCII-Zeichen im UTF-8-Gebietsschema an:

$ printf 'αβγ' | cut -b 2         
�

Das ist in Ordnung - es ist das zweite Byte .
Also schauen wir uns stattdessen das zweite Zeichen an:

$ printf 'αβγ' | cut -c 2 
�

Das sieht kaputt aus.
Bei einigen Experimenten hat sich herausgestellt, dass der Bereich 3-4das zweite Zeichen zeigt:

$ printf 'αβγ' | cut -c 3-4
β

Das ist aber genauso wie bei den Bytes 3 bis 4:

$ printf 'αβγ' | cut -b 3-4
β

Das -cmacht also nicht mehr als das -bfür UTF-8.

Ich würde erwarten, dass das Setup des Gebietsschemas für UTF-8 nicht richtig ist, aber im Vergleich dazu wcwie erwartet funktioniert.
Es wird häufig zum Zählen von Bytes mit der Option -c( --bytes) verwendet. (Beachten Sie die verwirrenden Optionsnamen.)

$ printf 'αβγ' | wc -c
6

Es können aber auch Zeichen mit option -m( --chars) gezählt werden, was einfach funktioniert:

$ printf 'αβγ' | wc -m
3

Meine Konfiguration scheint also in Ordnung zu sein - aber etwas Besonderes ist daran cut.

Vielleicht unterstützt es UTF-8 überhaupt nicht? Aber es scheint , Multi-Byte - Zeichen zu unterstützen, sonst wäre es muß nicht unterstützen -bund -c.

Also, was ist falsch? Und warum?


Das Setup des Gebietsschemas sieht für utf8 richtig aus, soweit ich das beurteilen kann:

$ locale
LANG=en_US.UTF-8
LANGUAGE=en_US
LC_CTYPE=en_US.UTF-8
LC_NUMERIC="en_US.UTF-8"
LC_TIME="en_US.UTF-8"
LC_COLLATE="en_US.UTF-8"
LC_MONETARY="en_US.UTF-8"
LC_MESSAGES="en_US.UTF-8"
LC_PAPER="en_US.UTF-8"
LC_NAME="en_US.UTF-8"
LC_ADDRESS="en_US.UTF-8"
LC_TELEPHONE="en_US.UTF-8"
LC_MEASUREMENT="en_US.UTF-8"
LC_IDENTIFICATION="en_US.UTF-8"
LC_ALL=

Die Eingabe, byteweise:

$ printf 'αβγ' | hd 
00000000  ce b1 ce b2 ce b3                                 |......|
00000006
Volker Siegel
quelle
Interessant! Es sieht so aus -c, als würde derselbe Code verwendet wie -b. Hast du dir den Quellcode angesehen? Vielleicht findest du einen Hinweis, wofür -ceigentlich gedacht ist.
Michas

Antworten:

13

Sie haben nicht gesagt, welche cutSie verwenden, aber da Sie die Option GNU long erwähnt haben, --charactersgehe ich davon aus, dass es diese ist. In diesem Fall beachten Sie diese Passage ausinfo coreutils 'cut invocation' :

‘-c character-list’
‘--characters=character-list’

Wählen Sie zum Drucken nur die Zeichen aus, die in der Zeichenliste aufgeführt sind. Das gleiche wie -bbisher , aber die Internationalisierung wird das ändern.

(Betonung hinzugefügt)

Momentan cutfunktioniert GNU immer in Form von Einzelbyte-Zeichen, daher wird das Verhalten, das Sie sehen, erwartet.


Beide Unterstützung der -bund -cOptionen wird von POSIX erforderlich - sie wurden zu GNU nicht hinzugefügt , cutweil es Multi-Byte - Unterstützung hatte und sie arbeiteten richtig, aber zu vermeiden Fehler auf POSIX-kompatiblen Eingang geben. Das Gleiche -cwurde in einigen anderen cutImplementierungen gemacht, zumindest nicht in FreeBSD und OS X.

Dies ist das historische Verhalten von -c. -bwurde neu hinzugefügt, um die Byte-Rolle zu übernehmen, -cdamit mit Mehrbyte-Zeichen gearbeitet werden kann. Vielleicht funktioniert es in ein paar Jahren wie gewünscht, obwohl der Fortschritt nicht gerade schnell war (es ist bereits über ein Jahrzehnt her). GNU cut implementiert die -nOption noch nicht einmal, obwohl sie orthogonal ist und den Übergang erleichtern soll. Es gibt potenzielle Kompatibilitätsprobleme mit alten Skripten, die ein Problem sein können, obwohl ich nicht genau weiß, woran das liegt.

Michael Homer
quelle
1
gute Arbeit. Sie werden die gleiche Art von Kommentaren auch in den GNU- trDokumenten finden. und selbst tarwenn ich mich nicht erinnere. Ich denke, es ist ein großes Projekt.
mikeserv
Gibt es eine Problemumgehung für Unicode-Probleme cut? Wo kann man zum Beispiel die Quellen für Patches herunterladen cut? Oder wäre es einfacher, ein anderes Dienstprogramm zu verwenden? ( grepLösung unten funktioniert nicht reibungslos mit Bereichen, z. B. 5-8,44-49)
dma_k
sehen dies 2017 Artikel, Unter dem Titel „Random Notizen und Hinweise in Bezug auf den laufenden Aufwand Multibyte und Unicode - Unterstützung in GNU Coreutils hinzufügen“ : crashcourse.housegordon.org/coreutils-multibyte-support.html
myrdd
Sie können einige Alternativen finden cut -chier: superuser.com/questions/506164/...
myrdd
5

colrm(ein Teil von util-linux, sollte bereits auf den meisten Distributionen installiert sein) scheint die Internationalisierung viel besser zu beherrschen:

$ echo 'αβγ' | colrm 3
αβ
$ echo 'αβγ' | colrm 2
α

Achten Sie auf die Nummerierung: colrm NEntfernt Spalten aus Nund druckt Zeichen bis zu N-1.

( Credits )

Skippy le Grand Gourou
quelle
2

Da viele grepImplementierungen Multibyte-fähig sind, können Sie auch grep -oeinige Verwendungen von simulieren cut -c.

$ echo Τηεοδ29 | grep -o '^..'
Τη
$ echo Τηεοδ29 | egrep -o '^..' | grep -o '.$'
η

Passen Sie die Anzahl der Perioden an, um cutBereiche zu simulieren .

Royce Williams
quelle