Non puoi usare `cut -c` (` --characters`) con UTF-8?


15

Il comando cutha un'opzione -cper lavorare sui caratteri, anziché sui byte con l'opzione -b. Ma questo non sembra funzionare, in en_US.UTF-8locale:

Il secondo byte fornisce il secondo carattere ASCII (che è codificato esattamente nello stesso UTF-8):

$ printf 'ABC' | cut -b 2          
B

ma non fornisce il secondo dei tre caratteri greci non ASCII nelle impostazioni internazionali UTF-8:

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

Va bene, è il secondo byte .
Quindi guardiamo invece al secondo personaggio :

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

Sembra rotto.
Con alcuni esperimenti, si scopre che l'intervallo 3-4mostra il secondo carattere:

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

Ma è lo stesso dei byte da 3 a 4:

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

Quindi il -cnon è più che -bper UTF-8.

Mi aspetto che l'impostazione della locale non sia corretta per UTF-8, ma in confronto wcfunziona come previsto;
Viene spesso usato per contare i byte, con option -c( --bytes). (Nota i nomi delle opzioni confuse.)

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

Ma può anche contare i caratteri con option -m( --chars), che funziona semplicemente:

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

Quindi la mia configurazione sembra essere ok, ma qualcosa di speciale cut.

Forse non supporta affatto UTF-8? Ma sembra supportare caratteri multi-byte, altrimenti non avrebbe bisogno di supportare -be -c.

Allora, cosa c'è che non va? E perché?


Il setup della localizzazione sembra giusto per utf8, per quanto ne so:

$ 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=

L'ingresso, byte per byte:

$ printf 'αβγ' | hd 
00000000  ce b1 ce b2 ce b3                                 |......|
00000006

Interessante! Sembra che -cstia usando lo stesso codice di -b. Hai dato un'occhiata al codice sorgente? Forse puoi trovare un suggerimento per cosa -cè realmente pensato.
Michas,

Risposte:


13

Non hai detto quale cutstai usando, ma dato che hai menzionato l'opzione GNU long --characters, suppongo sia quella. In tal caso, notare questo passaggio dainfo coreutils 'cut invocation' :

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

Selezionare per stampare solo i caratteri nelle posizioni elencate nell'elenco dei caratteri. Lo stesso -bper ora , ma l'internazionalizzazione lo cambierà.

(enfasi aggiunta)

Per il momento, GNU cutfunziona sempre in termini di "caratteri" a byte singolo, quindi è previsto il comportamento che vedi.


POSIX supporta entrambe le opzioni -be -cè richiesto da POSIX : non sono stati aggiunti a GNU cutperché aveva il supporto multi-byte e funzionavano correttamente, ma per evitare di dare errori sull'input conforme a POSIX. Lo stesso -cè stato fatto in alcune altre cutimplementazioni, sebbene non almeno quelle di FreeBSD e OS X.

Questo è il comportamento storico di -c. -bè stato recentemente aggiunto per assumere il ruolo di byte in modo che -cpossa funzionare con caratteri multi-byte. Forse tra qualche anno funzionerà come desiderato coerentemente, sebbene i progressi non siano stati esattamente rapidi (è già passato più di un decennio). GNU cut non ha ancora implementato l' -nopzione , anche se è ortogonale e intende aiutare la transizione. Ci sono potenziali problemi di compatibilità con i vecchi script, il che potrebbe essere un problema, anche se non so in modo definitivo quale sia il motivo.


1
buon lavoro. troverai lo stesso tipo di commenti anche nei trdocumenti di GNU . e anche tarse non ricordo male. immagino sia un grande progetto.
Mikeserv,

Esistono soluzioni alternative per il problema Unicode cut? Ad esempio, dove è possibile scaricare i sorgenti per le patch cut? O sarebbe più facile usare un'altra utility? (la grepsoluzione seguente non funziona in modo uniforme con intervalli, ad esempio 5-8,44-49)
dma_k

leggi questo articolo del 2017, sottotitolato "Note casuali e indicazioni relative allo sforzo in corso per aggiungere il supporto multibyte e unicode in GNU Coreutils" : crashcourse.housegordon.org/coreutils-multibyte-support.html
myrdd

puoi trovare alcune alternative cut -cqui: superuser.com/questions/506164/…
myrdd

5

colrm(parte di util-linux, dovrebbe essere già installato sulla maggior parte delle distribuzioni) sembra gestire l'internazionalizzazione molto meglio:

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

Attenzione alla numerazione: colrm Nrimuoverà le colonne da N, stampando i caratteri fino a N-1.

( crediti )


2

Poiché molte grepimplementazioni sono compatibili con più byte, è anche possibile utilizzare grep -oper simulare alcuni usi di cut -c.

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

Regola il numero di periodi per simulare cutintervalli.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.