Как сделать так, чтобы tr знал не-ascii (юникод) символы?

36

Я пытаюсь удалить некоторые символы из файла (UTF-8). Я использую trдля этого:

tr -cs '[[:alpha:][:space:]]' ' ' <testdata.dat 

Файл содержит несколько иностранных символов (например, «Латвийская» или «àé»). trкажется, не понимает их: он рассматривает их как не-альфа и удаляет тоже.

Я попытался изменить некоторые из моих настроек локали:

LC_CTYPE=C LC_COLLATE=C tr -cs '[[:alpha:][:space:]]' ' ' <testdata.dat
LC_CTYPE=ru_RU.UTF-8 LC_COLLATE=C tr -cs '[[:alpha:][:space:]]' ' ' <testdata.dat
LC_CTYPE=ru_RU.UTF-8 LC_COLLATE=ru_RU.UTF-8 tr -cs '[[:alpha:][:space:]]' ' ' <testdata.dat

К сожалению, ничего из этого не сработало.

Как я могу trпонять Unicode?

MatthewRock
источник

Ответы:

29

Это известное ( 1 , 2 , 3 , 4 , 5 , 6 ) ограничение реализации GNU tr.

Это не так много, что он не поддерживает иностранные , не английские или не ASCII символы, но он не поддерживает многобайтовые символы.

Эти символы кириллицы будут обрабатываться нормально, если они записаны в наборе символов iso8859-5 (однобайтовый на символ) (а ваша локаль использовала этот набор символов), но ваша проблема заключается в том, что вы используете UTF-8, где не-ASCII символы кодируются в 2 или более байтов.

У GNU есть план (см. Также ), чтобы исправить это, и работа ведется, но еще не началась.

FreeBSD или Solaris trне имеют проблемы.


В то же время, в большинстве случаев trвы можете использовать GNU sed или GNU awk, которые поддерживают многобайтовые символы.

Например, ваш:

tr -cs '[[:alpha:][:space:]]' ' '

может быть написано:

gsed -E 's/( |[^[:space:][:alpha:]])+/ /'

или:

gawk -v RS='( |[^[:space:][:alpha:]])+' '{printf "%s", sep $0; sep=" "}'

Чтобы преобразовать нижний и верхний регистр ( tr '[:upper:]' '[:lower:]'):

gsed 's/[[:upper:]]/\l&/g'

( lэто строчная L, а не 1цифра).

или:

gawk '{print tolower($0)}'

Для мобильности perlесть еще одна альтернатива:

perl -Mopen=locale -pe 's/([^[:space:][:alpha:]]| )+/ /g'
perl -Mopen=locale -pe '$_=lc$_'

Если вы знаете, что данные могут быть представлены в однобайтовом наборе символов, вы можете обработать их в этой кодировке:

(export LC_ALL=ru_RU.iso88595
 iconv -f utf-8 |
   tr -cs '[:alpha:][:space:]' ' ' |
   iconv -t utf-8) < Russian-file.utf8
Стефан Шазелас
источник
1
Я принял ваш вопрос из-за информации о тр. Я решил проблему и удалил вопрос о том, как ее решить (поэтому люди, ищущие tr, найдут только информацию о tr, а не какую-то произвольную проблему). Если бы вы тоже могли удалить решение, так как оно больше не нужно, я был бы благодарен.
MatthewRock
3
@MatthewRock Я сохранил это, но перефразировал его и сделал более общим, так как рассказывать о нем было бы полезно людям с такой же проблемой.
Стефан Шазелас
Откуда вы взяли, что кириллица (обычно) кодируется в ISO 8859-5? Вы когда-нибудь видели русский текст в Unicode?
Инснис Мрси
9
@IncnisMrsi, здесь важно только то, что ISO 8859-5 является одним из тех однобайтовых кодировок, в которых есть эти символы кириллицы. Широко используется это или нет, здесь не имеет значения. Если у вас есть локаль с кодировкой KOI-R или window-1251, обязательно используйте ее.
Стефан Шазелас
@IncnisMrsi Русский язык в сети почти всегда кодируется в UTF-8 (или иногда в Windows-1251), но только потому, что мы чувствовали боль многих однобайтовых кодировок на ранних этапах. Вот древняя (около 1998 года) веб-страница с (нефункциональным) переключателем кодирования: sch57.ru/collect .
Алексей Шпилкин