Kayan noktalı sayılar için normal ifade


116

Kayan nokta sayılarını eşleştirmek için bir görevim var. Bunun için şu normal ifadeyi yazdım:

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

Ancak bir hata döndürür:

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

Bildiğim gibi, bir kaçış karakteri .de kullanmamız gerekiyor . Lütfen yanlış olduğum yerde düzeltin.


10
Bu normal ifade hangi dilde kullanılıyor?
CaffGeek

3
@JDB - Neden bir sayı / float normal ifadesi için 100 puan veriyorsunuz? Standart her zaman (?:\d+(?:\.\d*)?|\.\d+)SO ...


1
[-+]?([0-9]*[.])?[0-9]+([eE][-+]?\d+)?üstel notasyonu da yakalamak istiyorsanız, e, g, 3.023e-23
wcochran

Java veya C ++ gibi bazı dillerde ters eğik çizgiden kaçınılmalıdır. Yani "\." Normal ifadesini almak için "\\." Dizesini kullanırsınız. Python, ham dizeleri kullanarak bunu aşar.
HackerBoss

Yanıtlar:


259

TL; DR

Kullanım [.]yerine \.ve [0-9]yerine \d(Java gibi) bazı dillerde sorunları kaçan önlemek için.

Bunu başlangıçta tanıdığı için isimsiz olana teşekkürler .

Bir kayan nokta numarasını eşleştirmek için nispeten basit bir model ,

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

Bu eşleşecek:

  • 123
  • 123.456
  • .456

Çalışan bir örneğe bakın

Ayrıca eşleştirme istiyorsanız 123.(ondalık kısmı olmayan bir nokta), biraz daha uzun bir ifadeye ihtiyacınız olacak:

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

Bu modelin daha kapsamlı bir açıklaması için pkeller'in cevabına bakın

Onaltılık ve sekizlik gibi ondalık olmayan sayıları eklemek istiyorsanız, bir dizenin sayı olup olmadığını nasıl anlarım? .

Bir girişin bir sayı olduğunu doğrulamak istiyorsanız (giriş içinde bir sayı bulmak yerine), o zaman kalıbı ^ve ile şöyle çevrelemelisiniz $:

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

Düzensiz Normal İfadeler

Çoğu modern dilde, API'lerde, çerçevelerde, kitaplıklarda vb. Uygulanan "düzenli ifadeler", biçimsel dil teorisinde geliştirilen bir kavrama dayanmaktadır . Ancak, yazılım mühendisleri bu uygulamaları resmi tanımın çok ötesine taşıyan birçok uzantı eklediler. Dolayısıyla, çoğu düzenli ifade motoru birbirine benzese de, aslında bir standart yoktur. Bu nedenle, birçok şey kullandığınız dil, API, çerçeve veya kitaplığa bağlıdır.

