Чи має (чи) 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 або віджимають, 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_COLLATEє

collate-nrules=4
collate-rulesets=""
collate-symb-hash-sizemb=2039
collate-codeset="UTF-8"

1
Не відтворюється на екземплярі Debian Lenny, що мені було в нагоді. Не перевіряв, чи en_USгенерується він.
alex

1
@alex Якщо локальний код не генерується, то Cмова використовується як резервна копія, а порядок її порівняння є прямими байтовими значеннями, тому Bїх не буде відповідати. Тест у локалі, який відображається у висновку locale -a.
Жил 'SO- перестань бути злим'

1
Зауважте, що en_US НЕ такий же, як en_US.utf8, і зазвичай означає en_US.iso-8859-1, залежно від того, що саме ви встановили. Якщо en_US (без суфікса) не відображається у висновку locale - у вас фактично немає цієї локалі. Що показує LC_COLLATE = en_US locale -k LC_COLLATE?
Ніл Мейхев

1
З цього часу тут з'явилося практичне, а не теоретичне запитання: Чому великі літери включені до ряду малих букв у підсумковому звороті?
Калеб

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він знаходиться в діапазоні, і той, де його немає. До речі, я не корінь на жодній машині (тому я не є чимось особливим, що я роблю як адміністратор).
Жил 'SO- перестань бути злим'

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