Не можете використовувати `cut -c` (` --символи`) з UTF-8?


15

У команді cutє можливість -cпрацювати над символами, а не байтами з цим параметром -b. Але це, схоже, не працює, на en_US.UTF-8мові:

Другий байт дає другий символ ASCII (який закодований точно так само в UTF-8):

$ printf 'ABC' | cut -b 2          
B

але не дає другого з трьох грецьких символів, що не належать до ASCII, у мові UTF-8:

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

Це добре - це другий байт .
Тому ми дивимось на другий символ замість цього:

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

Це виглядає розбитим.
З деякими експериментами виявляється, що діапазон 3-4показує другий символ:

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

Але це точно так само, як у байтах 3 до 4:

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

Таким чином, -cце не більше, ніж -bдля UTF-8.

Я б очікував, що налаштування місцевості не підходить для UTF-8, але порівняно, wcпрацює як очікувалося;
Він часто використовується для підрахунку байтів, за допомогою параметра -c( --bytes). (Зверніть увагу на заплутані назви опцій.)

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

Але він також може рахувати символи з опцією -m( --chars), яка просто працює:

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

Тож моя конфігурація здається нормальною, але щось особливе cut.

Можливо, він взагалі не підтримує UTF-8? Але це, здається, підтримує багатобайтові символи, інакше його не потрібно буде підтримувати -bі -c.

Отже, що не так? І чому?


Наскільки я можу сказати, налаштування локалі виглядає правильно для utf8:

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

Вхід, байт за байтом:

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

Цікаво! Схоже -c, використовується той же код, що і -b. Ви подивилися на вихідний код? Можливо, ви можете знайти підказку, для чого -cнасправді призначено.
micha

Відповіді:


13

Ви не сказали, що cutвикористовуєте, але оскільки ви згадали про довгий варіант GNU, --charactersя вважаю, що це той. У цьому випадку відзначте цей уривок ізinfo coreutils 'cut invocation' :

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

Виберіть для друку лише символи на позиціях, зазначених у списку символів. Те саме, що -bзараз , але інтернаціоналізація це змінить.

(наголос додано)

На даний момент GNU cutзавжди працює з точки зору однобайтових "символів", тому поведінка, яку ви бачите, очікується.


Підтримка -bі -cпараметрів, і параметрів потрібна POSIX - вони не були додані до GNU, cutоскільки у нього була багатобайтова підтримка, і вони працювали належним чином, але щоб уникнути помилок на вході, сумісному з POSIX. Те ж саме -cбуло зроблено і в деяких інших cutреалізаціях, хоча не принаймні FreeBSD та OS X.

Це історична поведінка Росії -c. -bНещодавно додано, щоб перейняти байтну роль, щоб -cможна було працювати з багатобайтовими символами. Можливо, через кілька років це буде працювати як завгодно послідовно, хоча прогрес точно не був швидким (минуло вже десятиліття). GNU cut ще не реалізує цю -nопцію , хоча вона є ортогональною і призначена для допомоги у переході. Існують потенційні проблеми сумісності зі старими сценаріями, що може викликати занепокоєння, хоча я точно не знаю, в чому причина.


1
хороша робота. ви знайдете такі самі коментарі і в trдокументах GNU . і навіть tarякщо я не згадую. я думаю, що це великий проект.
mikeserv

Чи є якесь вирішення проблеми unicode cut? Наприклад, де можна завантажити джерела для виправлених cut? Або було б легше використовувати іншу утиліту? ( grepрішення нижче не працює гладко з діапазонами, наприклад 5-8,44-49)
dma_k

дивіться цю статтю 2017 року, підзаголовком "Випадкові нотатки та покажчики щодо поточних зусиль для додавання багатобайтової та однокодової підтримки в GNU Coreutils" : crashcourse.housegordon.org/coreutils-multibyte-support.html
myrdd

Ви можете знайти деякі альтернативи cut -cтут: superuser.com/questions/506164/…
myrdd

5

colrm(частина util-linux, має бути вже встановлено на більшості дистрибутивів), схоже, справляється з інтернаціоналізацією набагато краще:

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

Остерігайтеся нумерації: colrm Nбуде видалено стовпці N, друкуючи символи до N-1.

( кредити )


2

Оскільки багато grepреалізацій знають багатобайти, ви також можете використовувати grep -oдля імітації деяких застосувань cut -c.

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

Відрегулюйте кількість періодів для імітації cutдіапазонів.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.