Ekspresi reguler untuk bilangan floating point

115

Saya memiliki tugas untuk mencocokkan angka floating point. Saya telah menulis ekspresi reguler berikut untuk itu:

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

Tapi, itu mengembalikan kesalahan:

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

Sesuai pengetahuan saya, kita perlu menggunakan karakter pelarian untuk itu .juga. Harap perbaiki saya jika saya salah.

Gopal Samant
sumber
10
Dalam bahasa apa regex ini digunakan?
CaffGeek
3
@JDB - Mengapa Anda memberikan 100 poin untuk sejumlah / float regex? Standar selalu (?:\d+(?:\.\d*)?|\.\d+)dan telah diposting infinitum iklan di SO ...
1
[-+]?([0-9]*[.])?[0-9]+([eE][-+]?\d+)?jika Anda ingin menangkap notasi eksponensial juga, e, g, 3.023e-23
wcochran
Dalam beberapa bahasa seperti Java atau C ++, garis miring terbalik harus di-escape. Jadi untuk mendapatkan regex "\.", Anda akan menggunakan string "\\.". Python mengatasi ini dengan menggunakan string mentah.
HackerBoss

Jawaban:

258

TL; DR

Gunakan [.]alih-alih \.dan [0-9]alih-alih \duntuk menghindari keluar dari masalah dalam beberapa bahasa (seperti Java).

Terima kasih kepada yang tak bernama karena awalnya mengenali ini.

Salah satu pola yang relatif sederhana untuk mencocokkan bilangan floating point adalah

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

Ini akan cocok dengan:

  • 123
  • 123.456
  • .456

Lihat contoh kerja

Jika Anda juga ingin mencocokkan 123.(titik tanpa bagian desimal), Anda memerlukan ekspresi yang sedikit lebih panjang:

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

Lihat jawaban pkeller untuk penjelasan lebih lengkap tentang pola ini

Jika Anda ingin memasukkan angka non-desimal, seperti heksadesimal dan oktal, lihat jawaban saya untuk Bagaimana cara mengidentifikasi jika string adalah angka? .

Jika Anda ingin memvalidasi bahwa input adalah angka (daripada mencari angka dalam input), maka Anda harus mengapit pola dengan ^dan $, seperti:

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

Ekspresi Reguler Tidak Beraturan

"Ekspresi reguler", seperti yang diterapkan di sebagian besar bahasa modern, API, kerangka kerja, perpustakaan, dll., Didasarkan pada konsep yang dikembangkan dalam teori bahasa formal . Namun, insinyur perangkat lunak telah menambahkan banyak ekstensi yang membawa implementasi ini jauh melampaui definisi formal. Jadi, meskipun sebagian besar mesin ekspresi reguler mirip satu sama lain, sebenarnya tidak ada standar. Untuk alasan ini, banyak hal bergantung pada bahasa, API, framework, atau library apa yang Anda gunakan.

(Kebetulan, untuk membantu mengurangi kebingungan, banyak yang menggunakan " regex " atau " regexp " untuk mendeskripsikan bahasa pencocokan yang disempurnakan ini. Lihat Apakah Regex Sama dengan Ekspresi Reguler? Di RexEgg.com untuk informasi selengkapnya.)

Meskipun demikian, sebagian besar mesin regex (sebenarnya, semuanya, sejauh yang saya tahu) akan menerimanya \.. Kemungkinan besar, ada masalah saat kabur.

Masalah dengan Melarikan Diri

Beberapa bahasa memiliki dukungan bawaan untuk ekspresi reguler, seperti JavaScript . Untuk bahasa yang tidak, pelolosan bisa menjadi masalah.

Ini karena Anda pada dasarnya membuat kode dalam bahasa dalam suatu bahasa. Java, misalnya, menggunakan \karakter escape dalam stringnya, jadi jika Anda ingin menempatkan karakter backslash literal dalam string, Anda harus menghindarinya:

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

Namun, ekspresi reguler juga menggunakan \karakter untuk melarikan diri, jadi jika Anda ingin mencocokkan \karakter literal , Anda harus melepaskannya untuk mesin regexe, lalu melepaskannya lagi untuk Java:

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

Dalam kasus Anda, Anda mungkin tidak lolos dari karakter garis miring terbalik dalam bahasa pemrograman Anda:

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

