Periksa Jika String Berangka di Java

1. Pengenalan

Sering kali semasa beroperasi pada String , kita perlu mengetahui sama ada String adalah nombor yang sah atau tidak.

Dalam tutorial ini, kita akan meneroka pelbagai cara untuk mengetahui apakah String yang diberikan adalah berangka , pertama menggunakan Java biasa, kemudian ungkapan biasa dan akhirnya dengan menggunakan perpustakaan luaran.

Setelah selesai membincangkan pelbagai pelaksanaan, kami akan menggunakan tanda aras untuk mendapatkan idea kaedah mana yang optimum.

2. Prasyarat

Mari mulakan dengan beberapa prasyarat sebelum kita menuju ke kandungan utama.

Pada bahagian terakhir artikel ini, kami akan menggunakan perpustakaan luaran Apache Commons yang akan kami tambahkan kebergantungannya di pom.xml kami :

 org.apache.commons commons-lang3 3.9 

Versi terbaru perpustakaan ini boleh didapati di Maven Central.

3. Menggunakan Java Plain

Mungkin kaedah termudah dan paling dipercayai untuk memeriksa sama ada String berangka atau tidak adalah dengan menguraikannya menggunakan kaedah bawaan Java:

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

Sekiranya kaedah ini tidak membuang NumberFormatException , itu bermaksud bahawa penguraian berjaya dan String adalah berangka:

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

Mari lihat kaedah ini dalam tindakan:

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

Dalam kita isNumeric () kaedah, kita hanya memeriksa nilai yang jenis Double , tetapi kaedah ini juga boleh diubah suai untuk memeriksa Integer , Float , Long dan jumlah yang besar dengan menggunakan mana-mana kaedah hurai yang kita telah menyenaraikan lebih awal .

Kaedah ini juga dibincangkan dalam artikel Java String Conversions.

4. Menggunakan Ungkapan Biasa

Sekarang mari kita gunakan regex -? \ D + (\. \ D +)? sepadan angka Strings terdiri daripada integer dan terapung positif atau negatif.

Tetapi ini tidak dapat dikatakan, bahawa kita pasti dapat mengubah regex ini untuk mengenal pasti dan menangani pelbagai peraturan. Di sini, kami akan mempermudahnya.

Mari pecahkan regex ini dan lihat bagaimana ia berfungsi:

  • -? - bahagian ini mengenal pasti jika nombor yang diberikan adalah negatif, tanda hubung “ - ” mencari tanda hubung secara harfiah dan tanda tanya “ ? ”Menandakan kehadirannya sebagai pilihan
  • \ d + - ini mencari satu atau lebih digit
  • (\. \ d +)? - bahagian regex ini adalah untuk mengenal pasti nombor apungan. Di sini kita mencari satu atau lebih digit diikuti dengan noktah. Tanda tanya, pada akhirnya, menunjukkan bahawa kumpulan lengkap ini adalah pilihan

Ungkapan biasa adalah topik yang sangat luas. Untuk mendapatkan gambaran ringkas, periksa tutorial kami di API ekspresi biasa Java.

Buat masa ini, mari buat kaedah menggunakan ungkapan biasa di atas:

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

Sekarang mari kita lihat beberapa penekanan untuk kaedah di atas:

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

5. Menggunakan Apache Commons

Di bahagian ini, kita akan membincangkan pelbagai kaedah yang terdapat di perpustakaan Apache Commons.

5.1. NumberUtils.isCreatable (Rentetan)

NumberUtils dari Apache Commons menyediakan kaedah statik NumberUtils.isCreatable (String) yang memeriksa sama ada String adalah nombor Java yang sah atau tidak.

Kaedah ini menerima:

  1. Nombor perenambelasan bermula dengan 0x atau 0X
  2. Nombor Oktal bermula dengan 0 terkemuka
  3. Notasi saintifik (contohnya 1.05e-10)
  4. Nombor yang ditandai dengan kelayakan jenis (contohnya 1L atau 2.2d)

Jika tali itu dibekalkan adalah null atau / kosong kosong , maka ia tidak dianggap nombor dan kaedah yang akan kembali palsu .

Mari jalankan beberapa ujian menggunakan kaedah ini:

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

Perhatikan bagaimana kita mendapat penegasan benar untuk nombor heksadesimal, nombor oktal dan notasi saintifik masing-masing pada baris 6, 7 dan 8.

Juga, pada baris 14, string "09" kembali palsu kerana "0" sebelumnya menunjukkan bahawa ini adalah nombor oktal dan "09" bukan nombor oktal yang sah.

Untuk setiap input yang kembali benar dengan kaedah ini, kita dapat menggunakan NumberUtils.createNumber (String) yang akan memberi kita nombor yang sah.

5.2. NumberUtils.isParsable (Rentetan)

Kaedah NumberUtils.isParsable (String) memeriksa sama ada String yang diberikan dapat dihuraikan atau tidak.

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.

Dari hasil ini, kita belajar bahawa melempar dan menangani NumberFormatException , yang terjadi hanya dalam 5% kes, mempunyai pengaruh yang cukup besar pada keseluruhan prestasi. Oleh itu, kami menyimpulkan bahawa penyelesaian yang optimum bergantung pada input yang diharapkan.

Juga, kita dapat membuat kesimpulan dengan selamat bahawa kita harus menggunakan kaedah dari perpustakaan Commons atau kaedah yang dilaksanakan serupa untuk prestasi yang optimum.

7. Kesimpulannya

Dalam artikel ini, kami meneroka pelbagai cara untuk mengetahui sama ada String berangka atau tidak. Kami melihat kedua-dua penyelesaian - kaedah terbina dalam dan juga perpustakaan luaran.

Seperti biasa, pelaksanaan semua contoh dan potongan kode yang diberikan di atas termasuk kod yang digunakan untuk melakukan penanda aras boleh didapati di GitHub.