Разница между [0-9], [[: digit:]] и \ d


35

В статье в Википедии о регулярных выражениях кажется, что [[:digit:]]= [0-9]= \d.

Каковы обстоятельства, когда они не равны? В чем разница?

После некоторого исследования, я думаю, одно отличие состоит в том, что выражение в скобках [:expr:]зависит от локали.


3
Разве статья Википедии, на которую вы ссылались, не ответила на ваш вопрос? Различные процессоры / механизмы регулярных выражений поддерживают разные синтаксисы для символьных классов (среди прочего).
Игаль

@igal wiki говорит, что есть разница, но не дает подробностей. Я спрашиваю детали, что-то вроде Исаака, сказал Триг. Я довольно заинтересован в их разнице в grep, sed, awk ... в версии GNU или нет.
Harbinn

Ответы:


40

Да, это [[:digit:]]~ [0-9]~ \d(где ~ означает приблизительно).
В большинстве языков программирования (где это поддерживается) \d[[:digit:]](идентично). Встречается реже , чем (не в POSIX , но это в GNU ).
\d[[:digit:]]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٠١٢٣٤٥٦٧٨٩۰۱۲۳۴۵۶۷۸۹߀߁߂߃߄߅߆߇߈߉०१२३४५६७८९

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

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

Измените его на [0-9], чтобы увидеть:

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

POSIX

Для конкретного POSIX BRE или ERE: не поддерживается (не в POSIX , но в GNU ). POSIX требуется для соответствия классу цифр, который, в свою очередь, требуется ISO C для символов от 0 до 9 и ничего больше. Так только в C локали все , , и означают одно и то же. Он не имеет возможных неправильных толкований, доступен в большем количестве утилит, и это обычно означает только . Поддерживается несколькими утилитами.
\dgrep -P[[:digit:]][0-9][0123456789]\d[[:digit:]][0123456789][[:digit:]][0123456789]\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] и [[: digit:]] соответствуют только 0123456789. И это будет четко указано в следующей редакции стандарта
Стефан Шазелас

Я был не в курсе , что perl«S \dв режиме Unicode согласованного на десятичных цифр от других сценариев. Спасибо за это. С PCRE, смотрите (*UCP)как в GNU grep -Po '(*UCP)\d'или grep -Po '(*UCP)[[:digit:]]для классов, основанных на свойствах Юникода.
Стефан Шазелас

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

13

Это зависит от того, как вы определяете цифру; [0-9]имеет тенденцию быть только ASCII (или, возможно, чем-то еще, что не является ни ASCII, ни надмножеством ASCII, но теми же 10 цифрами, что и в ASCII, только с различными битовыми представлениями (EBCDIC)); \dс другой стороны, это могут быть либо простые цифры (старые версии Perl, либо современные версии Perl с /aвключенным флагом регулярного выражения), либо совпадение в Юникоде, \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, по крайней мере, на основе man-страницы, это кажется так
ilkkachu

У меня есть кое-что о предвзятости BSD, да
три

Флаг / a - это специальный ограничитель, который сокращает список цифр Unicode до соответствия ... модификатор / a может использоваться для принудительного совпадения \ d только с ASCII от 0 до 9 . Как таковой, он заставляет совпадать точно так же и только [0-9].
Исаак

5

Различное значение из [0-9], [[:digit:]]и \dпредставлены в других ответах. Здесь я хотел бы добавить различия в реализации движка регулярных выражений.

            [[: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 grep 3.1 и GNU 4.4 используются для тестирования. 2) Я не знаю, как создать таблицу. Кажется, @ muru преобразовал таблицу в симпатичную текстовую форму.
Harbinn

@harbinn Пожалуйста, отредактируйте это в своем ответе.
Дэн Д.

@DanD. Информация о версии добавлена. спасибо за внимание
Harbinn

1
Обратите внимание, что встроенный reмодуль python не поддерживает [[: digit:]], но библиотека add in regexподдерживает его, поэтому я бы немного позаботился о том, что всегда работает. Это всегда работает в положительных ситуациях жалоб.
Стив Барнс

4

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

Вот некоторые из наиболее распространенных вариантов использования для сопоставления цифры:


Однократное извлечение данных

Часто, когда вы хотите сжать некоторые числа, сами числа находятся в неуклюже отформатированном текстовом файле. Вы хотите извлечь их для использования в вашей программе. Вы, вероятно, можете указать формат чисел (просматривая файл) и текущую локаль, поэтому можно использовать любую из форм , если она выполняет свою работу. \dтребует наименьшего количества нажатий клавиш, поэтому он очень часто используется.

Вход дезинфекции

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

Проверка достоверности данных

У вас есть немного данных, которые вы не собираетесь использовать для чего-то «опасного», но было бы неплохо узнать, является ли это число. Например, ваша программа позволяет пользователю вводить адрес, и вы хотите выделить возможную опечатку, если ввод не содержит номера дома. В этом случае вы, вероятно, хотите быть как можно более широким, так [[:digit:]]что это путь.


Казалось бы, это три наиболее распространенных варианта использования для сопоставления цифр. Если вы думаете, что я пропустил важный, пожалуйста, оставьте комментарий.


Хорошая работа, связана ли проблема безопасности, например, с ReDoS или другими
кадры
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.