Wyrażenie regularne dla liczb zmiennoprzecinkowych


116

Mam zadanie dopasować liczby zmiennoprzecinkowe. Napisałem dla niego następujące wyrażenie regularne:

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

Ale zwraca błąd:

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

Zgodnie z moją wiedzą musimy również użyć znaku ucieczki .. Proszę, popraw mnie tam, gdzie się mylę.


10
W jakim języku jest używane to wyrażenie regularne?
CaffGeek

3
@JDB - Dlaczego rozdajesz 100 punktów za wyrażenie regularne typu liczba / zmiennoprzecinkowe? Standard zawsze był (?:\d+(?:\.\d*)?|\.\d+)i był publikowany w nieskończoność na SO ...


1
[-+]?([0-9]*[.])?[0-9]+([eE][-+]?\d+)?jeśli chcesz również złapać notację wykładniczą, np. 3.023e-23
wcochran

W niektórych językach, takich jak Java czy C ++, należy zastosować ukośnik odwrotny. Aby uzyskać wyrażenie regularne „\.”, Należy użyć ciągu „\\.”. Python omija ten problem, używając nieprzetworzonych ciągów.
HackerBoss

Odpowiedzi:


259

TL; DR

Użyj [.]zamiast \.i [0-9]zamiast, \daby uniknąć problemów z ucieczką w niektórych językach (takich jak Java).

Dzięki bezimiennemu za pierwotne rozpoznanie tego.

Jednym stosunkowo prostym wzorcem dopasowania liczby zmiennoprzecinkowej jest

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

To będzie pasować:

  • 123
  • 123.456
  • .456

Zobacz przykład roboczy

Jeśli chcesz również dopasować 123.(kropka bez części dziesiętnej), będziesz potrzebować nieco dłuższego wyrażenia:

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

Zobacz odpowiedź pkellera, aby uzyskać pełniejsze wyjaśnienie tego wzoru

Jeśli chcesz uwzględnić liczby niedziesiętne, takie jak szesnastkowe i ósemkowe, zobacz moją odpowiedź na temat Jak rozpoznać, czy ciąg jest liczbą? .

Jeśli chcesz sprawdzić, czy dane wejściowe są liczbą (zamiast znajdować liczbę w danych wejściowych), powinieneś otoczyć wzór znakami ^i $, tak jak to:

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

Nieregularne wyrażenia regularne

„Wyrażenia regularne” zaimplementowane w większości nowoczesnych języków, interfejsów API, frameworków, bibliotek itp. Opierają się na koncepcji opracowanej w teorii języka formalnego . Jednak inżynierowie oprogramowania dodali wiele rozszerzeń, które przenoszą te implementacje daleko poza formalną definicję. Tak więc, chociaż większość silników wyrażeń regularnych jest do siebie podobna, w rzeczywistości nie ma standardu. Z tego powodu wiele zależy od tego, jakiego języka, API, frameworka czy biblioteki używasz.

(Nawiasem mówiąc, aby zmniejszyć zamieszanie, wiele miały do korzystania z „ regex ” lub „ regexp ”, aby opisać te ulepszone językach ogłoszeń. See Czy regex samo jak wyrażenie regularne? Na RexEgg.com aby uzyskać więcej informacji.)

To powiedziawszy, większość silników regex (właściwie wszystkie, o ile wiem) zaakceptowałaby \.. Najprawdopodobniej jest problem z ucieczką.

Kłopoty z ucieczką

Niektóre języki mają wbudowaną obsługę wyrażeń regularnych, na przykład JavaScript . Dla tych języków, które tego nie robią, ucieczka może stanowić problem.

Dzieje się tak, ponieważ zasadniczo kodujesz w języku w języku. Na przykład Java używa \jako znaku zmiany znaczenia w swoich ciągach, więc jeśli chcesz umieścić literalny znak ukośnika odwrotnego w ciągu, musisz go zmienić:

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

Jednak wyrażenia regularne również używają \znaku do ucieczki, więc jeśli chcesz dopasować znak dosłowny \, musisz go uciec dla silnika wyrażeń regularnych, a następnie ponownie uciec dla Javy:

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

W twoim przypadku prawdopodobnie nie uniknąłeś znaku ukośnika odwrotnego w języku, w którym programujesz:

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

