Різниця між [0-9], [[: цифра:]] та \ d


35

У статті Вікіпедії про регулярні вирази здається, що [[:digit:]]= [0-9]= \d.

Які обставини, коли вони не дорівнюють? Яка різниця?

Після деяких досліджень, я думаю, що одна відмінність полягає в тому, що експресія дужки [:expr:]залежить від локальності.


3
Чи не є стаття у Вікіпедії, яку ви пов’язали, щоб відповісти на своє запитання? Різні процесори / двигуни регулярного вираження підтримують різні синтаксиси для класів символів (серед іншого).
igal

@igal wiki каже, що є різниця, але не дає багато деталей. Я запитую деталі, щось на зразок isaac, сказав триг. Мене дуже цікавить їхня різниця в grep, sed, awk ... версія GNU чи ні.
Харбін

Відповіді:


40

Так, це [[:digit:]]~ [0-9]~ \d(де ~ означає приблизно).
У більшості мов програмування (де він підтримується) \d[[:digit:]](однаковий).
Це \dрідше, ніж [[:digit:]](не в POSIX, але воно є в GNU grep -P).

У UNICODE є багато цифр , наприклад:

123456789 # Hindu-Arabic Арабські цифри
٠١٢٣٤٥٦٧٨٩ # ARABIC-INDIC
۰۱۲۳۴۵۶۷۸۹ # EXTENDED ARABIC-INDIC/PERSIAN
߀߁߂߃߄߅߆߇߈߉ # NKO DIGIT
०१२३४५६७८९ # DEVANAGARI

Все це може бути включено до [[:digit:]]або \d.

Натомість, [0-9]це лише цифри ASCII 0123456789.


Є багато мов: Perl, Java, Python, C. У яких [[:digit:]]\d) вимагається розширене значення. Наприклад, цей код perl буде відповідати всім цифрам зверху:

$ a='0123456789 ٠١٢٣٤٥٦٧٨٩ ۰۱۲۳۴۵۶۷۸۹ ߀߁߂߃߄߅߆߇߈߉ ०१२३४५६७८९'

$ echo "$a" | perl -C -pe 's/[^\d]//g;' ; echo
0123456789٠١٢٣٤٥٦٧٨٩۰۱۲۳۴۵۶۷۸۹߀߁߂߃߄߅߆߇߈߉०१२३४५६७८९

Що еквівалентно вибору всіх символів, які мають властивості Unicode Numericта digits:

$ echo "$a" | perl -C -pe 's/[^\p{Nd}]//g;' ; echo
0123456789٠١٢٣٤٥٦٧٨٩۰۱۲۳۴۵۶۷۸۹߀߁߂߃߄߅߆߇߈߉०१२३४५६७८९

Який греп може відтворити (конкретна версія pcre може мати різний внутрішній список числових точок коду, ніж Perl):

$ echo "$a" | grep -oP '\p{Nd}+'
0123456789
٠١٢٣٤٥٦٧٨٩
۰۱۲۳۴۵۶۷۸۹
߀߁߂߃߄߅߆߇߈߉
०१२३४५६७८९

Змініть його на [0-9], щоб побачити:

$ echo "$a" | grep -o '[0-9]\+'
0123456789

POSIX

Для конкретного POSIX BRE або ERE:
The \dне підтримується (не в POSIX, але знаходиться в GNU grep -P). [[:digit:]]вимагає POSIX, щоб відповідати розрядному класу символів, який, в свою чергу, вимагається ISO C, щоб бути символами від 0 до 9 і більше нічого. Так тільки в C локалі все [0-9], [0123456789], \dі [[:digit:]]означають одне і те ж. [0123456789]Не має можливих пересудів, [[:digit:]]доступний в декількох утиліт , і це часто означає тільки [0123456789]. The \dпідтримується декількома утилітами.

Що стосується [0-9], значення виразів діапазону визначається лише POSIX у мові C; в інших місцевостях це може бути інакше (це може бути порядок кодової точки чи порядок порівняння чи щось інше).

снарядів

Деякі реалізації можуть розуміти, що діапазон є чимось іншим, ніж звичайний порядок ASCII (наприклад, ksh93):

$ LC_ALL=en_US.utf8 ksh -c 'a="'"$a"'";echo "${a//[0-9]}"'
  ۹ ߀߁߂߃߄߅߆߇߈߉ ९

І це впевнене джерело помилок, які чекають цього.


На практиці в системах POSIX iswctype()та BRE / ERE / подстановочні знаки в утилітах POSIX, [0-9] та [[: цифра:]] відповідають лише 0123456789. І це буде чітко викладено в наступній редакції стандарту
Стефан Шазелас

Я був не в курсі , що perl«S \dв режимі Unicode узгодженого на десяткових цифр від інших сценаріїв. Дякую за це. З PCRE див. (*UCP)Як у GNU grep -Po '(*UCP)\d'або grep -Po '(*UCP)[[:digit:]]для класів, що базуються на властивостях Unicode.
Стефан Шазелас

Я погоджуюся, що [:digit:]синтаксис підказує, що ви хочете використовувати локалізацію, тобто те, що користувач вважає цифрою. Я ніколи не використовую, [:digit:]тому що на практиці це те саме, що [0-9]і в будь-якому випадку, незмінно я хочу відповідати 0123456789, я ніколи не збираюся відповідати ٠١٢٣٤٥٦٧٨٩, і я не можу придумати випадок використання, коли б хто хотів відповідати десятковій цифрі в будь-якому сценарії з утилітами POSIX. Дивіться також нинішню дискусію про [:blank:]zsh ML . Ці класи персонажів трохи заплутані.
Стефан Шазелас