Semua pelarian ini bisa sangat membingungkan. Jika bahasa yang Anda gunakan mendukung string mentah , Anda harus menggunakannya untuk mengurangi jumlah garis miring terbalik, tetapi tidak semua bahasa mendukung string mentah (terutama: Java). Untungnya, ada alternatif yang akan berhasil beberapa saat:

String correctPattern = "[.]";

Untuk mesin regex, \.dan [.]artinya sama persis. Perhatikan bahwa ini tidak berfungsi di setiap kasus, seperti newline ( \\n), open square bracket ( \\[) dan backslash ( \\\\atau [\\]).

Catatan tentang Nomor Pencocokan

(Petunjuk: Ini lebih sulit dari yang Anda pikirkan)

Mencocokkan angka adalah salah satu hal yang menurut Anda cukup mudah dengan regex, tetapi sebenarnya cukup rumit. Mari kita lihat pendekatan Anda, sepotong demi sepotong:

[-+]?

Cocok dengan opsional -atau+

[0-9]*

Cocokkan 0 atau lebih digit berurutan

\.?

Cocokkan opsional .

[0-9]*

Cocokkan 0 atau lebih digit berurutan

Pertama, kita bisa sedikit membersihkan ekspresi ini dengan menggunakan singkatan kelas karakter untuk digit (perhatikan bahwa ini juga rentan terhadap masalah pelarian yang disebutkan di atas):

[0-9] = \d

Saya akan menggunakan di \dbawah, tetapi perlu diingat bahwa artinya sama dengan [0-9]. (Sebenarnya, di beberapa mesin \dakan mencocokkan angka dari semua skrip, jadi itu akan cocok lebih dari yang [0-9]akan, tapi itu mungkin tidak signifikan dalam kasus Anda.)

Sekarang, jika Anda melihat ini dengan cermat, Anda akan menyadari bahwa setiap bagian dari pola Anda adalah opsional . Pola ini bisa cocok dengan string panjang 0; string hanya terdiri dari +atau -; atau, string yang hanya terdiri dari a .. Ini mungkin bukan yang Anda inginkan.

Untuk memperbaikinya, sebaiknya mulai dengan "menambatkan" ekspresi reguler Anda dengan string minimal yang diperlukan, mungkin satu digit:

\d+

Sekarang kami ingin menambahkan bagian desimal, tetapi tidak sesuai dengan yang Anda pikirkan:

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

Ini akan tetap cocok dengan nilai seperti 123.. Lebih buruk lagi, ada sedikit kejahatan tentang itu. Titik ini opsional, artinya Anda memiliki dua kelas berulang berdampingan ( \d+dan \d*). Ini sebenarnya bisa berbahaya jika digunakan dengan cara yang salah, membuka sistem Anda terhadap serangan DoS.

Untuk memperbaikinya, daripada memperlakukan titik sebagai opsional, kita perlu memperlakukannya sebagai diperlukan (untuk memisahkan kelas karakter yang berulang) dan sebaliknya menjadikan seluruh bagian desimal opsional:

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

Ini terlihat lebih baik sekarang. Kami memerlukan titik antara urutan pertama dan detik, tetapi ada kesalahan fatal: kami tidak bisa mencocokkan .123karena sekarang diperlukan digit terdepan.

Ini sebenarnya cukup mudah untuk diperbaiki. Alih-alih menjadikan bagian "desimal" dari angka tersebut opsional, kita perlu melihatnya sebagai urutan karakter: 1 atau lebih angka yang dapat diawali dengan .yang dapat diawali dengan 0 atau lebih angka:

(\d*\.)?\d+

Sekarang kita tinggal menambahkan tandanya:

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

Tentu saja, garis miring tersebut cukup mengganggu di Java, jadi kita bisa mengganti kelas karakter bentuk panjang kita:

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

Mencocokkan versus Memvalidasi

Ini telah muncul di komentar beberapa kali, jadi saya menambahkan tambahan tentang pencocokan versus memvalidasi.

Tujuan pencocokan adalah untuk menemukan beberapa konten di dalam masukan ("jarum di tumpukan jerami"). Tujuan dari validasi adalah untuk memastikan bahwa masukan dalam format yang diharapkan.

Regexes, menurut sifatnya, hanya cocok dengan teks. Dengan beberapa masukan, mereka akan menemukan beberapa teks yang cocok atau tidak. Namun, dengan "menjepret" ekspresi ke awal dan akhir input dengan tag anchor ( ^dan $), kita dapat memastikan bahwa tidak ada kecocokan yang ditemukan kecuali seluruh input cocok dengan ekspresi tersebut, secara efektif menggunakan regex untuk memvalidasi .

Regex yang dijelaskan di atas ( [+-]?([0-9]*[.])?[0-9]+) akan cocok dengan satu atau beberapa angka dalam string target. Jadi diberi masukan:

apple 1.34 pear 7.98 version 1.2.3.4

Regex akan cocok 1.34, 7.98, 1.2, .3dan .4.

Untuk memvalidasi bahwa masukan yang diberikan adalah angka dan tidak lain adalah angka, "pasang" ekspresi ke awal dan akhir masukan dengan membungkusnya dalam tag jangkar:

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

Ini hanya akan menemukan kecocokan jika seluruh masukan adalah bilangan titik mengambang, dan tidak akan menemukan kecocokan jika masukan berisi karakter tambahan. Jadi, jika diberi masukan 1.2, kecocokan akan ditemukan, tetapi apple 1.2 peartidak ada kecocokan yang akan ditemukan.

Perhatikan bahwa beberapa mesin regex memiliki validate, isMatchatau fungsi serupa, yang pada dasarnya melakukan apa yang telah saya jelaskan secara otomatis, mengembalikan truejika kecocokan ditemukan dan falsejika tidak ada kecocokan yang ditemukan. Juga perlu diingat bahwa beberapa mesin memungkinkan Anda untuk mengatur flag yang mengubah definisi ^dan $, mencocokkan awal / akhir baris, bukan awal / akhir dari seluruh input. Ini biasanya bukan default, tetapi waspadalah terhadap flag-flag ini.

JDB masih mengingat Monica
sumber
2
JDB, terima kasih dan saya harap Anda masih ada! Saya membaca posting Anda di masa mendatang :) Jawaban Anda pasti menangani 0.24 dan 2.2 dan dengan benar melarang 4.2.44 Semua diuji dengan regex101.com Namun, itu melarang 123. yang seperti yang Anda katakan mungkin dapat diterima (dan saya pikir itu adalah!). Saya dapat memperbaiki ini dengan mengubah ekspresi Anda menjadi [- +]? (\ D * [.])? \ D * (perhatikan * di akhir, bukan +), tetapi kemudian hal-hal gila seperti. (contoh kedua Anda) diizinkan. Mau makan kuenya dan memakannya juga?
Dave
2
@Dave -\d+(\.\d*)?|\.\d+
JDB masih mengingat Monica
/[-+]?(\d*[.])?\d+/.test("1.bc") // returns true
yeouuu
1
@yeouuu ya, karena 1.cocok. Tambahkan ^dan $ke awal dan akhir regex jika Anda ingin mencocokkan hanya jika seluruh masukan cocok.
JDB masih mengingat Monica
5
float dapat memiliki eksponen atau menjadi NaN / Inf, jadi saya akan menggunakan ini [-+]?(([0-9]*[.]?[0-9]+([ed][-+]?[0-9]+)?)|(inf)|(nan)):, e / d untuk float / double presisi float. Jangan lupa bendera kasus lipat ke regex
Markus Schmassmann
23

