Kontroller, om en streng er numerisk i Java

1. Introduktion

Ofte under drift Snors, skal vi finde ud af, om en Snor er et gyldigt nummer eller ikke.

I denne vejledning undersøger vi flere måder at opdage, om det givne Snor er numerisk, først ved hjælp af almindelig Java, derefter almindelige udtryk og endelig ved hjælp af eksterne biblioteker.

Når vi er færdige med at diskutere forskellige implementeringer, bruger vi benchmarks til at få en idé om, hvilke metoder der er optimale.

2. Forudsætninger

Lad os starte med nogle forudsætninger, inden vi går videre til hovedindholdet.

I den sidste del af denne artikel bruger vi Apache Commons eksterne bibliotek, som vi vil tilføje sin afhængighed i vores pom.xml:

 org.apache.commons commons-lang3 3.9 

Den seneste version af dette bibliotek kan findes på Maven Central.

3. Brug af almindelig Java

Måske den nemmeste og mest pålidelige måde at kontrollere, om en Snor er numerisk eller ikke er ved at parsere det ved hjælp af Java's indbyggede metoder:

  1. Integer.parseInt (String)
  2. Float.parseFloat (String)
  3. Double.parseDouble (streng)
  4. Long.parseLong (String)
  5. nyt BigInteger (String)

Hvis disse metoder ikke kaster nogen NumberFormatException, så betyder det, at parsingen var vellykket, og at Snor er numerisk:

public static boolean isNumeric (String strNum) {if (strNum == null) {return false; } prøv {dobbelt d = Double.parseDouble (strNum); } catch (NumberFormatException nfe) {return false; } returner sandt }

Lad os se denne metode i aktion:

assertThat (isNumeric ("22")). isTrue (); assertThat (isNumeric ("5.05")). isTrue (); assertThat (isNumeric ("- 200")). isTrue (); assertThat (isNumeric ("10.0d")). isTrue (); assertThat (isNumeric ("22")). isTrue (); assertThat (isNumeric (null)). isFalse (); assertThat (isNumeric ("")). isFalse (); assertThat (isNumeric ("abc")). isFalse ();

I vores isNumeric () metode, vi kontrollerer bare for værdier, der er af typen Dobbelt, men denne metode kan også ændres for at kontrollere Heltal, Flyde, Lang og stort antal ved hjælp af en af ​​de analyseringsmetoder, som vi tidligere har tilmeldt.

Disse metoder diskuteres også i artiklen Java String Conversions.

4. Brug af regulære udtryk

Lad os nu bruge regex -? \ d + (\. \ d +)? for at matche numerisk Strenge bestående af det positive eller negative heltal og flyder.

Men det siger sig selv, at vi helt sikkert kan ændre dette regex for at identificere og håndtere en bred vifte af regler. Her holder vi det enkelt.

Lad os nedbryde denne regex og se, hvordan det fungerer:

  • -? - denne del identificerer, hvis det givne tal er negativt, bindestreg “”Søger efter bindestreg bogstaveligt og spørgsmålstegnet“?”Markerer dets tilstedeværelse som en valgfri
  • \ d + - dette søger efter et eller flere cifre
  • (\. \ d +)? - denne del af regex er at identificere floatnumre. Her søger vi efter et eller flere cifre efterfulgt af en periode. Spørgsmålstegnet betyder i sidste ende, at denne komplette gruppe er valgfri

Regulære udtryk er et meget bredt emne. For at få en kort oversigt, se vores vejledning om Java regulære udtryk API.

Lad os nu oprette en metode ved hjælp af ovenstående regulære udtryk:

privat mønster mønster = Pattern.compile ("-? \ d + (\. \ d +)?"); public boolean isNumeric (String strNum) {if (strNum == null) {return false; } returner mønster. matcher (strNum). matcher (); }

Lad os nu se på nogle påstande om ovenstående metode:

assertThat (isNumeric ("22")). isTrue (); assertThat (isNumeric ("5.05")). isTrue (); assertThat (isNumeric ("- 200")). isTrue (); assertThat (isNumeric (null)). isFalse (); assertThat (isNumeric ("abc")). er Falsk ();

5. Brug af Apache Commons

I dette afsnit diskuterer vi forskellige metoder, der er tilgængelige i Apache Commons-biblioteket.

5.1. NumberUtils.isCreatable (String)

NumberUtils fra Apache Commons giver en statisk metode NumberUtils.isCreatable (String) der kontrollerer, om en Snor er et gyldigt Java-nummer eller ej.

Denne metode accepterer:

  1. Hexadecimale tal, der starter med 0x eller 0X
  2. Oktaltal, der starter med et førende 0
  3. Videnskabelig notation (for eksempel 1.05e-10)
  4. Tal markeret med en typekvalifikator (for eksempel 1L eller 2.2d)