Cała ta ucieczka może być bardzo zagmatwana. Jeśli język, z którym pracujesz, obsługuje nieprzetworzone łańcuchy , powinieneś użyć ich, aby zmniejszyć liczbę ukośników odwrotnych, ale nie wszystkie języki obsługują (przede wszystkim Java). Na szczęście istnieje alternatywa, która będzie działać przez jakiś czas:

String correctPattern = "[.]";

W przypadku silnika wyrażeń regularnych \.i [.]oznaczają dokładnie to samo. Zauważ, że nie działa to w każdym przypadku, jak nowa linia ( \\n), otwarty nawias kwadratowy ( \\[) i ukośnik odwrotny ( \\\\lub [\\]).

Uwaga dotycząca pasujących liczb

(Podpowiedź: jest trudniej niż myślisz)

Dopasowanie liczby to jedna z tych rzeczy, które uważasz za dość łatwe w przypadku wyrażenia regularnego, ale w rzeczywistości jest to dość trudne. Przyjrzyjmy się Twojemu podejściu, kawałek po kawałku:

[-+]?

Dopasuj opcjonalny -lub+

[0-9]*

Dopasuj 0 lub więcej kolejnych cyfr

\.?

Dopasuj opcjonalne .

[0-9]*

Dopasuj 0 lub więcej kolejnych cyfr

Po pierwsze, możemy trochę wyczyścić to wyrażenie, używając skrótu klasy znaków dla cyfr (zwróć uwagę, że jest to również podatne na wspomniany powyżej problem ze znakami ucieczki):

[0-9] = \d

Użyję \dponiżej, ale pamiętaj, że oznacza to to samo co [0-9]. (Cóż, w rzeczywistości w niektórych silnikach \dbędą pasować cyfry ze wszystkich skryptów, więc będzie pasować bardziej niż [0-9]będzie, ale to prawdopodobnie nie ma znaczenia w twoim przypadku).

Teraz, jeśli przyjrzysz się temu uważnie, zdasz sobie sprawę, że każda część twojego wzoru jest opcjonalna . Ten wzorzec może pasować do łańcucha o długości 0; ciąg złożony tylko z +lub -; lub ciąg składający się tylko z .. To prawdopodobnie nie jest to, co zamierzałeś.

Aby to naprawić, dobrze jest zacząć od „zakotwiczenia” wyrażenia regularnego za pomocą minimalnego wymaganego ciągu, prawdopodobnie jednej cyfry:

\d+

Teraz chcemy dodać część dziesiętną, ale nie idzie to tam, gdzie myślisz:

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

Będzie to nadal pasowało do wartości takich jak 123.. Co gorsza, ma w sobie odrobinę zła . Kropka jest opcjonalna, co oznacza, że ​​masz dwie powtarzające się klasy obok siebie ( \d+i \d*). W rzeczywistości może to być niebezpieczne, jeśli zostanie użyte w niewłaściwy sposób, otwierając system na ataki DoS.

Aby to naprawić, zamiast traktować kropkę jako opcjonalną, musimy traktować ją jako wymaganą (aby oddzielić powtarzające się klasy znaków) i zamiast tego uczynić całą część dziesiętną opcjonalną:

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

Teraz wygląda lepiej. Wymagamy okresu między pierwszą sekwencją cyfr a drugą, ale jest fatalna wada: nie możemy dopasować, .123ponieważ wymagana jest teraz cyfra wiodąca.

W rzeczywistości jest to dość łatwe do naprawienia. Zamiast uczynić „dziesiętną” część liczby opcjonalną, musimy spojrzeć na nią jako na sekwencję znaków: 1 lub więcej liczb, które mogą być poprzedzone znakiem a, .które mogą być poprzedzone 0 lub większą liczbą cyfr:

(\d*\.)?\d+

Teraz dodajemy tylko znak:

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

Oczywiście te ukośniki są dość irytujące w Javie, więc możemy podstawiać w naszych długich klasach znaków:

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

Dopasowywanie a walidacja

Pojawiło się to kilka razy w komentarzach, więc dodaję dodatek dotyczący dopasowywania i sprawdzania poprawności.

Celem dopasowania jest znalezienie treści w danych wejściowych („igła w stogu siana”). Celem walidacji jest upewnienie się, że dane wejściowe mają oczekiwany format.

Regeksy z natury pasują tylko do tekstu. Biorąc pod uwagę pewne dane wejściowe, albo znajdą pasujący tekst, albo nie. Jednak poprzez „przyciąganie” wyrażenia do początku i końca danych wejściowych za pomocą znaczników kotwicy ( ^i $), możemy zapewnić, że żadne dopasowanie nie zostanie znalezione, chyba że całe dane wejściowe będą pasować do wyrażenia, efektywnie wykorzystując wyrażenia regularne do walidacji .

Wyrażenie regularne opisane powyżej ( [+-]?([0-9]*[.])?[0-9]+) dopasuje jedną lub więcej liczb w ciągu docelowym. Więc biorąc pod uwagę dane wejściowe:

apple 1.34 pear 7.98 version 1.2.3.4

Regex będą pasować 1.34, 7.98, 1.2, .3i .4.

Aby sprawdzić, czy dane wejściowe są liczbą, a jedynie liczbą, „przyciągnij” wyrażenie na początek i na koniec danych wejściowych, zawijając je w znaczniki kotwicy:

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

Spowoduje to znalezienie dopasowania tylko wtedy, gdy całe wejście jest liczbą zmiennoprzecinkową, i nie znajdzie dopasowania, jeśli wejście zawiera dodatkowe znaki. Tak więc, biorąc pod uwagę dane wejściowe 1.2, zostanie znalezione dopasowanie, ale pod warunkiem, że apple 1.2 pearżadne dopasowania nie zostaną znalezione.

Zauważ, że niektóre silniki regex mają validate, isMatchlub podobną funkcję, która w zasadzie robi to, co Opisałem automatycznie, wracając truejeśli zostanie znaleziony, a falsejeśli nie zostanie znaleziony. Pamiętaj również, że niektóre silniki pozwalają na ustawienie flag, które zmieniają definicję ^i $, dopasowując początek / koniec linii zamiast początku / końca całego wejścia. Zwykle nie jest to ustawienie domyślne, ale uważaj na te flagi.


2
JDB, dzięki i mam nadzieję, że nadal jesteś w pobliżu! Czytam twój post w przyszłości :) Twoja odpowiedź z pewnością zadba o 0.24 i 2.2 i poprawnie nie zezwala na 4.2.44 Wszystkie testowane z regex101.com Jednak nie zezwala na 123. co, jak mówisz, może być akceptowalne (i myślę, że tak jest!). Mogę to naprawić, zmieniając twoje wyrażenie na [- +]? (\ D * [.])? \ D * (zauważ * na końcu zamiast +), ale potem takie szalone rzeczy. (twój drugi przykład) są dozwolone. Zresztą, żeby mieć moje ciasto i też je zjeść?
Dave

