Влияет ли (должно) LC_COLLATE на диапазоны символов?

27

Порядок сортировки через LC_COLLATEопределяет не только порядок сортировки отдельных символов, но и значение диапазонов символов. Или это? Рассмотрим следующий фрагмент:

unset LANGUAGE LC_ALL
echo B | LC_COLLATE=en_US grep '[a-z]'

Интуитивно, Bне в [a-z], так что это не должно ничего выводить. Вот что происходит в Ubuntu 8.04 или 10.04. Но на некоторых машинах, на которых запущен Debian lenny или squeeze, Bвстречается, потому что диапазон a-zвключает в себя все, что находится между aи zв порядке сортировки, включая заглавные буквы до Bконца Z.

Все протестированные системы имеют en_USсгенерированную локаль. Я также пытался варьировать локаль: на машинах, где Bуказано выше, то же самое происходит в каждой доступной локали (в основном на латинице:, {en_{AU,CA,GB,IE,US},fr_FR,it_IT,es_ES,de_DE}{iso8859-1,iso8859-15,utf-8}также китайские локали), кроме японской (в любой доступной кодировке) и C/ POSIX.

Что означают диапазоны символов в регулярных выражениях , когда вы выходите за рамки ASCII? Почему существует разница между некоторыми установками Debian с одной стороны и другими установками Debian и Ubuntu с другой? Как ведут себя другие системы? Кто прав, и кому следует сообщать об ошибке?

(Обратите внимание, что я специально спрашиваю о поведении диапазонов символов, например [a-z]в en_USлокалях, в первую очередь в системах на основе GNU libc. Я не спрашиваю, как сопоставлять строчные или строчные буквы ASCII.)


На двух машинах Debian, один из которых Bнаходится в [a-z]и один , где он не, выход LC_COLLATE=en_US locale -k LC_COLLATEIS

collate-nrules=4
collate-rulesets=""
collate-symb-hash-sizemb=1
collate-codeset="ISO-8859-1"

а выходной сигнал LC_COLLATE=en_US.utf8 locale -k LC_COLLATEIS

collate-nrules=4
collate-rulesets=""
collate-symb-hash-sizemb=2039
collate-codeset="UTF-8"
Жиль "ТАК - перестань быть злым"
источник
1
Не воспроизводится на экземпляре Debian Lenny, который мне пригодился. Не проверял, если en_USгенерируется, хотя.
Алекс
1
@alex Если языковой стандарт не сгенерирован, Cязыковой стандарт используется в качестве запасного варианта, и его порядок сопоставления - прямые байтовые значения, поэтому Bне будет совпадать. Тест в локали, которая появляется в выходных данных locale -a.
Жиль "ТАК - перестань быть злым"
1
Обратите внимание, что en_US НЕ совпадает с en_US.utf8 и обычно означает en_US.iso-8859-1, в зависимости от того, что именно вы установили. Если en_US (без суффикса) не появляется в выводе локали -a, у вас фактически нет этой локали. Что показывает LC_COLLATE = en_US locale -k LC_COLLATE?
Нил Мэйхью
1
С тех пор это оказалось практическим, а не теоретическим вопросом: почему заглавные буквы включены в ряд строчных букв в регулярном выражении awk?
Калеб
1
@isaac К сожалению, через 7 лет у меня нет доступа к какой-либо проблемной системе. Все они были модернизированы или списаны.
Жиль "ТАК - перестань быть злым"

Ответы:

3

Если вы используете что-то отличное от Cлокали, вам не следует использовать диапазоны, подобные тем, что [a-z]они зависят от локали и не всегда дают ожидаемые результаты. Помимо проблемы с регистром, с которой вы уже столкнулись, некоторые локали обрабатывают символы с диакритическими знаками (например, á ) так же, как и базовый символ (то есть a ).

Вместо этого используйте именованный класс символов:

echo B | grep '[[:lower:]]'

Это всегда даст правильный результат для локали. Тем не менее, вам нужно выбрать локаль, чтобы отразить смысл как входного текста, так и теста, который вы пытаетесь применить.

Например, если вам нужно найти конкретное значение байта, используйте Cлокаль, которая всегда доступна:

echo B | LANG=C grep '[a-z]'

Если это не работает, как ожидалось, это действительно ошибка.

Нил Мэйхью
источник
Я знаю, что это не то, что я спросил. Я специально спрашиваю о том, что означает явный диапазон и почему разные дистрибутивы (даже с GNU libc и GNU grep) ведут себя по-разному. (Пропущено голосование, потому что, хотя то, что вы говорите, правильно, это не имеет значения.)
Жиль "ТАК - перестань быть злым"
1
Я хочу сказать, что значение явного диапазона зависит от локали, и разные системы не обязаны определять свои локали одинаково, поэтому это не ошибка. Технически, вы злоупотребляете системой, поэтому вы не должны удивляться получению «неопределенного» поведения. Кроме того, несколько человек отметили, что они не могут воспроизвести поведение в своих системах Debian, поэтому, похоже, что-то необычное в ваших системах.
Нил Мэйхью
1
Я знаю, что поведение диапазонов зависит от локали. Я спрашиваю, как и удивляюсь тому, что разные системы, использующие Glibc (и, оказывается, даже разные установки одного и того же выпуска Debian), ведут себя по-разному. Я добавил вывод locale -kсвоего вопроса; он идентичен на двух машинах Debian, одна из которых Bнаходится в диапазоне, а другая - нет. Кстати, я не root ни на одной машине (так что это не что-то особенное, что я делаю как администратор).
Жиль "ТАК - перестань быть злым"
echo "Baü" | LC_COLLATE=C grep -o '[[:lower:]]'возвращает aИ üпока echo "Baü" | LC_COLLATE=C grep -o '[a-z]'возвращает только a. На мой взгляд, «нижний» не совсем то, чего хотел ОП
Даниэль Олдер
Моя первоначальная точка зрения остается неизменной: не используйте диапазоны, если вы не находитесь в Cлокали. Я считаю, что это имеет отношение к ФП, который хотел сообщить об ошибке. Если вы не в Cлокали, результаты использования диапазонов крайне непредсказуемы и поэтому никогда не могут считаться ошибкой. С другой стороны, если вам нужно найти конкретное значение байта, просто используйте Cлокаль. Моя вторичная точка зрения заключалась в том, что если вы действительно хотите искать строчные буквы в локали, используйте класс символов. Хотя ОП может и не искали этого, другие могут, если найдут этот вопрос.
Нейл Мэйхью
1

Диапазоны в регулярных выражениях должны соответствовать параметрам сортировки. Вот соответствующий стандарт: http://pubs.opengroup.org/onlinepubs/007908799/xbd/re.html (ищите «выражения диапазона»). Поэтому echo B | LC_COLLATE=en_US grep '[a-z]'следует выводить Bданные с учетом разумного определения соответствующей локали. Я не могу объяснить, почему это иногда не работает для вас, но я был бы очень удивлен, если бы столкнулся с этим в не древней системе, которая должным образом установлена ​​и настроена.

Питер Айзентраут
источник
1
echo B | LC_COLLATE=en_US.utf8 grep '[a-z]' Ничего не печатает в Ubuntu 12.04 с помощью grep 2.10. На Centos 6.5 ничего не печатается с помощью grep 2.6.3. Работает ли в Debian 6.0.8 с grep 2.6.3.
Ян Д. Аллен