Регулярний вираз для чисел з плаваючою комою


115

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

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

Але, він повертає помилку:

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

Згідно з моїми знаннями, нам також потрібно використовувати символ втечі .. Будь ласка, виправте мене там, де я помиляюся.


10
На якій мові використовується цей регулярний вираз?
CaffGeek

3
@JDB - Чому ви віддаєте 100 балів за число / плаваючий регулярний вираз? Стандарт завжди був (?:\d+(?:\.\d*)?|\.\d+)і був розміщений ad infinitum на SO ...

дивіться також stackoverflow.com/questions/638565/…
Jason S

1
[-+]?([0-9]*[.])?[0-9]+([eE][-+]?\d+)?якщо ви хочете також назвати експоненціальні позначення, e, g, 3.023e-23
wcochran

У деяких мовах, таких як Java або C ++, слід наклоняти зворотний нахил. Отже, щоб отримати регулярний вираз "\.", Ви б використовували рядок "\\.". Python обходить це за допомогою сирих рядків.
HackerBoss

Відповіді:


258

TL; DR

Використовуйте [.]замість цього, \.а не [0-9]замість цього, \dщоб уникнути проблем у деяких мовах (наприклад, Java).

Дякуємо безіменному за первісне визнання цього.

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

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

Це буде відповідати:

  • 123
  • 123.456
  • .456

Дивіться робочий приклад

Якщо ви також хочете відповідати 123.(період без десяткової частини), вам знадобиться трохи довший вираз:

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

Дивіться відповідь pkeller для більш повного пояснення цієї моделі

Якщо ви хочете включити недесяткові числа, такі як шістнадцятковий і восьмеричний, дивіться мою відповідь на тему Як визначити, чи є рядок числом? .

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

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

Нерегулярні регулярні вирази

"Регулярні вирази", реалізовані в більшості сучасних мов, API, фреймворків, бібліотек тощо, базуються на концепції, розробленій у формальній теорії мови . Однак інженери програмного забезпечення додали багато розширень, які виходять з цих реалізацій далеко за рамки формального визначення. Отже, хоча більшість двигунів регулярної експресії схожі між собою, насправді немає стандарту. З цієї причини багато що залежить від того, якою мовою, API, рамкою чи бібліотекою ви користуєтесь.

(До речі, щоб зменшити плутанину, багато хто взяв для використання " regex " або " regexp " для опису цих розширених мов відповідності. Див., Чи Regex - це те саме, що і регулярний вираз? На RexEgg.com для отримання додаткової інформації.)

Це сказало, що більшість регекс-двигунів (насправді всі вони, наскільки я знаю) погодиться \.. Швидше за все, існує проблема із втечею.

Біда з втечею

Деякі мови мають вбудовану підтримку для регулярних виразів, таких як JavaScript . Для тих мов, які цього не роблять, втеча може стати проблемою.

Це тому, що ви в основному кодуєте мовою всередині мови. Наприклад, Java використовує \як рятувальний символ у своїх рядках, тому, якщо ви хочете розмістити буквальний зворотний косий рядок у рядку, вам слід уникнути цього:

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

Однак, регулярні вирази також використовують \символ для втечі, тому якщо ви хочете відповідати буквальному \символу, ви повинні залишити його для двигуна regexe, а потім знову вийти з нього для 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 = "[.]";

Для регулярних виразів, \.і [.]означають одне і те ж саме. Зауважте, що це працює не в кожному випадку, як newline ( \\n), відкрита квадратна дужка ( \\[) та зворотний кут ( \\\\або [\\]).

Примітка про відповідність чисел

(Підказка: Це важче, ніж ви думаєте)

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

[-+]?

Збігайте за бажанням -або+

[0-9]*

Збіг 0 або більше послідовних цифр

\.?

Збігайте за бажанням .

[0-9]*

Збіг 0 або більше послідовних цифр

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

[0-9] = \d

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

Тепер, якщо ви уважно подивитесь на це, ви зрозумієте, що кожна окрема частина вашого шаблону не є обов'язковою . Цей візерунок може відповідати рядку 0 довжини; рядок, що складається лише з +або -; або, рядок, що складається лише з a .. Це, мабуть, не те, що ви задумали.

Щоб виправити це, корисно почати з "прикріплення" свого регулярного виразу з мінімально необхідним рядком, ймовірно, однією цифрою:

\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якщо збіг не знайдено. Також майте на увазі, що деякі двигуни дозволяють встановлювати прапори, які змінюють визначення ^та $, узгоджуючи початок / кінець рядка, а не початок / кінець усього вводу. Зазвичай це не за замовчуванням, але слідкуйте за цими прапорами.


2
JDB, дякую, і я сподіваюся, що ти все ще існує! Я читаю ваше повідомлення в майбутньому :) Ваша відповідь, безумовно, стосується 0,24 і 2,2 і правильно відміняє 4.2.44 Усі перевірені на сайті regex101.com Однак він забороняє 123. Що, як ви кажете, може бути прийнятним (і я думаю, що це є!). Я можу це виправити, змінивши ваш вираз на [- +]? (\ D * [.])? \ D * (зауважте * в кінці замість +), але потім шалені речі, як-от. (ваш другий приклад) дозволено. У будь-якому випадку, щоб мати свій торт і з'їсти його теж?
Дейв


