Совпадение чисел с регулярными выражениями - только цифры и запятые - PullRequest
51 голосов
/ 22 ноября 2010

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

123,456,789
-12,34
1234
-8

Не могли бы вы мне помочь?

Ответы [ 10 ]

473 голосов
/ 22 ноября 2010

Что такое число?

У меня есть простой вопрос для вашего «простого» вопроса: что именно вы подразумеваете под «числом»?

  • Является ли −0 число?
  • Как вы относитесь к √−1?
  • Является ли или числом?
  • Является ли 186,282.42±0.02 милями в секунду одним числом - или их два или три?
  • Является ли 6.02e23 числом?
  • Является ли 3.141_592_653_589 числом?Как насчет π или −2π⁻³ ͥ?
  • Сколько чисел в 0.083̄?
  • Сколько чисел в 128.0.0.1?
  • Какое число содержит ?Как насчет ⚂⚃?
  • Есть ли в 10,5 mm один номер или два?
  • Является ли ∛8³ числом или их три?
  • Какое число представляет ↀↀⅮⅭⅭⅬⅫ AUC, 2762 или 2009?
  • Являются ли числа ४५६७ и ৭৮৯৮?
  • А как насчет 0377, 0xDEADBEEF и 0b111101101?
  • Является ли Inf числом?NaN?
  • Является ли ④② числом?Как насчет ?
  • Как вы относитесь к ?
  • Какое отношение ℵ₀ и ℵ₁ имеет отношение к числам?Или , и ?

Предлагаемые шаблоны

Кроме того, вам знакомы эти шаблоны?Можете ли вы объяснить плюсы и минусы каждого из них?

  1. /\D/
  2. /^\d+$/
  3. /^\p{Nd}+$/
  4. /^\pN+$/
  5. /^\p{Numeric_Value:10}$/
  6. /^\P{Numeric_Value:NaN}+$/
  7. /^-?\d+$/
  8. /^[+-]?\d+$/
  9. /^-?\d+\.?\d*$/
  10. /^-?(?:\d+(?:\.\d*)?|\.\d+)$/
  11. /^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/
  12. /^((\d)(?(?=(\d))|$)(?(?{ord$3==1+ord$2})(?1)|$))$/
  13. /^(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2})[.](?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9]{1,2}))$/
  14. /^(?:(?:[0-9a-fA-F]{1,2}):(?:[0-9a-fA-F]{1,2}):(?:[0-9a-fA-F]{1,2}):(?:[0-9a-fA-F]{1,2}):(?:[0-9a-fA-F]{1,2}):(?:[0-9a-fA-F]{1,2}))$/
  15. /^(?:(?:[+-]?)(?:[0123456789]+))$/
  16. /(([+-]?)([0123456789]{1,3}(?:,?[0123456789]{3})*))/
  17. /^(?:(?:[+-]?)(?:[0123456789]{1,3}(?:,?[0123456789]{3})*))$/
  18. /^(?:(?i)(?:[+-]?)(?:(?=[0123456789]|[.])(?:[0123456789]*)(?:(?:[.])(?:[0123456789]{0,}))?)(?:(?:[E])(?:(?:[+-]?)(?:[0123456789]+))|))$/
  19. /^(?:(?i)(?:[+-]?)(?:(?=[01]|[.])(?:[01]{1,3}(?:(?:[,])[01]{3})*)(?:(?:[.])(?:[01]{0,}))?)(?:(?:[E])(?:(?:[+-]?)(?:[01]+))|))$/
  20. /^(?:(?i)(?:[+-]?)(?:(?=[0123456789ABCDEF]|[.])(?:[0123456789ABCDEF]{1,3}(?:(?:[,])[0123456789ABCDEF]{3})*)(?:(?:[.])(?:[0123456789ABCDEF]{0,}))?)(?:(?:[G])(?:(?:[+-]?)(?:[0123456789ABCDEF]+))|))$/
  21. /((?i)([+-]?)((?=[0123456789]|[.])([0123456789]{1,3}(?:(?:[_,]?)[0123456789]{3})*)(?:([.])([0123456789]{0,}))?)(?:([E])(([+-]?)([0123456789]+))|))/

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

