Nie można używać `cut -c` (` --characters`) z UTF-8?

15

Polecenie cutma opcję -cpracy na znakach zamiast bajtów z tą opcją -b. Ale to wydaje się nie działać, na poziomie en_US.UTF-8lokalnym:

Drugi bajt podaje drugi znak ASCII (który jest tak samo zakodowany w UTF-8):

$ printf 'ABC' | cut -b 2          
B

ale nie podaje drugiego z trzech greckich znaków spoza ASCII w ustawieniach regionalnych UTF-8:

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

W porządku - to drugi bajt . Zamiast tego
patrzymy na drugą postać :

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

To wygląda na zepsute.
Po niektórych eksperymentach okazuje się, że zakres 3-4pokazuje drugą postać:

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

Ale to tak samo, jak w bajtach od 3 do 4:

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

Czyli -cnie więcej niż -bdla UTF-8.

Spodziewałbym się, że ustawienia regionalne nie są odpowiednie dla UTF-8, ale dla porównania wcdziałają zgodnie z oczekiwaniami;
Jest często używany do liczenia bajtów, z opcją -c( --bytes). (Zwróć uwagę na mylące nazwy opcji).

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

Ale może również liczyć znaki za pomocą opcji -m( --chars), która po prostu działa:

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

Więc moja konfiguracja wydaje się być w porządku - ale coś jest wyjątkowego cut.

Może wcale nie obsługuje UTF-8? Ale wydaje się, że obsługuje znaki wielobajtowe, w przeciwnym razie nie musiałby obsługiwać znaków -bi -c.

Co jest nie tak? I dlaczego?


Konfiguracja regionalna wygląda na utf8, o ile mogę powiedzieć:

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

Dane wejściowe bajt po bajcie:

$ printf 'αβγ' | hd 
00000000  ce b1 ce b2 ce b3                                 |......|
00000006
Volker Siegel
źródło
Ciekawy! Wygląda na -cto, że używa tego samego kodu co -b. Czy spojrzałeś na kod źródłowy? Może uda ci się znaleźć wskazówkę, co -cwłaściwie jest przeznaczone.
michas

Odpowiedzi:

13

Nie powiedziałeś, którego cutużywasz, ale skoro wspomniałeś o opcji długiego GNU --characters, zakładam, że to ta. W takim przypadku zwróć uwagę na ten fragment zinfo coreutils 'cut invocation' :

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

Wybierz, aby drukować tylko znaki z pozycji wymienionych na liście znaków. Tak jak -bna razie , ale internacjonalizacja to zmieni.

(podkreślenie dodane)

W tej chwili GNU cutzawsze działa w kategoriach jednobajtowych „znaków”, więc oczekiwane zachowanie jest oczekiwane.


Obsługa zarówno opcji, jak -bi -copcji jest wymagana przez POSIX - nie zostały dodane do GNU, cutponieważ miały obsługę wielu bajtów i działały poprawnie, ale aby uniknąć błędów w danych wejściowych zgodnych z POSIX. To samo -czostało zrobione w niektórych innych cutimplementacjach, chociaż przynajmniej nie FreeBSD i OS X.

Jest to historyczny zachowanie od -c. -bzostał niedawno dodany, aby przejąć rolę bajtu, dzięki czemu -cmoże pracować ze znakami wielobajtowymi. Może za kilka lat będzie działał zgodnie z oczekiwaniami, chociaż postęp nie był tak szybki (minęło już ponad dekadę). GNU cut nawet jeszcze nie implementuje tej -nopcji , chociaż jest ortogonalna i ma na celu pomóc w przejściu. Istnieją potencjalne problemy ze zgodnością ze starymi skryptami, które mogą stanowić problem, chociaż nie wiem ostatecznie, jaki jest tego powód.

Michael Homer
źródło
1
dobra robota. ten sam rodzaj komentarzy znajdziesz także w trdokumentach GNU . i nawet tarjeśli źle się nie zapamiętam. Myślę, że to duży projekt.
mikeserv
Czy jest jakieś obejście problemu dla probelmu unicode cut? Na przykład, gdzie można pobrać źródła łatane cut? A może łatwiej byłoby użyć innego narzędzia? ( grepponiższe rozwiązanie nie działa płynnie z zakresami np. 5-8,44-49)
dma_k
zobacz 2017 artykuł, podtytułu „Random notatki i wskazówki dotyczące trwającego wysiłku, aby dodać wielobajtową i Unicode wsparcie w GNU Coreutils” : crashcourse.housegordon.org/coreutils-multibyte-support.html
myrdd
możesz znaleźć alternatywy dla cut -ctutaj: superuser.com/questions/506164/…
myrdd
5

colrm(część util-linux, powinna być już zainstalowana w większości dystrybucji) wydaje się znacznie lepiej radzić sobie z internacjonalizacją:

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

Uwaga na numerację: colrm Nusunie kolumny z N, drukuje znaki do N-1.

( kredyty )

Skippy le Grand Gourou
źródło
2

Ponieważ wiele grepimplementacji obsługuje wiele bajtów, możesz także użyć ich grep -odo symulacji niektórych zastosowań cut -c.

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

Dostosuj liczbę okresów, aby zasymulować cutzakresy.

Royce Williams
źródło