Saya tidak berpikir bahwa jawaban apa pun di halaman ini pada saat penulisan ini benar (juga banyak saran lain di tempat lain di SO juga salah). Masalahnya adalah Anda harus mencocokkan semua kemungkinan berikut:

  • Tidak ada titik desimal (yaitu nilai integer)
  • Digit sebelum dan sesudah titik desimal (misalnya 0.35, 22.165)
  • Hanya digit sebelum koma desimal (mis. 0., 1234.)
  • Hanya digit setelah koma desimal (mis. .0, .5678)

Pada saat yang sama, Anda harus memastikan bahwa setidaknya ada satu digit di suatu tempat, yaitu yang berikut ini tidak diperbolehkan:

  • titik desimalnya sendiri
  • titik desimal bertanda tangan tanpa digit (yaitu +.atau -.)
  • +atau -sendiri
  • string kosong

Ini tampak rumit pada awalnya, tetapi salah satu cara untuk menemukan inspirasi adalah dengan melihat sumber OpenJDK untuk java.lang.Double.valueOf(String)metode tersebut (mulai dari http://hg.openjdk.java.net/jdk8/jdk8/jdk , klik "telusuri", arahkan ke bawah /src/share/classes/java/lang/dan temukan Doublekelasnya). Regex panjang yang berisi kelas ini melayani berbagai kemungkinan yang mungkin tidak ada dalam pikiran OP, tetapi mengabaikan kesederhanaan bagian-bagiannya yang berhubungan dengan NaN, tak terhingga, notasi heksadesimal dan eksponen, dan menggunakan \ddaripada notasi POSIX untuk satu digit, saya dapat mengurangi bagian penting dari regex untuk bilangan floating point bertanda tanpa eksponen ke:

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

Saya tidak berpikir bahwa ada cara untuk menghindari (...)|(...)konstruksi tanpa membiarkan sesuatu yang tidak mengandung angka, atau melarang salah satu kemungkinan yang tidak memiliki angka sebelum koma desimal atau tanpa angka setelahnya.

Jelas dalam praktiknya, Anda harus memenuhi spasi kosong di belakang atau sebelumnya, baik di regex itu sendiri atau dalam kode yang menggunakannya.

pkeller
sumber
Jika Anda menambahkan persyaratan untuk mencocokkan angka seperti 123., maka ya ... sakelar atau adalah satu-satunya solusi, seperti yang saya tunjukkan dalam komentar di posting asli saya.
JDB masih mengingat Monica
1
Ini, dan semua / sebagian besar jawaban lainnya, abaikan bahwa pelampung dapat memiliki eksponen.
NateS
1
@NateS Benar, saya memang menulis "mengabaikan untuk kesederhanaan bagian-bagiannya yang berhubungan dengan NaN, tak terhingga, notasi heksadesimal dan eksponen", karena itu sepertinya cocok dengan cakupan pertanyaan OP. Ada implementasi yang lebih lengkap, termasuk yang saya temukan di kode sumber JDK.
pkeller
1
Dapatkah regex [+-]?((?=\.?\d)\d*\.?\d*)digunakan untuk menghindari pergantian? Ini menggunakan lookahead ...
4esn0k
1
@ 4esn0k regex yang bagus! Saya telah bermain-main dengannya, dan itu berhasil. Saya memiliki dua peringatan: (1) tidak semua mesin regex mendukung pernyataan lebar-nol (meskipun kebanyakan yang modern melakukannya, AFAIK), dan (2) tampilan ke depan hanyalah pergantian dengan nama lain: mesin masih harus mencoba sesuatu dan mundur jika tidak berhasil. Namun, dapatkan suara positif untuk ide yang sangat rapi.
pkeller
7

yang Anda butuhkan adalah:

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

Saya lolos dari tanda "+" dan "-" dan juga mengelompokkan desimal dengan angka berikut karena sesuatu seperti "1." bukan angka yang valid.

Perubahan akan memungkinkan Anda untuk mencocokkan bilangan bulat dan float. sebagai contoh:

0
+1
-2.0
2.23442
DiverseAndRemote.com
sumber
Masalah dengan ungkapan ini adalah hal .1itu tidak diizinkan, meskipun masukan seperti itu secara universal diakui sebagai benar.
JDB masih mengingat Monica
Ini sekarang akan menerima string panjang nol, -dan +, yang bukan angka. Regex itu rumit! :)
JDB masih mengingat Monica
Juga, ini tidak menjawab pertanyaan sebenarnya OP, yang mana itu \.tidak berhasil.
JDB masih mengingat Monica
7