Как видите, существует огромное количество числовых возможностей: вполне вероятно, на самом деле их стоит.11

Ключ к предлагаемым шаблонам

Каждое приведенное ниже нумерованное объяснение описывает шаблон соответствующего нумерованного шаблона, указанного выше.

  1. Соответствует, если есть какие-либо не цифры в любом месте строки, включая пробелы, такие как разрывы строк.
  2. Соответствует только в том случае, если строка не содержит ничего, кроме цифр, с возможным исключением разрывов задней строки.Обратите внимание, что цифра определяется как имеющая свойство «Общая категория» Десятичное число, которое доступно как \p{Nd}, \p{Decimal_Number} или \p{General_Category=Decimal_Number}.Этот ход на самом деле является просто отражением тех кодовых точек, чья категория числового типа является десятичной, которая доступна как \p{Numeric_Type=Decimal}.
  3. Это то же самое, что 2 в большинстве языков регулярных выражений.Java здесь исключение, потому что она не отображает простые экранированные символы, такие как \w и \W, \d и \D, \s и \S и \b или \B всоответствующее свойство Unicode.Это означает, что вы не должны использовать ни один из этих восьми односимвольных escape-символов для любых данных Unicode в Java, потому что они работают только на ASCII, даже если Java всегда использует символы Unicode внутри.
  4. Это немного отличается от 3 в этомон не ограничен десятичными числами, но может быть любым числом вообще;то есть любой символ со свойством \pN, \p{Number} или \p{General_Category=Number}.К ним относятся \p{Nl} или \p{Letter_Number} для таких вещей, как римские цифры и \p{No} или \p{Other_Number} для подписанных и подписанных чисел, дробей и чисел в кружках - среди прочих, таких как счетные стержни.
  5. Это соответствует толькоэти строки полностью состоят из чисел, десятичное значение которых равно 10, например, римская цифра десять и , , , , , и .
  6. Только те строки, которые содержат символы без числового значения NaN;другими словами, все символы должны иметь числовые значения.
  7. Соответствует только десятичным числам,опционально с ведущим HYPHEN MINUS.
  8. То же, что 7, но теперь также работает, если знак плюс вместо минуса.
  9. Поиск десятичных чисел с дополнительным HYPHEN MINUS и дополнительным ПОЛНЫМ ОСТАНОВОМ плюс ноль или более десятичных чисел после.
  10. То же, что и 9, но не требует цифр перед точкой, если она есть после.
  11. Стандартное обозначение с плавающей точкой для C и многих других языков, допускающее научное обозначение.
  12. Находит числа, состоящие только из двух или более десятичных знаков любого сценария в порядке убывания, например, 987 или 54321. Это рекурсивное регулярное выражение включает в себя обратный вызов к коду Perl, который проверяет, имеет ли цифра прогнозного значения значение кодовой точки, которое является преемником текущая цифра; то есть его порядковая стоимость на единицу больше. Это можно сделать в PCRE, используя функцию C в качестве выноски.
  13. Это ищет действительный адрес IPv4 с четырьмя десятичными числами в допустимом диапазоне, например 128.0.0.1 или 255.255.255.240, но не 999.999.999.999.
  14. Здесь ищется действительный MAC-адрес, поэтому шесть пар двоеточия разделяют две шестнадцатеричные цифры ASCII.
  15. Это ищет целые числа в диапазоне ASCII с необязательным начальным знаком. Это нормальный шаблон для сопоставления целых чисел ASCII.
  16. Это похоже на 15, за исключением того, что для разделения групп по три требуется запятая.
  17. Это похоже на 15, за исключением того, что запятая для разделения групп теперь является необязательной.
  18. Это обычный шаблон для сопоставления чисел с плавающей точкой в ​​стиле C в ASCII.
  19. Это похоже на 18, но для разделения групп по 3 и в базе-2 требуется запятая, а не в базе-10.
  20. Это похоже на 19, но в гексе. Обратите внимание, что дополнительный показатель теперь обозначается как G вместо E, так как E является действительной шестнадцатеричной цифрой.
  21. Это проверяет, что строка содержит число с плавающей точкой в ​​стиле C, но с необязательным разделителем группировки каждые три цифры запятой или подчеркивания (LOW LINE) между ними. Он также сохраняет эту строку в группе захвата \1, делая ее доступной как $1 после успешного совпадения.