13

Це залежить від того, як ви визначаєте цифру; [0-9]має тенденцію бути лише ASCII (або, можливо, чимось іншим, що не є ні ASCII, ні суперкомплектом ASCII, але тими ж 10 цифрами, що і в ASCII, лише з різними представленнями бітів (EBCDIC)); \dз іншого боку, це можуть бути просто прості цифри (старі версії Perl, або сучасні версії Perl з /aувімкненим прапором регулярного вираження), або це може бути збіг Unicode, \p{Digit}який є більш великим набором цифр, ніж [0-9]або /\d/aзбігається.

$ perl -E 'say "match" if 42 =~ m/\d/'
match
$ perl -E 'say "match" if "\N{U+09EA}" =~ m/\d/'
match
$ perl -E 'say "match" if "\N{U+09EA}" =~ m/\d/a'
$ perl -E 'say "match" if "\N{U+09EA}" =~ m/[0-9]/'
$ 

perldoc perlrecharclass для отримання додаткової інформації або перегляньте документацію відповідної мови, щоб побачити, як вона поводиться.

Але зачекай, є ще більше! Локал може також змінюватись, що \dвідповідає, тому \dможе відповідати меншій кількості цифр, ніж повний набір таких Unicode, і (сподіваємось, зазвичай) також включає [0-9]. Це схоже на різницю C між isdigit(3)( [0-9]) та isnumber(3)( [0-9плюс усе, що ще є з локалі).

Можуть бути здійснені дзвінки, щоб отримати значення цифри, навіть якщо це не так [0-9]:

$ perl -MUnicode::UCD=num -E 'say num(4)'
4
$ perl -MUnicode::UCD=num -E 'say num("\N{U+09EA}")'
4
$ 

Я думаю isnumber(), що це BSD-річ, принаймні на основі чоловічої сторінки, здається, так
ilkkachu

У мене є щось з упередженням BSD, так
thrig

Прапор / a є специфічним обмежувачем для зменшення списку цифр Unicode, щоб відповідати лише … / / модифікатор можна використовувати, щоб примусити \ d відповідати лише ASCII 0 - 9 . Як такий, він змушує співставляти абсолютно те саме і єдине [0-9].
Ісаак

5

Різне значення з [0-9], [[:digit:]]і \dпредставлені в інших відповідях. Тут я хотів би додати відмінності у впровадженні двигуна regex.

            [[:digit:]]    \d
grep -E               ✓     ×
grep -P               ✓     ✓
sed                   ✓     ×
sed -E                ✓     ×

Так [[:digit:]]завжди працює , \dзалежить. У посібнику grep згадується, що [[:digit:]]якраз 0-9у Cлокалі.

PS1: Якщо ви знаєте більше, будь ласка, розгорніть таблицю.

PS2: GNU grep 3.1 та GNU 4.4 використовується для тесту.


2
1) Існує багато версій grepі sed, мабуть, найбільша різниця між версіями GNU та іншими. Ця відповідь може бути кориснішою, якщо в ній згадується, до якої версії grepі до sedякої посилається. Або яке джерело цієї таблиці для цього питання. 2) ця таблиця також може бути переписана в текст, оскільки вона не містить нічого, що вимагає, щоб це було зображення
ilkkachu

@ilkkachu 1) остання версія GNU 3.1 та GNU 4.4 використовується для тестування. 2) Я не знаю, як створити таблицю. Здається, @ muru перетворив таблицю в гарну текстову форму.
Харбін

@harbinn Будь ласка, відредагуйте це у своїй відповіді.
Дан Д.

@DanD. інформація про версію додана. thx для уваги
harbinn

1
Зауважте, що вбудований reмодуль python не підтримує [[: digit:]], але додавання в бібліотеці regexпідтримує його, тому я б трохи похитнувся, коли завжди працює. Він завжди працює в позиційних скаргах.
Стів Барнс

4

Теоретичні відмінності вже досить добре пояснені в інших відповідях, тому залишається пояснити практичні відмінності.

Ось кілька найпоширеніших випадків використання для відповідності цифри:


Вилучення даних з одного разу

Часто, коли ви хочете стиснути деякі числа, самі цифри знаходяться в незграбно відформатованому текстовому файлі. Ви хочете витягнути їх для використання у вашій програмі. Ви, ймовірно, можете сказати формат чисел (подивившись на файл) та поточний локал, тож нормально використовувати будь-яку форму , доки вона не виконає роботу. \dвимагає найменших натискань клавіш, тому це дуже часто використовується.

Санітація введення

У вас є ненадійний ввід користувача (можливо, з веб-форми), і вам потрібно переконатися, що він не містить сюрпризів. Можливо, ви хочете зберегти його в числовому полі в базі даних або використовувати як параметр команди оболонки для запуску на сервері. У цьому випадку ви дуже хочете [0-9], оскільки це найбільш обмежуючий і передбачуваний.

Перевірка даних

У вас є небагато даних, які ви не збираєтесь використовувати для нічого "небезпечного", але було б добре знати, чи є це число. Наприклад, ваша програма дозволяє користувачеві вводити адресу, і ви хочете виділити можливий друкарський помилок, якщо введення не містить домашнього номера. У цьому випадку ви, мабуть, хочете бути якомога ширшими, тому [[:digit:]]й шлях.


Це, здається, є трьома найпоширенішими випадками використання для відповідності цифр. Якщо ви думаєте, що я пропустив важливе, будь ласка, залиште коментар.


хороша робота, проблема безпеки , пов'язані, наприклад, Redos або інші
FRAMS
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.