Saya ingin mencocokkan apa yang dianggap kebanyakan bahasa sebagai angka yang valid (integer dan float):

  • '5' / '-5'

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

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

Catatan:

  • 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

Untuk mendukung '1'. dan '.1' kita membutuhkan operator OR ('|') untuk memastikan kita mengecualikan '.' dari pencocokan.

[+-]?+/- sing adalah opsional karena ?berarti 0 atau 1 pertandingan

( karena kita memiliki 2 sub ekspresi kita perlu meletakkannya di dalam tanda kurung

\d+([.]\d*)?(e[+-]?\d+)? Ini untuk angka yang dimulai dengan digit

| memisahkan sub ekspresi

[.]\d+(e[+-]?\d+)? ini untuk angka yang dimulai dengan '.'

) akhir ekspresi

  • Untuk angka yang dimulai dengan '.'

[.] karakter pertama adalah titik (di dalam tanda kurung atau yang lain adalah karakter wildcard)

\d+ satu atau lebih digit

(e[+-]?\d+)? ini adalah notasi ilmiah opsional (0 atau 1 cocok karena diakhiri dengan '?')

  • Untuk angka yang dimulai dengan digit

\d+ satu atau lebih digit

([.]\d*)? opsional kita dapat memiliki karakter titik nol atau lebih digit setelahnya