Источники и ремонтопригодность

Образцы с номерами 1,2,7–11 взяты из предыдущего воплощения списка Perl Часто задаваемые вопросы в вопросе «Как проверить ввод?». Этот раздел был заменен предложением использовать модуль Regexp :: Common , написанный Abigail и Damian Conway . Оригинальные шаблоны все еще можно найти в рецепте 2.1 Perl Cookbook , «Проверка, является ли строка допустимым числом», решения которой можно найти для головокружительного числа разнообразных языки, включая ada, общий lisp, groovy, guile, haskell, java, merd, ocaml, php, pike, python, rexx, ruby ​​и tcl в проекте PLEAC .

Шаблон 12 может быть более разборчиво переписан

m{
    ^
    (
        ( \d )
        (?(?= ( \d ) ) | $ )
        (?(?{ ord $3 == 1 + ord $2 }) (?1) | $ )
    )
    $
}x

Используется regex recursion , который встречается во многих движках шаблонов, включая Perl и все языки, полученные из PCRE. Но он также использует встроенный код для проверки своего второго условного шаблона; насколько мне известно, кодовые выноски доступны только в Perl и PCRE.

Шаблоны 13–21 были получены из вышеупомянутого модуля Regexp :: Common. Обратите внимание, что для краткости все они написаны без пробелов и комментариев, которые вы определенно захотите в производственном коде. Вот как это может выглядеть в режиме /x:

$real_rx = qr{ (   # start $1 to hold entire pattern
    ( [+-]? )                  # optional leading sign, captured into $2
    (                          # start $3
        (?=                    # look ahead for what next char *will* be
            [0123456789]       #    EITHER:  an ASCII digit
          | [.]                #    OR ELSE: a dot
        )                      # end look ahead
        (                      # start $4
           [0123456789]{1,3}       # 1-3 ASCII digits to start the number
           (?:                     # then optionally followed by
               (?: [_,]? )         # an optional grouping separator of comma or underscore
               [0123456789]{3}     # followed by exactly three ASCII digits
           ) *                     # repeated any number of times
        )                          # end $4
        (?:                        # begin optional cluster
             ( [.] )               # required literal dot in $5
             ( [0123456789]{0,} )  # then optional ASCII digits in $6
        ) ?                        # end optional cluster
     )                         # end $3
    (?:                        # begin cluster group
        ( [E] )                #   base-10 exponent into $7
        (                      #   exponent number into $8
            ( [+-] ? )         #     optional sign for exponent into $9
            ( [0123456789] + ) #     one or more ASCII digits into $10
        )                      #   end $8
      |                        #   or else nothing at all
    )                          # end cluster group
) }xi;          # end $1 and whole pattern, enabling /x and /i modes

С точки зрения разработки программного обеспечения, все еще есть несколько проблем со стилем, используемым в версии режима /x, показанной выше. Во-первых, много повторений кода, где вы видите одно и то же [0123456789]; что произойдет, если одна из этих последовательностей случайно пропустит цифру? Во-вторых, вы полагаетесь на позиционные параметры, которые вы должны учитывать. Это означает, что вы можете написать что-то вроде:

(
  $real_number,          # $1
  $real_number_sign,     # $2
  $pre_exponent_part,    # $3
  $pre_decimal_point,    # $4
  $decimal_point,        # $5
  $post_decimal_point,   # $6
  $exponent_indicator,   # $7
  $exponent_number,      # $8
  $exponent_sign,        # $9
  $exponent_digits,      # $10
) = ($string =~ /$real_rx/);