Hvis den medfølgende streng er nul eller tom / tom, så betragtes det ikke som et tal, og metoden vender tilbage falsk.

Lad os køre nogle tests ved hjælp af denne metode:

assertThat (NumberUtils.isCreatable ("22")). isTrue (); assertThat (NumberUtils.isCreatable ("5.05")). isTrue (); assertThat (NumberUtils.isCreatable ("- 200")). isTrue (); assertThat (NumberUtils.isCreatable ("10.0d")). isTrue (); assertThat (NumberUtils.isCreatable ("1000L")). erTrue (); assertThat (NumberUtils.isCreatable ("0xFF")). isTrue (); assertThat (NumberUtils.isCreatable ("07")). isTrue (); assertThat (NumberUtils.isCreatable ("2.99e + 8")). isTrue (); assertThat (NumberUtils.isCreatable (null)). isFalse (); assertThat (NumberUtils.isCreatable ("")). er Falsk (); assertThat (NumberUtils.isCreatable ("abc")). er Falsk (); assertThat (NumberUtils.isCreatable ("22")). er Falsk (); assertThat (NumberUtils.isCreatable ("09")). er Falsk ();

Bemærk hvordan vi får det rigtigt påstande om heksadesimale tal, oktale tal og videnskabelige notationer i henholdsvis linie 6, 7 og 8.

Også på linje 14 strengen “09” vender tilbage falsk fordi det foregående “0” angiver, at dette er et oktalt tal og “09” er ikke et gyldigt oktalt tal.

For hvert input, der vender tilbage rigtigt med denne metode kan vi bruge NumberUtils.createNumber (streng) hvilket giver os det gyldige nummer.

5.2. NumberUtils.isParsable (streng)

Det NumberUtils.isParsable (streng) metode kontrollerer, om den givne Snor er parsabel eller ej.

Parsable numre er dem, der er parset med succes ved en hvilken som helst analysemetode som Integer.parseInt (String), Long.parseLong (String), Float.parseFloat (String) eller Double.parseDouble (streng).

I modsætning til NumberUtils.isCreatable (), denne metode accepterer ikke hexadecimale tal, videnskabelige notationer eller strenge, der slutter med nogen type kvalifikator, det vil sige 'F', 'F', 'd', 'D', 'l'eller'L'.

Lad os se på nogle af bekræftelser:

assertThat (NumberUtils.isParsable ("22")). isTrue (); assertThat (NumberUtils.isParsable ("- 23")). isTrue (); assertThat (NumberUtils.isParsable ("2.2")). isTrue (); assertThat (NumberUtils.isParsable ("09")). isTrue (); assertThat (NumberUtils.isParsable (null)). isFalse (); assertThat (NumberUtils.isParsable ("")). er Falsk (); assertThat (NumberUtils.isParsable ("6.2f")). isFalse (); assertThat (NumberUtils.isParsable ("9.8d")). isFalse (); assertThat (NumberUtils.isParsable ("22L")). er Falsk (); assertThat (NumberUtils.isParsable ("0xFF")). isFalse (); assertThat (NumberUtils.isParsable ("2.99e + 8")). er Falsk ();

På linje 4, i modsætning til NumberUtils.isCreatable (), tallet, der starter med streng “0” betragtes ikke som et oktalt tal, men et normalt decimaltal, og derfor returneres det sandt.

Vi kan bruge denne metode som erstatning for det, vi gjorde i afsnit 3, hvor vi prøver at analysere et nummer og kontrollere for en fejl.

5.3. StringUtils.isNumeric (CharSequence)

Metoden StringUtils.isNumeric (CharSequence) kontrollerer strengt Unicode-cifre. Det betyder:

  1. Alle cifre fra ethvert sprog, der er et Unicode-tal, er acceptabelt
  2. Da et decimaltegn ikke betragtes som et Unicode-tal, er det ikke gyldigt
  3. Ledende tegn (enten positive eller negative) er heller ikke acceptable

Lad os nu se denne metode i aktion:

assertThat (StringUtils.isNumeric ("123")). erTrue (); assertThat (StringUtils.isNumeric ("١٢٣")). erTrue (); assertThat (StringUtils.isNumeric ("१२३")). erTrue (); assertThat (StringUtils.isNumeric (null)). er Falsk (); assertThat (StringUtils.isNumeric ("")). er Falsk (); assertThat (StringUtils.isNumeric ("")). er Falsk (); assertThat (StringUtils.isNumeric ("12 3")). er Falsk (); assertThat (StringUtils.isNumeric ("ab2c")). er Falsk (); assertThat (StringUtils.isNumeric ("12.3")). er Falsk (); assertThat (StringUtils.isNumeric ("- 123")). er Falsk ();