(e[+-]?\d+)? ini adalah notasi ilmiah opsional

  • Notasi ilmiah

e literal yang menentukan eksponen

[+-]? tanda eksponen opsional

\d+ satu atau lebih digit

Semua itu digabungkan:

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

Untuk menerima Ejuga:

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

( Kasus uji )

Yannis T
sumber
4

Ini adalah sederhana: Anda telah menggunakan Java dan Anda harus menggunakan \\.bukan \.(mencari karakter melarikan diri di Jawa).

yang tak bernama
sumber
Anda mungkin benar ... pesan kesalahan tampak seperti kesalahan sintaks bahasa pemrograman daripada kesalahan pengurai ekspresi reguler.
JDB masih mengingat Monica
3

Yang ini berhasil untuk saya:

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

Anda juga dapat menggunakan yang ini (tanpa parameter bernama):

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

Gunakan beberapa penguji regex online untuk mengujinya (mis. Regex101)

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

Ini akan cocok dengan:

  1. 1.2
  2. 12.3
  3. 1,2
  4. 12,3
Mihai Ciobanu
sumber
Meskipun cuplikan kode ini diterima, dan mungkin memberikan sedikit bantuan, akan sangat ditingkatkan jika menyertakan penjelasan tentang bagaimana dan mengapa ini menyelesaikan masalah. Ingatlah bahwa Anda menjawab pertanyaan untuk pembaca di masa depan, bukan hanya orang yang bertanya sekarang! Harap edit jawaban Anda untuk menambahkan penjelasan, dan berikan indikasi batasan dan asumsi apa yang berlaku.
Toby Speight
oh thnks, saya sedang mencari ini
Serg Burlaka
0
[+-]?(([1-9][0-9]*)|(0))([.,][0-9]+)?

[+-]? - tanda pengantar opsional

(([1-9][0-9]*)|(0)) - bilangan bulat tanpa nol di depan, termasuk nol tunggal

([.,][0-9]+)? - bagian pecahan opsional

Aleksei Gutikov
sumber
1
Berikan info lebih lanjut - bagi orang yang tidak mengetahui regexps itu adalah hyerogliphs. Bagi orang yang mengenal mereka, mereka tidak membutuhkannya.
peterh - Pulihkan Monica
0

Di C ++ menggunakan perpustakaan regex

Jawabannya akan seperti ini:

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

Perhatikan bahwa saya tidak mengambil simbol tanda, jika Anda menginginkannya dengan simbol tanda itu akan menjadi seperti ini:

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

Ini juga memisahkan angka biasa atau angka desimal.

LuisDev99
sumber
0

Dalam notasi c, bilangan float dapat muncul dalam bentuk berikut:

  1. 123
  2. 123.
  3. 123.24
  4. .24
  5. 2e-2 = 2 * 10 kekuatan -2 = 2 * 0,1
  6. 4E + 4 = 4 * 10 kekuatan 4 = 4 * 10.000

Untuk membuat ekspresi reguler float, pertama-tama saya akan membuat "variabel ekspresi reguler int":

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

Sekarang, saya akan menulis potongan kecil ekspresi reguler float - solusinya adalah menggabungkan potongan tersebut dengan atau simbol "|".

Potongan:

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

Solusi akhir (menggabungkan potongan kecil):

(([+-]?{int})|(([+-]?{int})"."[0-9]*)|("."[0-9]*)|([+-]?{int}[eE][+-]?{int})
Zoran Medojević
sumber
-1
[+/-] [0-9]*.[0-9]+

Coba solusi ini.

Lola Gorochana
sumber
-1

untuk javascript

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

Yang akan berhasil untuk 1,23 1234.22 0 0,12 12

Anda dapat mengubah bagian dalam {}untuk mendapatkan hasil yang berbeda dalam panjang desimal dan juga bagian depan desimal. Ini digunakan dalam masukan untuk memasukkan angka dan memeriksa setiap masukan saat Anda mengetik hanya mengizinkan yang lewat.

mjwrazor.dll
sumber