, которыйоткровенно отвратителен!Легко ошибиться в нумерации, трудно вспомнить, куда идут символические имена, и писать утомительно, особенно если вам не нужны все эти фрагменты.Переписать это, чтобы использовать именованные группы вместо просто пронумерованных.Опять же, я буду использовать синтаксис Perl для переменных, но содержимое шаблона должно работать везде, где поддерживаются именованные группы.

use 5.010;              # Perl got named patterns in 5.10
$real_rx = qr{
  (?<real_number>
    # optional leading sign
    (?<real_number_sign> [+-]? )
    (?<pre_exponent_part>
        (?=                         # look ahead for what next char *will* be
            [0123456789]            #    EITHER:  an ASCII digit
          | [.]                     #    OR ELSE: a dot
        )                           # end look ahead
        (?<pre_decimal_point>
            [0123456789]{1,3}       # 1-3 ASCII digits to start the number
            (?:                     # then optionally followed by
                (?: [_,]? )         # an optional grouping separator of comma or underscore
                [0123456789]{3}     # followed by exactly three ASCII digits
            ) *                     # repeated any number of times
         )                          # end <pre_decimal_part>
         (?:                        # begin optional anon cluster
            (?<decimal_point> [.] ) # required literal dot
            (?<post_decimal_point>
                [0123456789]{0,}  )
         ) ?                        # end optional anon cluster
   )                                # end <pre_exponent_part>
   # begin anon cluster group:
   (?:
       (?<exponent_indicator> [E] ) #   base-10 exponent
       (?<exponent_number>          #   exponent number
           (?<exponent_sign>   [+-] ?         )
           (?<exponent_digits> [0123456789] + )
       )                      #   end <exponent_number>
     |                        #   or else nothing at all
   )                          # end anon cluster group
 )                            # end <real_number>
}xi;

Теперь названы абстракции, что помогает.Вы можете вывести группы по именам, и вам нужны только те, которые вам небезразличны.Например:

if ($string =~ /$real_rx/) {
    ($pre_exponent, $exponent_number) =
        @+{ qw< pre_exponent exponent_number > };
}

Есть еще одна вещь, чтобы сделать этот шаблон, чтобы сделать его еще более обслуживаемым.Проблема в том, что повторений еще слишком много, что означает, что его слишком легко изменить в одном месте, но не в другом.Если бы вы проводили анализ МакКейба, вы бы сказали, что его показатель сложности слишком высок.Большинство из нас просто скажут, что это слишком отступ.Это мешает следовать.Чтобы исправить все эти вещи, нам нужен «грамматический шаблон», в котором есть блок определения для создания именованных абстракций, который мы потом несколько позже рассматриваем как вызов подпрограммы в матче.

use 5.010;              # Perl first got regex subs in v5.10
$real__rx = qr{ 

    ^                   # anchor to front
    (?&real_number)     # call &real_number regex sub
    $                   # either at end or before final newline

  ##################################################
  # the rest is definition only; think of         ##
  # each named buffer as declaring a subroutine   ##
  # by that name                                  ##
  ##################################################
  (?(DEFINE)
      (?<real_number>
          (?&mantissa)
          (?&abscissa) ?

      )
      (?<abscissa>
          (?&exponent_indicator)
          (?&exponent)
      )
      (?<exponent>
          (&?sign)    ?
          (?&a_digit) +
      )
      (?<mantissa>
         # expecting either of these....
         (?= (?&a_digit)
           | (?&point)
         )
         (?&a_digit) {1,3}
         (?: (?&digit_separator) ?
             (?&a_digit) {3}
         ) *
         (?: (?&point)
             (?&a_digit) *
         ) ?
      )
      (?<point>               [.]     )
      (?<sign>                [+-]    )
      (?<digit_separator>     [_,]    )
      (?<exponent_indicator>  [Ee]    )
      (?<a_digit>             [0-9]   )
   ) # end DEFINE block
}x;

Посмотрите, как безумно лучше грамматический рисунок, чем исходный шаблон с шумом линии?Также намного проще получить правильный синтаксис: я набрал его без единой синтаксической ошибки регулярного выражения, которую нужно было исправить.(Хорошо, я набрал все остальные без синтаксических ошибок, но я делал это некоторое время.:)