(Bu arada, kafa karışıklığını azaltmaya yardımcı olmak için, birçok kişi bu gelişmiş eşleşen dilleri tanımlamak için " regex " veya " regexp " kullanmayı tercih etti. Daha fazla bilgi için RexEgg.com'da Normal İfade Normal İfade ile Aynı mı? Konusuna bakın.)

Bununla birlikte, çoğu normal ifade motoru (aslında, bildiğim kadarıyla hepsi) kabul ederdi \.. Büyük olasılıkla, kaçışla ilgili bir sorun var.

Kaçmanın Sıkıntısı

JavaScript gibi bazı dillerde normal ifadeler için yerleşik destek bulunur . Yapmayan diller için kaçış bir sorun olabilir.

Bunun nedeni, temelde bir dilde bir dilde kodlama yapıyor olmanızdır. Örneğin Java, \dizelerinde bir kaçış karakteri olarak kullanır , bu nedenle bir dizgeye değişmez bir ters eğik çizgi karakteri yerleştirmek istiyorsanız, ondan kaçmanız gerekir:

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

Bununla birlikte, normal ifadeler kaçış için karakteri de kullanır; \bu nedenle, bir değişmez \karakterle eşleştirmek istiyorsanız , normal ifade motoru için ondan kaçmalı ve ardından Java için tekrar çıkış yapmalısınız:

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

Sizin durumunuzda, muhtemelen programlama yaptığınız dilde ters eğik çizgi karakterinden kaçmadınız:

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

Tüm bu kaçışlar çok kafa karıştırıcı olabilir. Çalıştığınız dil ham dizeleri destekliyorsa , ters eğik çizgi sayısını azaltmak için bunları kullanmalısınız, ancak tüm diller bunu yapmaz (en önemlisi: Java). Neyse ki, bazen işe yarayacak bir alternatif var:

String correctPattern = "[.]";

Bir normal ifade motoru için \.ve [.]tamamen aynı anlama gelir. Bunun yeni satır ( \\n), açık köşeli parantez ( \\[) ve ters eğik çizgi ( \\\\veya [\\]) gibi her durumda işe yaramayacağını unutmayın .

Eşleşen Numaralar Hakkında Bir Not

(İpucu: Düşündüğünüzden daha zor)

Bir sayıyı eşleştirmek, normal ifadeyle oldukça kolay olduğunu düşündüğünüz şeylerden biridir, ancak aslında oldukça zordur. Yaklaşımınıza parça parça bir göz atalım:

[-+]?

İsteğe bağlı bir -veya+

[0-9]*

0 veya daha fazla ardışık basamağı eşleştir

\.?

İsteğe bağlı bir eşleştir .

[0-9]*

0 veya daha fazla ardışık basamağı eşleştir

İlk olarak, rakamlar için bir karakter sınıfı kısaltması kullanarak bu ifadeyi biraz temizleyebiliriz (bunun ayrıca yukarıda bahsedilen kaçış sorununa da açık olduğunu unutmayın):

[0-9] = \d

\dAşağıda kullanacağım , ancak bununla aynı anlama geldiğini unutmayın [0-9]. (Aslında, bazı motorlarda \dtüm komut dosyalarındaki rakamlarla eşleşecek, bu yüzden istenenden daha fazla eşleşecek [0-9], ancak bu muhtemelen sizin durumunuzda önemli değil.)

Şimdi, buna dikkatlice bakarsanız, kalıbınızın her bir parçasının isteğe bağlı olduğunu fark edeceksiniz . Bu desen, 0 uzunluklu bir dizeyle eşleşebilir; yalnızca +veya içeren bir dize -; veya yalnızca a'dan oluşan bir dize .. Muhtemelen amaçladığınız şey bu değil.

Bunu düzeltmek için, normal ifadenizi gerekli minimum dizeyle, muhtemelen tek bir rakamla "sabitleyerek" başlamak yararlıdır:

\d+

Şimdi ondalık kısmı eklemek istiyoruz, ancak düşündüğünüz yere gitmiyor:

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

Bu, gibi değerlerle eşleşmeye devam edecek 123.. Daha da kötüsü, bunda bir miktar kötülük var. Nokta isteğe bağlıdır, yani yan yana ( \d+ve \d*) tekrarlanan iki sınıfınız vardır . Bu aslında yanlış bir şekilde kullanılırsa tehlikeli olabilir ve sisteminizi DoS saldırılarına açık hale getirir.

Bunu düzeltmek için, noktayı isteğe bağlı olarak ele almak yerine, gerektiği gibi ele almalıyız (tekrarlanan karakter sınıflarını ayırmak için) ve bunun yerine tüm ondalık kısmı isteğe bağlı yapmalıyız:

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

Bu şimdi daha iyi görünüyor. İlk basamak dizisi ile ikincisi arasında bir süreye ihtiyacımız var, ancak ölümcül bir kusur var: eşleşemiyoruz .123çünkü artık bir ön basamak gerekli.

Bunu düzeltmek aslında oldukça kolaydır. Sayının "ondalık" kısmını isteğe bağlı yapmak yerine, ona bir karakter dizisi olarak bakmalıyız: 1 veya daha fazla sayı, .önüne 0 veya daha fazla sayı eklenebilen bir ön ek olabilir:

(\d*\.)?\d+

Şimdi sadece işareti ekliyoruz:

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

Elbette, bu eğik çizgiler Java'da oldukça can sıkıcıdır, bu nedenle uzun biçimli karakter sınıflarımızda bunların yerini alabiliriz:

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

Eşleştirme ve Doğrulama

Bu, yorumlarda birkaç kez gündeme geldi, bu yüzden eşleştirme ve doğrulama konusunda bir ek ekliyorum.

Eşleştirmenin amacı , girdi içindeki bazı içerikleri bulmaktır ("samanlıkta iğne"). Doğrulamanın amacı , girdinin beklenen bir formatta olmasını sağlamaktır.

Normal ifadeler, doğaları gereği yalnızca metinle eşleşir . Bazı girdiler verildiğinde, ya eşleşen bir metin bulacaklar ya da bulamayacaklar. Bununla birlikte, bir ifadeyi girişin başına ve sonuna tutturma etiketleriyle ( ^ve $) "yapıştırarak", girişin tamamı ifadeyle eşleşmedikçe hiçbir eşleşme bulunmamasını sağlayabilir ve bu da doğrulamak için düzenli ifadeleri etkili bir şekilde kullanabiliriz .

Yukarıda ( [+-]?([0-9]*[.])?[0-9]+) açıklanan normal ifade, bir hedef dizedeki bir veya daha fazla sayıyla eşleşir . Yani girdi verildiğinde:

apple 1.34 pear 7.98 version 1.2.3.4

Regex maç olacak 1.34, 7.98, 1.2, .3ve .4.

Verilen bir girdinin bir sayı olduğunu ve sayıdan başka bir şey olmadığını doğrulamak için, bağlantı etiketlerine sararak ifadeyi girdinin başına ve sonuna "yapıştırın":

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

Bu, yalnızca girişin tamamı bir kayan noktalı sayı ise bir eşleşme bulur ve giriş ek karakterler içeriyorsa bir eşleşme bulmaz. Dolayısıyla, girdi verildiğinde 1.2, bir eşleşme bulunacak, ancak apple 1.2 pearhiçbir eşleşme bulunmayacaktır.

Bazı regex motorlar olması Not validate, isMatchesasen ben dönmeden, otomatik tarif ettik yapar ki, ya da benzer işlevi truebir eşleşme bulunursa ve falseeşleşme bulunursa. Ayrıca bazı motorların , tüm girdinin başlangıcı / bitişi yerine bir satırın başlangıcını / sonunu eşleştiren ^ve tanımını değiştiren bayraklar ayarlamanıza izin verdiğini unutmayın $. Bu genellikle varsayılan değildir, ancak bu bayraklara dikkat edin.


2
JDB, teşekkürler ve umarım hala buralardasındır! Gelecekte gönderinizi okuyorum :) Cevabınız kesinlikle 0.24 ve 2.2 ile ilgileniyor ve 4.2.44'e tamamen izin vermiyor. Hepsi regex101.com ile test edildi Ancak, 123'e izin vermiyor. dır-dir!). İfadenizi [- +]? (\ D * [.])? \ D * (+ yerine sonunda * dikkat edin) olarak değiştirerek bunu düzeltebilirim ama sonra çılgınca şeyler. (ikinci örneğiniz) izin verilir. Her neyse, pastamı alıp da yemeye ne dersin?
Dave