2
@Dave -\d+(\.\d*)?|\.\d+
JDB nadal pamięta Monikę

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

1
@yeouuu tak, ponieważ 1.pasuje. Dodaj ^i $na początku i na końcu wyrażenia regularnego, jeśli chcesz dopasować tylko wtedy, gdy całe dane wejściowe są zgodne.
JDB nadal pamięta Monicę

5
liczby [-+]?(([0-9]*[.]?[0-9]+([ed][-+]?[0-9]+)?)|(inf)|(nan))zmiennoprzecinkowe mogą mieć wykładniki lub być NaN / Inf, więc użyłbym tego:, e / d dla liczby zmiennoprzecinkowej / podwójnej precyzji. Nie zapomnij flagi spasowania do wyrażenia regularnego
Markus Schmassmann

23

Nie sądzę, aby którakolwiek z odpowiedzi na tej stronie w momencie pisania była poprawna (również wiele innych sugestii w innych miejscach na SO jest błędnych). Trudność polega na tym, że musisz dopasować wszystkie poniższe możliwości:

  • Bez kropki dziesiętnej (tj. Wartość całkowita)
  • Cyfr, zarówno przed jak i po przecinku (np 0.35, 22.165)
  • Cyfry przed przecinkiem tylko (np 0., 1234.)
  • Miejsc po przecinku tylko (na przykład .0, .5678)