Грамматические шаблоны выглядят гораздо больше как BNF, чем старые уродливые регулярные выражения, которыелюди пришли ненавидеть.Их гораздо проще читать, писать и поддерживать.Итак, давайте не будем больше мерзких узоров, хорошо?

37 голосов
/ 22 ноября 2010

Если вы хотите разрешить только цифры и запятые, ^[-,0-9]+$ - это ваше регулярное выражение. Если вы также хотите разрешить пробелы, используйте ^[-,0-9 ]+$.

Однако, если вы хотите разрешить правильные числа, лучше используйте что-то вроде этого:

^([-+] ?)?[0-9]+(,[0-9]+)?$

или просто используйте синтаксический анализатор номеров .net (для различных стилей номеров см. MSDN ):

try {
    double.Parse(yourString, NumberStyle.Number);
}
catch(FormatException ex) {
    /* Number is not in an accepted format */
}
9 голосов
/ 22 ноября 2010

Попробуйте это:

^-?\d{1,3}(,\d{3})*(\.\d\d)?$|^\.\d\d$

Позволяет:

1
12
.99
12.34 
-18.34
12,345.67
999,999,999,999,999.99
6 голосов
/ 24 июня 2014

Поскольку этот вопрос был вновь открыт четыре года спустя, я хотел бы предложить другой вариант.Поскольку кто-то тратит много времени на работу с регулярными выражениями, мое мнение таково:

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

Если это вообще возможно, используйте свой язык.Могут быть функции, которые помогут вам определить, является ли значение, содержащееся в строке, действительным числом.При этом, если вы принимаете различные форматы (запятые и т. Д.), У вас может не быть выбора.

B.Не пишите регулярное выражение вручную для проверки диапазона номеров

  • Трудно написать регулярное выражение для сопоставления с числом в данном диапазоне.Вы можете ошибиться, даже написав регулярное выражение в , соответствующее числу от 1 до 10 .
  • Если у вас есть регулярное выражение для диапазона номеров, его сложно отладить.Во-первых, смотреть на это ужасно.Во-вторых, как вы можете быть уверены, что соответствует всем желаемым значениям, но не соответствует ни одному из значений, которые вам не нужны? Честно говоря, если вы один, без сверстников, смотрящих через плечо, вы можете«т.Лучший метод отладки - это программный вывод целого диапазона чисел и сравнение их с регулярным выражением.
  • К счастью, есть инструменты для автоматического создания регулярного выражения для диапазона чисел.

C.Расходуйте Regex Energy с умом: используйте инструменты

  • Совпадение чисел в заданном диапазоне - это проблема, которая была решена.Вам не нужно пытаться изобретать велосипед.Это проблема, которая может быть решена программно механическим способом, гарантирующим отсутствие ошибок.Воспользуйтесь этой бесплатной поездкой.
  • Решение регулярного выражения с числовым диапазоном может быть интересно в учебных целях пару раз.Кроме того, если у вас есть энергия, чтобы инвестировать в развитие своих навыков регулярных выражений, потратьте их на что-то полезное, например, на углубление понимания жадности регулярных выражений , чтения регулярных выражений Unicode , игры ссовпадения или рекурсия нулевой ширины, прочитав SO FAQ по регулярным выражениям и обнаружив хитрые приемы, такие как, как исключить определенные шаблоны из совпадения регулярных выражений ... или читая классику, такую ​​как Регулярные выражения Matering, 3-е изд или Поваренная книга регулярных выражений, 2-е изд .

Для инструментов вы можете использовать:

  • Online: Regex_for_range
  • Офлайн: единственный, о котором я знаю, это RegexMagic (не бесплатно) от гуру регулярных выражений Яна Гойваэртса.Это его новаторский продукт для регулярных выражений, и, насколько я помню, у него есть широкий спектр возможностей для генерации чисел в заданном диапазоне, помимо других функций.
  • Если условия слишком сложные, автоматически сгенерировать два диапазона ... затем соединить их с оператором чередования |

