Überprüfen Sie, ob eine Zeichenfolge in Java numerisch ist

1. Einleitung

Oft während des Betriebs auf String s, müssen wir herausfinden, ob ein String eine gültige Zahl ist oder nicht.

In diesem Lernprogramm werden verschiedene Möglichkeiten untersucht, um festzustellen, ob die angegebene Zeichenfolge numerisch ist. Verwenden Sie zunächst einfaches Java, dann reguläre Ausdrücke und schließlich externe Bibliotheken.

Sobald wir verschiedene Implementierungen besprochen haben, verwenden wir Benchmarks, um eine Vorstellung davon zu bekommen, welche Methoden optimal sind.

2. Voraussetzungen

Beginnen wir mit einigen Voraussetzungen, bevor wir zum Hauptinhalt übergehen.

Im letzten Teil dieses Artikels verwenden wir die externe Apache Commons-Bibliothek, für die wir ihre Abhängigkeit in unserer pom.xml hinzufügen :

 org.apache.commons commons-lang3 3.9 

Die neueste Version dieser Bibliothek finden Sie auf Maven Central.

3. Verwenden von Plain Java

Der einfachste und zuverlässigste Weg, um zu überprüfen, ob ein String numerisch ist oder nicht, besteht darin, ihn mit den in Java integrierten Methoden zu analysieren:

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

Wenn diese Methoden keine werfen Number , dann bedeutet es , dass das Parsen erfolgreich war und der String numerisch ist:

public static boolean isNumeric(String strNum) { if (strNum == null) { return false; } try { double d = Double.parseDouble(strNum); } catch (NumberFormatException nfe) { return false; } return true; }

Lassen Sie uns diese Methode in Aktion sehen:

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();

In unserer isNumeric () -Methode suchen wir nur nach Werten vom Typ Double . Diese Methode kann jedoch auch geändert werden, um mithilfe einer der zuvor eingetragenen Analysemethoden nach Integer- , Float- , Long- und Large-Zahlen zu suchen .

Diese Methoden werden auch im Artikel Java String Conversions erläutert.

4. Verwenden regulärer Ausdrücke

Verwenden wir jetzt Regex -? \ D + (\. \ D +)? um numerische Zeichenfolgen abzugleichen, die aus der positiven oder negativen Ganzzahl und den Gleitkommazahlen bestehen.

Es versteht sich jedoch von selbst, dass wir diesen regulären Ausdruck definitiv ändern können, um eine Vielzahl von Regeln zu identifizieren und zu handhaben. Hier halten wir es einfach.

Lassen Sie uns diesen regulären Ausdruck aufschlüsseln und sehen, wie er funktioniert:

  • -? - Dieser Teil gibt an, ob die angegebene Zahl negativ ist. Der Bindestrich „ - “ sucht buchstäblich nach dem Bindestrich und das Fragezeichen „ ? ”Markiert seine Anwesenheit als optional
  • \ d + - Dies sucht nach einer oder mehreren Ziffern
  • (\. \ d +)? - Dieser Teil von Regex dient zur Identifizierung von Float-Nummern. Hier suchen wir nach einer oder mehreren Ziffern, gefolgt von einem Punkt. Das Fragezeichen am Ende bedeutet, dass diese vollständige Gruppe optional ist

Reguläre Ausdrücke sind ein sehr breites Thema. Um einen kurzen Überblick zu erhalten, lesen Sie unser Tutorial zur Java-API für reguläre Ausdrücke.

Lassen Sie uns zunächst eine Methode mit dem obigen regulären Ausdruck erstellen:

private Pattern pattern = Pattern.compile("-?\\d+(\\.\\d+)?"); public boolean isNumeric(String strNum) { if (strNum == null) { return false; } return pattern.matcher(strNum).matches(); }

Schauen wir uns nun einige Aussagen für die obige Methode an:

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

5. Verwenden von Apache Commons

In diesem Abschnitt werden verschiedene Methoden erläutert, die in der Apache Commons-Bibliothek verfügbar sind.

5.1. NumberUtils.isCreatable (String)

NumberUtils von Apache Commons bietet eine statische Methode NumberUtils.isCreatable (String), mit der überprüft wird, ob ein String eine gültige Java-Nummer ist oder nicht.

Diese Methode akzeptiert:

  1. Hexadezimalzahlen beginnend mit 0x oder 0X
  2. Oktalzahlen beginnend mit einer führenden 0
  3. Wissenschaftliche Notation (zum Beispiel 1.05e-10)
  4. Mit einem Typqualifikator gekennzeichnete Nummern (z. B. 1L oder 2.2d)

Wenn die angegebene Zeichenfolge null oder leer / leer ist , wird sie nicht als Zahl betrachtet und die Methode gibt false zurück .

Lassen Sie uns einige Tests mit dieser Methode ausführen:

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")).isTrue(); 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("")).isFalse(); assertThat(NumberUtils.isCreatable("abc")).isFalse(); assertThat(NumberUtils.isCreatable(" 22 ")).isFalse(); assertThat(NumberUtils.isCreatable("09")).isFalse();