Jednocześnie musisz upewnić się, że gdzieś jest co najmniej jedna cyfra, czyli niedozwolone są:

  • sama kropka dziesiętna
  • znak dziesiętny ze znakiem bez cyfr (np. +.lub -.)
  • +lub -samodzielnie
  • pusty ciąg

Na początku wydaje się to trudne, ale jednym ze sposobów znalezienia inspiracji jest przyjrzenie się źródłu OpenJDK dla java.lang.Double.valueOf(String)metody (zacznij od http://hg.openjdk.java.net/jdk8/jdk8/jdk , kliknij "Przeglądaj", przejdź w dół /src/share/classes/java/lang/i znajdź Doubleklasę). Długi regex, który zawiera ta klasa, uwzględnia różne możliwości, o których OP prawdopodobnie nie miał na myśli, ale ignoruje dla uproszczenia części, które dotyczą NaN, nieskończoności, notacji szesnastkowej i wykładników, i używa \dzamiast notacji POSIX dla pojedynczej cyfry, mogę zredukować ważne części wyrażenia regularnego dla liczby zmiennoprzecinkowej ze znakiem bez wykładnika do:

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

Nie sądzę, aby można było uniknąć (...)|(...)konstrukcji bez dopuszczenia czegoś, co nie zawiera cyfr, lub zakazania jednej z możliwości, która nie ma cyfr przed kropką dziesiętną lub żadnych cyfr po niej.

Oczywiście w praktyce będziesz musiał uwzględnić końcowe lub poprzedzające białe znaki, albo w samym wyrażeniu regularnym, albo w kodzie, który go używa.


Jeśli dodasz wymóg dopasowania liczb 123., to tak ... przełącznik lub jest jedynym rozwiązaniem, jak wskazałem w komentarzu do mojego oryginalnego postu.
JDB nadal pamięta Monikę z

1
To i wszystkie / większość innych odpowiedzi ignorują fakt, że zmiennoprzecinkowa może mieć wykładnik.
NateS

1
@NateS Zgadza się, napisałem „ignorując dla uproszczenia te części, które dotyczą NaN, nieskończoności, notacji szesnastkowej i wykładników”, ponieważ wydaje się, że pasuje to do zakresu pytania OP. Istnieje więcej kompletnych implementacji, w tym ta, którą znalazłem w kodzie źródłowym JDK.
pkeller

1
Czy [+-]?((?=\.?\d)\d*\.?\d*)można użyć wyrażenia regularnego, aby uniknąć zmiany? Używa
lookahead

1
@ 4esn0k Niezłe wyrażenie regularne! Bawiłem się tym i to działa. Mam dwa zastrzeżenia: (1) nie wszystkie silniki wyrażeń regularnych obsługują asercje o zerowej szerokości (chociaż większość współczesnych je obsługuje, AFAIK) i (2) antycypowanie to po prostu alternatywa pod inną nazwą: silnik wciąż musi coś wypróbować i cofnij się, jeśli to nie zadziała. Niemniej jednak poproś o bardzo fajny pomysł.
pkeller

7

potrzebujesz:

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

Uniknąłem znaku „+” i „-”, a także zgrupowałem ułamek dziesiętny z następującymi po nim cyframi, ponieważ coś w rodzaju „1”. nie jest prawidłową liczbą.

Zmiany pozwolą ci dopasować liczby całkowite i zmiennoprzecinkowe. na przykład:

0
+1
-2.0
2.23442

Problem z tym wyrażeniem polega na tym, .1że nie byłoby to dozwolone, mimo że takie dane wejściowe są powszechnie uznawane za poprawne.
JDB nadal pamięta Monikę

Teraz będzie akceptować ciągi znaków -i +, które nie są liczbami. Regex jest trudny! :)
JDB wciąż pamięta Monikę

Ponadto nie odpowiada to faktycznemu pytaniu PO, które \.nie działa.
JDB wciąż pamięta Monikę

7

Chcę dopasować, które większość języków uważa za prawidłowe liczby (liczby całkowite i zmiennoprzecinkowe):

  • '5' / '-5'

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

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

Uwagi:

  • 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

Aby wspierać oba „1”. i „.1” potrzebujemy operatora OR („|”), aby upewnić się, że wykluczamy „.” od dopasowania.