D.Упражнение: построение регулярного выражения для спецификаций в вопросе

Эти спецификации довольно широки ... но не обязательно расплывчаты.Давайте снова посмотрим на примерные значения:

123,456,789
-12,34
1234
-8

Как соотносятся первые два значения?В первом запятая соответствует группе степеней по три.Во втором он, вероятно, соответствует десятичной запятой в числовом формате континентального европейского стиля.Это не означает, что мы должны разрешать использование цифр везде, как в 1,2,3,44.К тому же, мы не должны быть ограничительными.Например, регулярное выражение в принятом ответе не будет соответствовать одному из требований: 123,456,789 (см. demo ).

Как мы можем построить наше регулярное выражение в соответствии со спецификациями?

  • Давайте закрепим выражение между ^ и $, чтобы избежать подсовпадений
  • Давайте допустим дополнительный минус: -?
  • Давайте сопоставим два типа чиселпо обе стороны от чередования (?:this|that):
  • Слева цифра европейского стиля с необязательной запятой для десятичной части: [1-9][0-9]*(?:,[0-9]+)?
  • Справа число с разделителями тысяч: [1-9][0-9]{1,2}(?:,[0-9]{3})+

Полное регулярное выражение:

^-?(?:[1-9][0-9]*(?:,[0-9]+)?|[1-9][0-9]{1,2}(?:,[0-9]{3})+)$

См. demo .

Это регулярное выражение не допускает числа в европейском стиле, начинающиеся с 0, например 0,12.Это особенность, а не ошибка.Чтобы соответствовать им, небольшой твик подойдет:

^-?(?:(?:0|[1-9][0-9]*)(?:,[0-9]+)?|[1-9][0-9]{1,2}(?:,[0-9]{3})+)$

См. demo .

4 голосов
/ 22 ноября 2010

Попробуйте это:

^-?[\d\,]+$

Это позволит необязательный - в качестве первого символа, а затем любую комбинацию запятых и цифр.

3 голосов
/ 22 ноября 2010
^-?    # start of line, optional -
(\d+   # any number of digits
|(\d{1,3}(,\d{3})*))  # or digits followed by , and three digits
((,|\.)\d+)? # optional comma or period decimal point and more digits
$  # end of line
2 голосов
/ 28 июня 2014
^[-+]?(\d{1,3})(,?(?1))*$

Regular expression visualization

Демо Debuggex

Так что же это?!

  • ^ отмечает начало строки
  • [-+]? допускается минус или плюс сразу после начала строки
  • (\d{1,3}) соответствует не менее одной и не более трех ({1,3}) цифр (\d - обычно [0-9]) в строке и группирует их (скобки (...) строит группу) в качестве первой группы
  • (,?(?1))* хорошо ... давайте разберемся с этим
    • (...) строит другую группу ( не так важно )
    • ,? соответствует запятой (если она существует) сразу после первой последовательности цифр
    • (?1) снова соответствует шаблону первой группы (помните (\d{1,3})); в словах: в этот момент выражение соответствует знаку (плюс / минус / нет), за которым следует последовательность цифр, возможно, после которой следует запятая, за которой снова следует другая последовательность цифр.
    • (,?(?1))*, * повторяет вторую часть (запятая и последовательность) как можно чаще
  • $ наконец соответствует концу строки

Преимущество таких выражений состоит в том, чтобы избегать определения одного и того же шаблона в вашем выражении снова и снова и снова ... ну, иногда недостатком является сложность: - /

0 голосов
/ 10 октября 2015

Попробуйте это:

    boxValue = boxValue.replace(/[^0-9\.\,]/g, "");

Этот RegEx будет соответствовать только цифрам, точкам и запятым.

0 голосов
/ 25 июня 2014

Для примеров:

    ^(-)?([,0-9])+$

Это должно работать. Реализуйте его на любом языке, который вам нужен.

0 голосов
/ 04 марта 2014

В Java Вы можете использовать java.util.Scanner с его useLocale методом

Scanner myScanner =  new Scanner(input).useLocale( myLocale)

isADouble = myScanner.hasNextDouble()
...