Bemærk, at inputparametrene i linje 2 og 3 repræsenterer tal 123 på henholdsvis arabisk og devanagari. Da de er gyldige Unicode-cifre, returneres denne metode rigtigt på dem.

5.4. StringUtils.isNumericSpace (CharSequence)

Det StringUtils.isNumericSpace (CharSequence) kontrollerer strengt Unicode-cifre og / eller mellemrum. Dette er det samme som StringUtils.isNumeric () med den eneste forskel, at den også accepterer mellemrum, ikke kun ledende og bageste mellemrum, men også hvis de er mellem tal:

assertThat (StringUtils.isNumericSpace ("123")). isTrue (); assertThat (StringUtils.isNumericSpace ("١٢٣")). isTrue (); assertThat (StringUtils.isNumericSpace ("")). isTrue (); assertThat (StringUtils.isNumericSpace ("")). isTrue (); assertThat (StringUtils.isNumericSpace ("12 3")). isTrue (); assertThat (StringUtils.isNumericSpace (null)). isFalse (); assertThat (StringUtils.isNumericSpace ("ab2c")). isFalse (); assertThat (StringUtils.isNumericSpace ("12.3")). er Falsk (); assertThat (StringUtils.isNumericSpace ("- 123")). er Falsk ();

6. Benchmarks

Før vi afslutter denne artikel, lad os gennemgå nogle benchmarkresultater for at hjælpe os med at analysere, hvilken af ​​de ovennævnte metoder, der er bedst til vores brugssag.

6.1. Simpel benchmark

For det første tager vi en enkel tilgang. Vi vælger en strengværdi - til vores test bruger vi Heltal.MAX_VALUE. Derefter testes denne værdi i forhold til alle vores implementeringer:

Benchmark Mode Cnt Score Error Units Benchmarking.usingCoreJava avgt 20 57.241 ± 0.792 ns / op Benchmarking.usingNumberUtils_isCreatable avgt 20 26.711 ± 1.110 ns / op Benchmarking.usingNumberUtils_isParsable avgt 20 46.577 ± 1.973 ns / op Benchmarking Benchmarking.usingStringUtils_isNumeric avgt 20 35.885 ± 1.691 ns / op Benchmarking.usingStringUtils_isNumericSpace avgt 20 31.979 ± 1.393 ns / op

Som vi ser er de dyreste operationer regulære udtryk. Derefter er vores centrale Java-baserede løsning.

Desuden skal du bemærke, at operationerne ved hjælp af Apache Commons-biblioteket stort set er de samme.

6.2. Forbedret benchmark

Lad os bruge et mere forskelligt sæt tests til et mere repræsentativt benchmark:

  • 95 værdier er numeriske (0-94 og Heltal.MAX_VALUE)
  • 3 indeholder tal, men er stadig forkert formateret - ‘x0‘, ‘0..005′, og '–11
  • 1 indeholder kun tekst
  • 1 er en nul

Efter udførelse af de samme tests ser vi resultaterne:

Benchmark Mode Cnt Score Error Units Benchmarking.usingCoreJava avgt 20 10162.872 ± 798.387 ns / op Benchmarking.usingNumberUtils_isCreatable avgt 20 1703.243 ± 108.244 ns / op Benchmarking.usingNumberUtils_isParsable avgt 20 1589.915 ± 203.052 ns. Benchmarking.usingStringUtils_isNumeric avgt 20 1071.753 ± 8.657 ns / op Benchmarking.usingStringUtils_isNumericSpace avgt 20 1157.722 ± 24.139 ns / op

Den vigtigste forskel er, at to af vores tests - løsningen med regulære udtryk og den kernebaserede Java-løsning - har byttet plads.

Af dette resultat lærer vi at kaste og håndtere NumberFormatException, som kun forekommer i 5% af tilfældene, har en relativt stor indvirkning på den samlede præstation. Så vi konkluderer, at den optimale løsning afhænger af vores forventede input.

Vi kan også med sikkerhed konkludere, at vi skal bruge metoderne fra Commons-biblioteket eller en metode, der er implementeret på samme måde for optimal ydelse.

7. Konklusion

I denne artikel undersøgte vi forskellige måder at finde ud af, om en Snor er numerisk eller ikke. Vi kiggede på begge løsninger - indbyggede metoder og også eksterne biblioteker.

Som altid kan implementeringen af ​​alle eksempler og kodestykker, der er angivet ovenfor, inklusive den kode, der bruges til at udføre benchmarks, findes på GitHub.