2
@Dave -\d+(\.\d*)?|\.\d+
JDB, Monica'yı

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

1
@yeouuu evet, çünkü 1.eşleşiyor. Yalnızca girdinin tamamı eşleşirse eşleştirmek istiyorsanız normal ifadenin başına ve sonuna ^ve ekleyin $.
JDB hala Monica

5
float'ların üsleri olabilir veya NaN / Inf olabilir, bu yüzden şunu kullanırdım:, [-+]?(([0-9]*[.]?[0-9]+([ed][-+]?[0-9]+)?)|(inf)|(nan))float / double hassasiyet float için e / d.
Normal ifadeye

23

Yazma sırasında bu sayfadaki yanıtların hiçbirinin doğru olduğunu sanmıyorum (ayrıca SO'daki diğer birçok öneri de yanlıştır). Sorun şu ki, aşağıdaki olasılıkların tümünü eşleştirmeniz gerekiyor:

  • Ondalık nokta yok (yani bir tamsayı değeri)
  • Ondalık noktadan önce ve sonra rakamlar (ör 0.35. 22.165)
  • Yalnızca ondalık noktadan önceki rakamlar (örneğin 0., 1234.)
  • Yalnızca ondalık noktadan sonraki rakamlar (örneğin .0, .5678)

Aynı zamanda, bir yerde en az bir rakam olduğundan emin olmalısınız, yani aşağıdakilere izin verilmez:

  • kendi başına bir ondalık nokta
  • rakam içermeyen işaretli bir ondalık nokta (yani +.veya -.)
  • +veya -kendi başlarına
  • boş bir dize

Bu ilk bakışta zor görünebilir, ancak ilham almanın bir yolu, java.lang.Double.valueOf(String)yöntem için OpenJDK kaynağına bakmaktır ( http://hg.openjdk.java.net/jdk8/jdk8/jdk adresinden başlayın, "göz at" ı tıklayın, aşağı gidin /src/share/classes/java/lang/ve Doublesınıfı bulun ). Bu sınıfın içerdiği uzun normal ifade, OP'nin muhtemelen aklında olmayan çeşitli olasılıklara hitap eder, ancak basitlik için NaN, sonsuzluk, Onaltılık gösterimler ve üslerle ilgili kısımlarını görmezden \dgelir ve POSIX gösterimi yerine kullanmak tek basamaklı ise, işaretli kayan noktalı sayı için normal ifadenin önemli kısımlarını üssüz olarak azaltabilirim:

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

(...)|(...)Basamak içermeyen bir şeye izin vermeden veya ondalık basamağından önce basamak içermeyen veya ondan sonra basamak bulunmayan olasılıklardan birini yasaklamadan inşaattan kaçınmanın bir yolu olduğunu düşünmüyorum .

Açıkçası, pratikte, ya normal ifadenin kendisinde ya da onu kullanan kodda, sondaki ya da önceki boşlukları karşılamanız gerekecektir.


Gibi sayıları eşleştirme şartını eklerseniz 123., o zaman evet ... veya anahtarı, orijinal yazımdaki bir yorumda belirttiğim gibi tek çözümdür .
JDB, Monica'yı

1
Bu ve diğer tüm / çoğu yanıt, bir kayan noktanın bir üssüne sahip olabileceğini görmezden gelir.
NateS

1
@NateS Doğru, "basitlik adına NaN, sonsuz, Onaltılık gösterim ve üslerle ilgili kısımlarını görmezden gelerek" yazdım, çünkü bu OP'nin sorusunun kapsamına uygun görünüyor. JDK kaynak kodunda bulduğum da dahil olmak üzere daha eksiksiz uygulamalar var.
pkeller

1
Normal [+-]?((?=\.?\d)\d*\.?\d*)ifade, değişiklikten kaçınmak için kullanılabilir mi? Bir
önden okuma

1
@ 4esn0k Güzel normal ifade! Onunla oynadım ve işe yarıyor. İki uyarım var: (1) tüm normal ifade motorları sıfır genişlikli iddiaları desteklemiyor (modern olanların çoğu desteklese de, AFAIK) ve (2) ileriye bakış sadece başka bir adın alternatifidir: motor hala bir şeyler denemek zorundadır ve işe yaramazsa geriye dönün. Yine de çok güzel bir fikir için olumlu oy verin.
pkeller

7

ihtiyacın olan şey:

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

"+" Ve "-" işaretinden kaçtım ve ayrıca ondalık sayıları "1" gibi bir şeyden beri takip eden rakamlarla gruplandırdım. Geçerli bir numara değil.

Değişiklikler, tam sayıları ve kayan sayıları eşleştirmenize izin verecektir. Örneğin:

0
+1
-2.0
2.23442

Bu ifadeyle ilgili sorun .1, evrensel olarak doğru olarak kabul edilmesine rağmen, buna izin verilmeyecek olmasıdır.
JDB,

Bu artık sıfır uzunluktaki dizeleri kabul edecek -ve +sayı olmayan dizeler . Regex aldatıcıdır! :)
JDB

Ayrıca, bu OP'nin gerçek sorusuna cevap vermiyor, ki \.bu işe yaramıyor.
JDB,

7

Çoğu dilin geçerli sayılar olarak kabul ettiği şeyleri eşleştirmek istiyorum (tam sayı ve kayan sayılar):

  • '5' / '-5'

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

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

Notlar:

  • 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

Hem '1'i desteklemek için. ve '.1', '.' dışladığımızdan emin olmak için bir OR operatörüne ('|') ihtiyacımız var. eşleşmeden.

[+-]?+/- şarkı isteğe bağlıdır çünkü ?0 veya 1 eşleşme anlamına gelir

( 2 alt ifademiz olduğu için bunları parantez içine koymamız gerekir

\d+([.]\d*)?(e[+-]?\d+)? Bu, bir rakamla başlayan numaralar içindir

| alt ifadeleri ayırır

[.]\d+(e[+-]?\d+)? bu "ile başlayan sayılar içindir."

) ifadelerin sonu

  • "." İle başlayan sayılar için

[.] ilk karakter noktadır (köşeli parantez içinde veya bir joker karakterdir)

\d+ bir veya daha fazla rakam

(e[+-]?\d+)? bu isteğe bağlı ('?' bitmesi nedeniyle 0 veya 1 eşleşme) bilimsel gösterimdir

  • Bir rakamla başlayan numaralar için

\d+ bir veya daha fazla rakam

([.]\d*)? isteğe bağlı olarak bir nokta karakterimiz ve ondan sonra sıfır veya daha fazla rakam olabilir

(e[+-]?\d+)? bu isteğe bağlı bilimsel bir gösterimdir

  • Bilimsel gösterim

e üs belirten değişmez bilgi

[+-]? isteğe bağlı üs işareti

\d+ bir veya daha fazla rakam

Bunların tümü birleştirildi:

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

Kabul etmek için E:

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

( Test durumları )


4

Bu basit: Java kullandınız ve \\.bunun yerine kullanmalısınız \.(Java'da karakter kaçışını arayın).


Muhtemelen haklısınız ... hata mesajı bir normal ifade ayrıştırıcı hatası yerine bir programlama dili sözdizimi hatası gibi görünüyor.
JDB,

3

Bu benim için çalıştı:

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

Bunu da kullanabilirsiniz (adlandırılmış parametre olmadan):

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

Test etmek için birkaç çevrimiçi normal ifade test kullanıcısı kullanın (ör. Regex101)


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

Bu eşleşecek:

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

Bu kod parçacığı açığız ve bazı yardım sağlamak görülebilir fakat bunun olacağını bunun bir açıklama dahil eğer büyük ölçüde geliştirilmiş bir nasıl ve niçin bu çözer sorunu. Sadece şimdi soran kişi değil, gelecekte okuyucular için soruyu cevapladığınızı unutmayın! Lütfen açıklama eklemek için cevabınızı düzenleyin ve hangi sınırlamaların ve varsayımların geçerli olduğuna dair bir gösterge verin.
Toby Speight

oh teşekkürler, bunun için lokking yapıyorum
Serg Burlaka

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

[+-]? - isteğe bağlı önde gelen işaret

(([1-9][0-9]*)|(0)) - tek sıfır dahil olmak üzere başında sıfır olmadan tam sayı

([.,][0-9]+)? - isteğe bağlı kesirli bölüm


1
Daha fazla bilgi verin - regexp'leri bilmeyen insanlar için bu hyerogliflerdir. Onları tanıyan insanlar için buna ihtiyaçları yok.
peterh - Monica'yı eski durumuna getir

0

C ++ 'da regex kitaplığını kullanarak

Cevap şöyle olacaktı:

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

İşaret sembolünü almadığıma dikkat edin, eğer onu işaret sembolü ile isterseniz, bununla ilgili olacaktır:

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

Bu aynı zamanda normal bir sayıyı veya ondalık bir sayıyı ayırır.


0

C gösterimde, kayan sayı aşağıdaki şekillerde ortaya çıkabilir:

  1. 123
  2. 123.
  3. 123,24
  4. 0,24
  5. 2e-2 = 2 * 10 pow -2 = 2 * 0.1
  6. 4E + 4 = 4 * 10 üs 4 = 4 * 10000

Float düzenli ifade oluşturmak için önce "int düzenli ifade değişkeni" oluşturacağım:

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

Şimdi, küçük float düzenli ifadeler yazacağım - çözüm bu parçaları veya simbol "|" ile birleştirmektir.

Chunks:

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

Nihai çözüm (küçük parçaları birleştirmek):

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


-1

javascript için

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

1.23 için çalışan 1234.22 0 0.12 12

{}Ondalık uzunlukta ve ondalık basamağın önünde farklı sonuçlar elde etmek için içindeki parçaları değiştirebilirsiniz . Bu, girişlerde sayı girmek ve siz yazarken her girişi kontrol etmek için kullanılır ve yalnızca geçenlere izin verir.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.