LINUX.ORG.RU

Перечисление в регулярных выражениях и UTF


0

0

Насколько я понимаю, выражение [a-z] означает любую малую (строчную) английскую букву от a до z. Правильно? Соответственно [^a-z] означает любую букву кроме 26 малых от a до z. Выражение [A-Za-z] в grep и sed означает любую английскую букву, а команда sed 's/[^A-Za-z]//g' удалит из потока все символы кроме английских букв и концов строк. Русские эквиваленты: [А-Яа-яЁё] и 's/[^А-Яа-яЁё]//g'. Всё пока правильно?

Так вот, у меня в локали ru_RU.UTF-8 это не работает. Команда

dd if=/dev/urandom bs=102400 count=1 | iconv -c -f ucs2 -t utf-8 | sed 's/[^A-Za-z]//g' > random.txt 
должна отправить в random.txt только английские буквы. Вместо них — мешанина символов, главным образом иероглифов.

Проблема в том, что английские символы — 1-байтные, а в потоке преобладают многообайтные? Если заменить на русские буквы:

dd if=/dev/urandom bs=102400 count=1 | iconv -c -f ucs2 -t utf-8 | sed 's/[^А-Яа-яЁё]//g' > random.txt 
результат будет схож — тоже значительная часть лишних знаков проходит через фильтр.

Если попробовать фильтровать не sed-ом, а grep-ом, результаты будут такие же.

Проблема у меня в системе, или в grep и sed? Как можно сделать такой фильтр помимо перечисления всех возможных символов?

Заранее спасибо.

★★★★★

Похоже, что в область, скажем A-Z попадают не только английские прописные, но и еще буквы со специальными пометками, тоесть A-Ä-Â-B, ну или что-то такое.

anonymous
()
Ответ на: комментарий от anonymous

> Похоже, что в область, скажем A-Z попадают не только английские прописные, но и еще буквы со специальными пометками, тоесть A-Ä-Â-B, ну или что-то такое.

У меня туда попадают даже иероглифы.

Вот к русским — да, и прочие символы кириллицы, и надстрочные знаки. Там ещё как-то можно это объяснить тем, что при переводе из UCS-2 в UTF-8 оно несколько раз попадает на границы диапазонов и разрывается.

Какая локаль? Тоже UTF-8? Было бы интересно проверить в какой-нибудь другой: например 8-битной и UCS-2.

question4 ★★★★★
() автор топика

так всё ожидаемо: dd if=/dev/urandom bs=102400 count=1 | iconv -c -f ucs2 -t utf-8 | sed 's/[^abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMOPQRSTUVWXYZ]//g' > random.txt

Видно в a-z действительно входят всякие буквы, а какие-нить иероглифы как-то соответствуют английским буквам.

gena2x ★★★
()

А вам обязательно использовать posix-овые утилиты? В том же питоне есть специальные выражения для букв, которые учитывают кодировку. С перлом не знаком, но подозреваю, что там тоже есть что-нибудь такое.

anonymous
()
Ответ на: комментарий от anonymous

> А вам обязательно использовать posix-овые утилиты?

Задачу решил просто перечислив нужные символы. Ещё до того, как запостил вопрос. Но стало интересно, что это: задуманное авторами поведение или недоработка (если не ошибаюсь, grep начал нормально работать с UTF всего года полтора назад). И можно ли решить задачу лучше.

Заодно ещё вопрос по sed-у: как в перечисление в квадратных скобках вставить квадратные скобки? Экранировать их \ не получается. Только как \x5b и \x5d ?

В том же питоне есть специальные выражения для букв, которые учитывают кодировку.

Сразу вопрос: как такое перечисление работает в питоне? Я проверил VIM — там работает.

С перлом не знаком, но подозреваю, что там тоже есть что-нибудь такое.

Есть.

question4 ★★★★★
() автор топика
Ответ на: комментарий от question4

На сколько я помню, в питоновском модуле re множество \w отвечает за все буквы, цифры и знак подчеркивания, при компиляции регэкспа можно указать флаг LOCALE.

anonymous
()
Ответ на: комментарий от anonymous

> в питоновском модуле re множество \w отвечает за все буквы, цифры и знак подчеркивания

Спасибо. Но интереснее, как с диапазонами идущих подряд русских букв.

question4 ★★★★★
() автор топика
Ответ на: комментарий от gena2x

> какие-нить иероглифы как-то соответствуют английским буквам

Что меня смущает: в UTF-8 у первых 128 символов (включая английские буквы) старший бит 0. У всех остальных старшие биты всегда 1.

question4 ★★★★★
() автор топика

Обнаружил интересную вещь: если grep работает с регулярными выражениями в стиле перла (-P), [a-z] обрабатывает корректно. Но русские буквы обрабатывает как 2 однобайтных символа :)

question4 ★★★★★
() автор топика
echo 'АБВГДЕ' | perl -Mutf8 -npe 'utf8::decode $_; s/[Б-Д]//g; utf8::encode $_'

Мдя.... длинно получается.

Biga
()
Ответ на: комментарий от anonymous

> Похоже, что в область, скажем A-Z попадают не только английские прописные, но и еще буквы со специальными пометками, тоесть A-Ä-Â-B, ну или что-то такое.

Мейнтейнер grep утверждает, что такое поведение соответствует POSIX. Видимо, так следует трактовать фразу из в man grep «Many locales sort characters in dictionary order, and in these locales [a-d] is typically not equivalent to [abcd]». Только я не ожидал, что они эту «сортировку как в словаре» включают для языков, не использующих соответствующие символы. Баг не приняли.

Фокусы с grep -P оказались ошибкой в PCRE. Написал и им.

question4 ★★★★★
() автор топика
Ответ на: комментарий от question4

Там ответили «у нас всё работает, просто кто-то не умеет нашей библиотекой пользоваться». pcregrep с ключом -u нормально обрабатывает UTF, но обещают глюки на 8-битных кодировках.

Пересобрал grep с учётом их рекомендаций. Не заработало. Собрал ванильный grep без патчей Gentoo. Оказалось, он тоже не понимает UTF! Болото...

question4 ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.