/[-+]?(\d*[.])?\d+/.test("1.bc") // returns true
yeouuu

1
@yeouuu так, тому що 1.відповідає. Додати ^і $в початок і кінець регулярного виразу , якщо ви хочете , щоб відповідати тільки якщо всій вхідний сірників.
JDB досі пам’ятає Моніку

5
float можуть мати показники або бути NaN / Inf, тому я би використовував це:, [-+]?(([0-9]*[.]?[0-9]+([ed][-+]?[0-9]+)?)|(inf)|(nan))e / d для float / подвійної точності float. Не забувайте прапор складного корпусу до
виразів

23

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

  • Немає десяткової точки (тобто ціле значення)
  • Цифри як до, так і після десяткової крапки (наприклад 0.35, 22.165)
  • Цифри перед десятковою комою (наприклад 0., 1234.)
  • Цифри лише після десяткової крапки (наприклад .0, .5678)

У той же час ви повинні переконатися, що десь є хоча б одна цифра, тобто не дозволяється:

  • десятковий знак самостійно
  • підписаний десятковий знак без цифр (тобто +.або -.)
  • +або -самостійно
  • порожній рядок

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

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

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

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


Якщо ви додасте вимогу до відповідності таким числу 123., то так ... перемикач - це єдине рішення, як я зазначив у коментарі до свого початкового повідомлення.
JDB досі пам’ятає Моніку

1
Це та всі / більшість інших відповідей ігнорують, що поплавок може мати показник.
NateS

1
@NateS Правильно, я написав "ігноруючи для простоти його частини, які стосуються NaN, нескінченності, шістнадцяткових позначень та показників", тому що це, здається, відповідає обсягу питання ОП. Навколо є більш повні реалізації, включаючи ту, яку я знайшов у вихідному коді JDK.
pkeller

1
Чи [+-]?((?=\.?\d)\d*\.?\d*)можна використовувати регулярний вираз, щоб уникнути чергування? У ній використовується
lookahead

1
@ 4esn0k Гарний регекс! Я пограв з цим, і це спрацює. У мене є два застереження: (1) не всі регекс-двигуни підтримують твердження нульової ширини (хоча більшість сучасних - AFAIK), і (2) перегляд вперед - це лише чергування з іншою назвою: двигун все ще повинен щось спробувати і назад, якщо це не працює. Все ж є прихильність до дуже охайної ідеї.
pkeller

7

що вам потрібно:

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

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

Зміни дозволять вам зіставити цілі числа та плавці. наприклад:

0
+1
-2.0
2.23442

Проблема з цим виразом полягає в тому, що .1це не буде дозволено, навіть якщо таке введення загальновизнано як правильне.
JDB досі пам’ятає Моніку

Тепер він прийме рядки нульової довжини -та +, які не є числами. Регекс хитрий! :)
JDB досі пам’ятає Моніку

Крім того, це не відповідає актуальному питанню ОП, яке \.не працює.
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' нам потрібен оператор АБО ('|'), щоб переконатися, що ми виключаємо '.' від відповідності.

[+-]?+/- співати необов’язково, оскільки ?означає 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)


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

Це буде відповідати:

  1. 1.2
  2. 12.3
  3. 1,2
  4. 12,3

У той час як цей фрагмент коду вітається, і може забезпечити деяку допомогу, було б значно покращено , якщо вона була придбана пояснення про те , як і те, чому це вирішує проблему. Пам’ятайте, що ви відповідаєте на запитання читачів у майбутньому, а не лише про людину, яка зараз запитує! Будь ласка, відредагуйте свою відповідь, щоб додати пояснення та вказати, які обмеження та припущення застосовуються.
Toby Speight

о, спасибі, я люблю це
Serg Burlaka

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]+

Це також відокремлює звичайне число або десятковий номер.


0

У позначенні c число float може зустрічатися у таких формах:

  1. 123
  2. 123.
  3. 123.24
  4. .24
  5. 2е-2 = 2 * 10 порох -2 = 2 * 0,1
  6. 4E + 4 = 4 * 10 pow 4 = 4 * 10 000

Для створення регулярної експресії з плаваючою формою я спершу створить "змінну регулярної експресії":

(([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

для JavaScript

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

Який би працював за 1,23 1234,22 0 0,12 12

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

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