Beachten Sie, wie wir in den Zeilen 6, 7 und 8 wahre Aussagen für Hexadezimalzahlen, Oktalzahlen und wissenschaftliche Notationen erhalten.

Auch auf der Leitung 14, die Zeichenfolge „09“ zurückkehrt falsch , weil die vorhergehenden „0“ zeigt an, dass dies eine Oktalzahl und „09“ ist keine gültige Oktalzahl.

Für jede Eingabe, die mit dieser Methode true zurückgibt , können wir NumberUtils.createNumber (String) verwenden, die uns die gültige Nummer gibt.

5.2. NumberUtils.isParsable (String)

Die NumberUtils.isParsable (String) -Methode prüft, ob der angegebene String analysierbar ist oder nicht.

Parsable numbers are those that are parsed successfully by any parse method like Integer.parseInt(String), Long.parseLong(String), Float.parseFloat(String) or Double.parseDouble(String).

Unlike NumberUtils.isCreatable(), this method won't accept hexadecimal numbers, scientific notations or strings ending with any type qualifier, that is, ‘f', ‘F', ‘d' ,'D' ,'l'or‘L'.

Let's look at some affirmations:

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("")).isFalse(); assertThat(NumberUtils.isParsable("6.2f")).isFalse(); assertThat(NumberUtils.isParsable("9.8d")).isFalse(); assertThat(NumberUtils.isParsable("22L")).isFalse(); assertThat(NumberUtils.isParsable("0xFF")).isFalse(); assertThat(NumberUtils.isParsable("2.99e+8")).isFalse();

On line 4, unlike NumberUtils.isCreatable(), the number starting with string “0” isn't considered as an octal number, but a normal decimal number and hence it returns true.

We can use this method as a replacement for what we did in section 3, where we’re trying to parse a number and checking for an error.

5.3. StringUtils.isNumeric(CharSequence)

The method StringUtils.isNumeric(CharSequence) checks strictly for Unicode digits. This means:

  1. Any digits from any language that is a Unicode digit is acceptable
  2. Since a decimal point is not considered as a Unicode digit, it's not valid
  3. Leading signs (either positive or negative) are also not acceptable

Let's now see this method in action:

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

Note that the input parameters in lines 2 and 3 are representing numbers 123 in Arabic and Devanagari respectively. Since they're valid Unicode digits, this method returns true on them.

5.4. StringUtils.isNumericSpace(CharSequence)

The StringUtils.isNumericSpace(CharSequence) checks strictly for Unicode digits and/or space. This is same as StringUtils.isNumeric() with the only difference being that it accepts spaces as well, not only leading and trailing spaces but also if they're in between numbers:

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")).isFalse(); assertThat(StringUtils.isNumericSpace("-123")).isFalse();

6. Benchmarks

Before we conclude this article, let's go through some benchmark results to help us to analyze which of the above-mentioned methods are best for our use-case.

6.1. Simple Benchmark

First, we take a simple approach. We pick one string value – for our test we use Integer.MAX_VALUE. Then, that value will be tested against all our implementations:

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.usingRegularExpressions avgt 20 101.580 ± 4.244 ns/op Benchmarking.usingStringUtils_isNumeric avgt 20 35.885 ± 1.691 ns/op Benchmarking.usingStringUtils_isNumericSpace avgt 20 31.979 ± 1.393 ns/op

As we see, the most costly operations are regular expressions. After that is our core Java-based solution.

Moreover, note that the operations using the Apache Commons library are by-and-large the same.

6.2. Enhanced Benchmark

Let's use a more diverse set of tests, for a more representative benchmark:

  • 95 values are numeric (0-94 and Integer.MAX_VALUE)
  • 3 contain numbers but are still malformatted — ‘x0‘, ‘0..005′, and ‘–11
  • 1 contains only text
  • 1 is a null

Upon executing the same tests, we'll see the results:

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/op Benchmarking.usingRegularExpressions avgt 20 7168.761 ± 344.597 ns/op Benchmarking.usingStringUtils_isNumeric avgt 20 1071.753 ± 8.657 ns/op Benchmarking.usingStringUtils_isNumericSpace avgt 20 1157.722 ± 24.139 ns/op

The most important difference is that two of our tests – the regular expressions solution and the core Java-based solution – have traded places.

Aus diesem Ergebnis lernen wir, dass das Auslösen und Behandeln der NumberFormatException , die nur in 5% der Fälle auftritt, einen relativ großen Einfluss auf die Gesamtleistung hat. Wir schließen daraus, dass die optimale Lösung von unserem erwarteten Input abhängt.

Wir können auch sicher schließen, dass wir die Methoden aus der Commons-Bibliothek oder eine ähnlich implementierte Methode verwenden sollten, um eine optimale Leistung zu erzielen.

7. Fazit

In diesem Artikel haben wir verschiedene Möglichkeiten untersucht, um festzustellen, ob ein String numerisch ist oder nicht. Wir haben uns beide Lösungen angesehen - integrierte Methoden und auch externe Bibliotheken.

Wie immer finden Sie die Implementierung aller oben angegebenen Beispiele und Codefragmente, einschließlich des Codes, der zum Ausführen von Benchmarks verwendet wird, auf GitHub.