Не можете использовать `cut -c` (` --characters`) с 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работает как ожидалось;
Он часто используется для подсчета байтов с помощью option -c( --bytes). (Обратите внимание на запутанные имена опций.)

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

Но он также может считать символы с помощью option -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
Volker Siegel
источник
Интересный! Похоже, -cиспользует тот же код, что и -b. Вы смотрели на исходный код? Может быть, вы можете найти подсказку для чего -cна самом деле.
Михас

Ответы:

13

Вы не сказали, что cutвы используете, но так как вы упомянули опцию GNU long, --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 года, озаглавленную «Случайные заметки и указатели о текущих усилиях по добавлению поддержки многобайтовой и Unicode в 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диапазонов.

Ройс Уильямс
источник