Регулярное выражение для чисел с плавающей запятой

117

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

[-+]?[0-9]*\.?[0-9]*

Но он возвращает ошибку:

Invalid escape sequence (valid ones are  \b  \t  \n  \f  \r  \"  \'  \\ )

Насколько мне известно, нам нужно использовать escape-символ для .также. Пожалуйста, поправьте меня, где я ошибаюсь.

Гопал Самант
источник
10
На каком языке используется это регулярное выражение?
CaffGeek
3
@JDB - Почему вы тратите 100 очков за регулярное выражение типа number / float? Стандарт всегда был (?:\d+(?:\.\d*)?|\.\d+)и публикуется до бесконечности на SO ...
см. также stackoverflow.com/questions/638565/…
Джейсон С.
1
[-+]?([0-9]*[.])?[0-9]+([eE][-+]?\d+)?если вы тоже хотите уловить экспоненциальную запись, например, 3.023e-23
wcochran
В некоторых языках, таких как Java или C ++, обратная косая черта должна быть экранирована. Итак, чтобы получить регулярное выражение «\.», Вы должны использовать строку «\\.». Python обходит это, используя необработанные строки.
HackerBoss

Ответы:

260

TL; DR

Используйте [.]вместо \.и [0-9]вместо, \dчтобы избежать проблем на некоторых языках (например, Java).

Спасибо безымянному за то, что изначально это признал.

Один относительно простой шаблон для сопоставления числа с плавающей запятой:

[+-]?([0-9]*[.])?[0-9]+

Это будет соответствовать:

  • 123
  • 123.456
  • .456

Посмотреть рабочий пример

Если вы также хотите сопоставить 123.(точка без десятичной части), вам понадобится немного более длинное выражение:

[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)

См . Ответ Пкеллера для более полного объяснения этого шаблона.

Если вы хотите включить недесятичные числа, например шестнадцатеричные и восьмеричные, см. Мой ответ на вопрос Как определить, является ли строка числом? .

Если вы хотите подтвердить, что ввод является числом (а не находить число во вводе), тогда вы должны окружить шаблон ^и $, например:

^[+-]?([0-9]+([.][0-9]*)?|[.][0-9]+)$

Неправильные регулярные выражения

«Регулярные выражения», реализованные в большинстве современных языков, API, фреймворков, библиотек и т. Д., Основаны на концепции, разработанной в теории формального языка . Однако инженеры-программисты добавили множество расширений, которые выводят эти реализации далеко за рамки формального определения. Итак, хотя большинство обработчиков регулярных выражений похожи друг на друга, на самом деле стандарта нет. По этой причине многое зависит от того, какой язык, API, фреймворк или библиотеку вы используете.

(Между прочим, чтобы уменьшить путаницу, многие стали использовать « регулярное выражение » или « регулярное выражение » для описания этих расширенных языков сопоставления. Дополнительную информацию см. В разделе Является ли регулярное выражение таким же, как регулярное выражение? На сайте RexEgg.com.)

Тем не менее, большинство движков регулярных выражений (на самом деле, все они, насколько я знаю) будут принимать \.. Скорее всего, возникла проблема с побегом.

Проблема с побегом

Некоторые языки имеют встроенную поддержку регулярных выражений, например JavaScript . Для тех языков, которые этого не делают, побег может быть проблемой.

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

// creates a single character string: "\"
String x = "\\";

Однако регулярные выражения также используют \символ для экранирования, поэтому, если вы хотите сопоставить буквальный \символ, вы должны экранировать его для механизма регулярных выражений, а затем снова экранировать его для Java:

// Creates a two-character string: "\\"
// When used as a regex pattern, will match a single character: "\"
String regexPattern = "\\\\";

В вашем случае вы, вероятно, не избежали символа обратной косой черты на языке, на котором вы программируете:

// will most likely result in an "Illegal escape character" error
String wrongPattern = "\.";
// will result in the string "\."
String correctPattern = "\\.";

Все эти побеги могут сильно сбить с толку. Если язык, с которым вы работаете, поддерживает необработанные строки , вы должны использовать их, чтобы сократить количество обратных косых черт, но не все языки поддерживают (в первую очередь: Java). К счастью, в некоторых случаях есть альтернатива:

String correctPattern = "[.]";

Для механизма регулярных выражений \.и [.]означает одно и то же. Обратите внимание, что это работает не во всех случаях, например, новая строка ( \\n), открытая квадратная скобка ( \\[) и обратная косая черта ( \\\\или [\\]).

Замечание о совпадении чисел

(Подсказка: это сложнее, чем вы думаете)

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

[-+]?

Сопоставьте необязательный -или+

[0-9]*

Соответствие 0 или более последовательных цифр

\.?

Сопоставьте необязательный .

[0-9]*

Соответствие 0 или более последовательных цифр

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

[0-9] знак равно \d

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

Теперь, если вы внимательно посмотрите на это, вы поймете, что каждая часть вашего узора необязательна . Этот шаблон может соответствовать строке длиной 0; строка, состоящая только из +или -; или, строка, состоящая только из .. Вероятно, это не то, что вы хотели.

Чтобы исправить это, полезно начать с "привязки" вашего регулярного выражения к минимально необходимой строке, возможно, с одной цифрой:

\d+

Теперь мы хотим добавить десятичную часть, но она не идет туда, где вы думаете:

\d+\.?\d* /* This isn't quite correct. */

Это по-прежнему будет соответствовать таким значениям, как 123.. Хуже того, в этом есть оттенок зла . Точка не обязательна, это означает, что у вас есть два повторяющихся класса рядом ( \d+и \d*). Это может быть опасно при неправильном использовании, открывая вашу систему для DoS-атак.

Чтобы исправить это, вместо того, чтобы рассматривать точку как необязательную, нам нужно обрабатывать ее как требуется (чтобы отделить повторяющиеся классы символов) и вместо этого сделать всю десятичную часть необязательной:

\d+(\.\d+)? /* Better. But... */

Сейчас это выглядит лучше. Нам нужен период между первой и второй последовательностями цифр, но есть фатальный недостаток: мы не можем сопоставить, .123потому что теперь требуется первая цифра.

На самом деле это довольно легко исправить. Вместо того, чтобы делать «десятичную» часть числа необязательной, нам нужно рассматривать ее как последовательность символов: 1 или несколько чисел, которые могут начинаться с префикса a, .который может начинаться с 0 или более чисел:

(\d*\.)?\d+

Теперь просто добавляем знак:

[+-]?(\d*\.)?\d+

Конечно, в Java эти косые черты довольно раздражают, поэтому мы можем заменить их в наших классах длинных символов:

[+-]?([0-9]*[.])?[0-9]+

Сопоставление и проверка

Об этом пару раз упоминалось в комментариях, поэтому я добавляю дополнение о сопоставлении, а не проверке.

Цель сопоставления - найти некоторый контент во входных данных («иголка в стоге сена»). Цель проверки - убедиться, что ввод находится в ожидаемом формате.

Регулярные выражения, по своей природе, только совпадают текст. Получив некоторый ввод, они либо найдут соответствующий текст, либо нет. Однако, «привязав» выражение к началу и концу ввода с помощью тегов привязки ( ^и $), мы можем гарантировать, что совпадение не будет найдено, если только весь ввод не соответствует выражению, эффективно используя регулярные выражения для проверки .

Регулярное выражение, описанное выше ([+-]?([0-9]*[.])?[0-9]+ ) будет соответствовать одному или нескольким числам в целевой строке. Итак, учитывая ввод:

apple 1.34 pear 7.98 version 1.2.3.4

Регулярное выражение будет соответствовать 1.34 , 7.98, 1.2, .3и .4.

Чтобы проверить, что данный ввод является числом и только числом, «привяжите» выражение к началу и концу ввода, заключив его в теги привязки:

^[+-]?([0-9]*[.])?[0-9]+$

Это найдет совпадение, только если весь ввод является числом с плавающей запятой, и не найдет совпадения, если ввод содержит дополнительные символы. Итак, учитывая ввод1.2 совпадение будет найдено, но при заданном apple 1.2 pearсовпадении не будет найдено.

Обратите внимание , что некоторые регулярные выражения двигатели имеют validate, isMatchили аналогичную функцию, которая по существу делает то , что я описал автоматически, возвращаясь , trueесли совпадение найдено , и falseесли совпадение не найдено. Также имейте в виду, что некоторые движки позволяют вам устанавливать флаги, которые изменяют определение ^и $, соответствуя началу / концу строки, а не началу / концу всего ввода. Обычно это не значение по умолчанию, но будьте осторожны с этими флагами.

JDB до сих пор помнит Монику
источник
2
JDB, спасибо и надеюсь, что вы все еще здесь! Я буду читать ваш пост в будущем :) Ваш ответ, безусловно, касается 0.24 и 2.2 и правильно запрещает 4.2.44 Все протестировано с regex101.com Однако он запрещает 123. что, как вы говорите, может быть приемлемым (и я думаю, что это является!). Я могу исправить это, изменив ваше выражение на [- +]? (\ D * [.])? \ D * (обратите внимание * в конце вместо +), но затем сумасшедшие вещи вроде. (ваш второй пример) разрешены. В любом случае, чтобы съесть мой торт и тоже его съесть?
Дэйв
2
@Dave -\d+(\.\d*)?|\.\d+
JDB все еще помнит Монику
/[-+]?(\d*[.])?\d+/.test("1.bc") // returns true
yeouuu 02
1
@yeouuu да, потому что 1.совпадает. Добавьте ^и $в начало и конец регулярного выражения, если вы хотите сопоставить, только если совпадает весь ввод.
JDB все еще помнит Монику
5
float может иметь показатель степени или быть NaN / Inf, поэтому я бы использовал это:, [-+]?(([0-9]*[.]?[0-9]+([ed][-+]?[0-9]+)?)|(inf)|(nan))e / d для float / double precision float. Не забудьте добавить к регулярному выражению флаг сгиба
Маркус Шмассманн
23

Я не думаю, что какие-либо ответы на этой странице на момент написания являются правильными (также многие другие предложения в других местах на SO также ошибочны). Сложность заключается в том, что вам нужно сопоставить все следующие возможности:

  • Без десятичной точки (т.е. целое число)
  • Цифры до и после десятичной точки (например 0.35,22.165 )
  • Только цифры перед десятичной запятой (например 0.,1234. )
  • Только цифры после десятичной точки (например .0, .5678)

В то же время вы должны убедиться, что где-то есть хотя бы одна цифра, т.е. следующее недопустимо:

  • десятичная точка сама по себе
  • десятичная точка со знаком без цифр (например, +.или -.)
  • + или - самостоятельно
  • пустая строка

Сначала это кажется сложным, но один из способов найти вдохновение - взглянуть на исходный код java.lang.Double.valueOf(String)метода OpenJDK (начните с http://hg.openjdk.java.net/jdk8/jdk8/jdk , нажмите «просмотреть», перейдите вниз /src/share/classes/java/lang/и найди Doubleкласс). Длинное регулярное выражение, которое содержит этот класс, обслуживает различные возможности, которые OP, вероятно, не имел в виду, но игнорирует для простоты его части, которые имеют дело с NaN, бесконечностью, шестнадцатеричной нотацией и показателями, а также с использованием\d нотацию вместо POSIX для одной цифрой, я могу сократить важные части регулярного выражения для числа с плавающей запятой со знаком без экспоненты до:

[+-]?((\d+\.?\d*)|(\.\d+))

Я не думаю, что есть способ избежать (...)|(...) конструкции, не допуская чего-либо, не содержащего цифр, или запретив одну из возможностей, в которой нет цифр перед десятичной точкой или цифр после нее.

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

пкеллер
источник
Если вы добавите требование сопоставления чисел, например 123., тогда да ... переключатель или - единственное решение, как я указал в комментарии к моему исходному сообщению.
JDB все еще помнит Монику
1
Этот и все / большинство других ответов игнорируют то, что float может иметь показатель степени.
NateS 08
1
@NateS Правильно, я написал «игнорируя для простоты те части, которые имеют дело с NaN, бесконечностью, шестнадцатеричной нотацией и показателями», потому что это, похоже, соответствует сфере охвата вопроса OP. Есть и более полные реализации, включая ту, которую я нашел в исходном коде JDK.
pkeller 09
1
Можно ли использовать регулярное выражение, [+-]?((?=\.?\d)\d*\.?\d*)чтобы избежать чередования? Он использует просмотр
вперед
1
@ 4esn0k Хорошее регулярное выражение! Я поигрался с этим, и он действительно работает. У меня есть два предостережения: (1) не все движки регулярных выражений поддерживают утверждения с нулевой шириной (хотя большинство современных это делают, AFAIK), и (2) упреждающий просмотр - это просто чередование под другим именем: движок все еще должен что-то попробовать и вернитесь назад, если это не сработает. Тем не менее, проголосуйте за очень интересную идею.
pkeller
7

вам нужно:

[\-\+]?[0-9]*(\.[0-9]+)?

Я избежал знаков «+» и «-», а также сгруппировал десятичную дробь со следующими цифрами, начиная с чего-то вроде «1». не действительное число.

Изменения позволят вам сопоставлять целые числа и числа с плавающей запятой. например:

0
+1
-2.0
2.23442
DiverseAndRemote.com
источник
Проблема с этим выражением заключается в том, что .1он не разрешен, даже если такой ввод общепризнан как правильный.
JDB все еще помнит Монику
Теперь он будет принимать строки нулевой длины -и +, которые не являются числами. Regex - это сложно! :)
JDB все еще помнит Монику
Кроме того, это не отвечает на фактический вопрос OP, который \.не работает.
JDB все еще помнит Монику
7

Я хочу сопоставить то, что большинство языков считает допустимыми числами (целые числа и числа с плавающей запятой):

  • '5' / '-5'

  • '1.0' / '1.' / '.1' / '-1.' / '-.1'

  • '0.45326e+04', '666999e-05', '0.2e-3', '-33.e-1'

Ноты:

  • preceding sign of number ('-' or '+') is optional

  • '-1.' and '-.1' are valid but '.' and '-.' are invalid

  • '.1e3' is valid, but '.e3' and 'e3' are invalid

Для поддержки обоих «1». и '.1' нам нужен оператор ИЛИ ('|'), чтобы исключить '.' от соответствия.

[+-]?+/- Sing является необязательным, поскольку ?означает 0 или 1 совпадение

( поскольку у нас есть 2 подвыражения, нам нужно заключить их в скобки

\d+([.]\d*)?(e[+-]?\d+)? Это для чисел, начинающихся с цифры

| разделяет подвыражения

[.]\d+(e[+-]?\d+)? это для номеров, начинающихся с "."

) конец выражений

  • Для номеров, начинающихся с "."

[.] первый символ - точка (внутри скобок, иначе это подстановочный знак)

\d+ одна или несколько цифр

(e[+-]?\d+)? это необязательная научная нотация (0 или 1 совпадение из-за окончания "?")

  • Для номеров, начинающихся с цифры

\d+ одна или несколько цифр

([.]\d*)? при желании мы можем иметь символ точки и ноль или более цифр после него

(e[+-]?\d+)? это необязательное научное обозначение

  • Научная нотация

e литерал, указывающий экспоненту

[+-]? необязательный знак экспоненты

\d+ одна или несколько цифр

Все вместе:

[+-]?(\d+([.]\d*)?(e[+-]?\d+)?|[.]\d+(e[+-]?\d+)?)

Также принять E:

[+-]?(\d+([.]\d*)?([eE][+-]?\d+)?|[.]\d+([eE][+-]?\d+)?)

( Тестовые примеры )

Яннис Т
источник
4

Это просто: вы использовали Java и должны использовать \\.вместо \.(поиск экранирования символов в Java).

безымянный
источник
Вероятно, вы правы ... сообщение об ошибке выглядит как синтаксическая ошибка языка программирования, а не ошибка парсера регулярных выражений.
JDB все еще помнит Монику
3

Это сработало для меня:

(?P<value>[-+]*\d+\.\d+|[-+]*\d+)

Вы также можете использовать это (без именованного параметра):

([-+]*\d+\.\d+|[-+]*\d+)

Используйте какой-нибудь онлайн-тестер регулярных выражений, чтобы проверить его (например, regex101)

grafi71
источник
2
^[+]?([0-9]{1,2})*[.,]([0-9]{1,1})?$

Это будет соответствовать:

  1. 1.2
  2. 12,3
  3. 1,2
  4. 12,3
Михай Чобану
источник
Хотя этот фрагмент кода приветствуется и может оказать некоторую помощь, его можно было бы значительно улучшить, если бы он включал объяснение того, как и почему это решает проблему. Помните, что вы отвечаете на вопрос для будущих читателей, а не только для человека, который задает его сейчас! Пожалуйста , измените свой ответ , чтобы добавить объяснение, и дать указание о том , что применять ограничения и допущения.
Тоби Спейт
О, спасибо, я ищу это
Серж Бурлака,
0
[+-]?(([1-9][0-9]*)|(0))([.,][0-9]+)?

[+-]? - необязательный ведущий знак

(([1-9][0-9]*)|(0)) - целое без нуля в начале, включая единственный ноль

([.,][0-9]+)? - необязательная дробная часть

Алексей Гутиков
источник
1
Дайте больше информации - для людей, не знающих регулярных выражений, это гиероглифы. Людям, знающим их, это не нужно.
Питер - Восстановить Монику
0

В C ++ с использованием библиотеки регулярных выражений

Ответ будет примерно таким:

[0-9]?([0-9]*[.])?[0-9]+

Обратите внимание, что я не беру символ знака, если бы вы хотели его с символом знака, он бы сделал следующее:

[+-]?([0-9]*[.])?[0-9]+

Это также отделяет обычное число или десятичное число.

LuisDev99
источник
0

В нотации c число с плавающей запятой может иметь следующие формы:

  1. 123
  2. 123.
  3. 123,24
  4. 0,24
  5. 2e-2 = 2 * 10 пол -2 = 2 * 0,1
  6. 4E + 4 = 4 * 10 pow 4 = 4 * 10 000

Для создания регулярного выражения с плавающей запятой я сначала создам «переменную регулярного выражения int»:

(([1-9][0-9]*)|0) will be int

Теперь я напишу небольшие фрагменты регулярного выражения с плавающей запятой - решение состоит в том, чтобы объединить эти фрагменты с помощью символа "|".

Чанки:

- (([+-]?{int}) satysfies case 1
- (([+-]?{int})"."[0-9]*)  satysfies cases 2 and 3
- ("."[0-9]*) satysfies case 4
- ([+-]?{int}[eE][+-]?{int}) satysfies cases 5 and 6

Окончательное решение (соединение небольших кусков):

(([+-]?{int})|(([+-]?{int})"."[0-9]*)|("."[0-9]*)|([+-]?{int}[eE][+-]?{int})
Зоран Медоевич
источник
-1
[+/-] [0-9]*.[0-9]+

Попробуйте это решение.

Лола Горочана
источник
-1

для javascript

const test = new RegExp('^[+]?([0-9]{0,})*[.]?([0-9]{0,2})?$','g');

Что будет работать для 1,23 1234,22 0 0,12 12

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

Mjwrazor
источник