[+-]?+/- sing jest opcjonalne, ponieważ ?oznacza 0 lub 1 dopasowania

( ponieważ mamy 2 wyrażenia podrzędne, musimy je umieścić w nawiasach

\d+([.]\d*)?(e[+-]?\d+)? Dotyczy to liczb zaczynających się od cyfry

| oddziela wyrażenia podrzędne

[.]\d+(e[+-]?\d+)? dotyczy to numerów zaczynających się od „.”

) koniec wyrażeń

  • Dla numerów zaczynających się od „.”

[.] pierwszy znak to kropka (w nawiasach lub w innym przypadku jest to symbol wieloznaczny)

\d+ jedna lub więcej cyfr

(e[+-]?\d+)? jest to opcjonalna notacja naukowa (0 lub 1 trafień ze względu na końcówkę „?”)

  • Dla liczb zaczynających się od cyfry

\d+ jedna lub więcej cyfr

([.]\d*)? opcjonalnie możemy mieć kropkę, a po niej zero lub więcej cyfr

(e[+-]?\d+)? jest to opcjonalna notacja naukowa

  • Notacja naukowa

e literał, który określa wykładnik

[+-]? opcjonalny znak potęgi

\d+ jedna lub więcej cyfr

Wszystkie razem:

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

Aby również zaakceptować E:

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

( Przypadki testowe )


4

To proste: użyłeś Javy i powinieneś użyć \\.zamiast \.(szukaj znaków ucieczki w Javie).


Prawdopodobnie masz rację ... komunikat o błędzie wygląda jak błąd składni języka programowania, a nie błąd parsera wyrażeń regularnych.
JDB nadal pamięta Monikę z

3

Ten pracował dla mnie:

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

Możesz również użyć tego (bez nazwanego parametru):

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

Użyj testera regex online, aby go przetestować (np. Regex101)


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

To będzie pasować:

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

Choć ten fragment kodu jest mile widziany i może zapewnić jakąś pomoc, byłoby znacznie się poprawiła, gdyby obejmowała wyjaśnienie z how i dlaczego to rozwiązuje problem. Pamiętaj, że odpowiadasz na pytanie do czytelników w przyszłości, a nie tylko osoba, która zapyta teraz! Proszę edytować swoje odpowiedzi, aby dodać wyjaśnienie, i dać wskazówkę co zastosować ograniczenia i założenia.
Toby Speight

och dzięki, szukam tego
Serg Burlaka

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

[+-]? - opcjonalny znak wiodący

(([1-9][0-9]*)|(0)) - liczba całkowita bez wiodącego zera, w tym pojedyncze zero

([.,][0-9]+)? - opcjonalna część ułamkowa


1
Podaj więcej informacji - dla osób nie znających wyrażeń regularnych są to hyeroglify. Dla ludzi, którzy je znają, nie potrzebują tego.
peterh - Przywróć Monikę

0

W C ++ przy użyciu biblioteki regex

Odpowiedź wyglądałaby tak:

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

Zauważ, że nie biorę symbolu znaku, gdybyś chciał go z symbolem znaku, to by to dotyczyło:

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

To również oddziela zwykłą liczbę lub liczbę dziesiętną.


0

W notacji c liczba zmiennoprzecinkowa może mieć następujące kształty:

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

Aby utworzyć zmiennoprzecinkowe wyrażenie regularne, najpierw utworzę „zmienną wyrażenia regularnego int”:

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

Teraz napiszę małe fragmenty wyrażenia regularnego typu float - rozwiązaniem jest połączenie tych fragmentów za pomocą symbolu „|”.

Kawałki:

- (([+-]?{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

Ostateczne rozwiązanie (łączenie małych kawałków):

(([+-]?{int})|(([+-]?{int})"."[0-9]*)|("."[0-9]*)|([+-]?{int}[eE][+-]?{int})


-1

dla javascript

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

Co zadziała dla 1,23 1234,22 0 0,12 12

Możesz zmienić części w, {}aby uzyskać różne wyniki w długości i początku części dziesiętnej. Jest to używane w wejściach do wprowadzania liczby i sprawdzania każdego wejścia podczas wpisywania, zezwalając tylko na to, co się powiedzie.

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.