Periksa Jika String Adalah Numerik di Java

1. Perkenalan

Seringkali saat mengoperasikan String s, kita perlu mencari tahu apakah sebuah String adalah angka yang valid atau tidak.

Dalam tutorial ini, kita akan menjelajahi beberapa cara untuk mendeteksi jika String yang diberikan adalah numerik , pertama menggunakan Java biasa, kemudian ekspresi reguler dan terakhir dengan menggunakan pustaka eksternal.

Setelah kita selesai membahas berbagai implementasi, kita akan menggunakan tolok ukur untuk mendapatkan gambaran tentang metode mana yang optimal.

2. Prasyarat

Mari kita mulai dengan beberapa prasyarat sebelum kita melanjutkan ke konten utama.

Di bagian akhir artikel ini, kami akan menggunakan pustaka eksternal Apache Commons yang akan kami tambahkan ketergantungannya di pom.xml kami :

 org.apache.commons commons-lang3 3.9 

Versi terbaru dari perpustakaan ini dapat ditemukan di Maven Central.

3. Menggunakan Plain Java

Mungkin cara termudah dan paling andal untuk memeriksa apakah String itu numerik atau tidak adalah dengan menguraikannya menggunakan metode bawaan Java:

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

Jika metode ini tidak menampilkan NumberFormatException , artinya penguraian berhasil dan String berupa numerik:

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 kita lihat metode ini beraksi:

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 metode isNumeric () kami, kami hanya memeriksa nilai-nilai yang berjenis Double , tetapi metode ini juga dapat dimodifikasi untuk memeriksa angka Integer , Float , Long dan besar dengan menggunakan salah satu metode parse yang telah kami daftarkan sebelumnya .

Metode ini juga dibahas di artikel Konversi String Java.

4. Menggunakan Ekspresi Reguler

Sekarang mari kita gunakan regex -? \ D + (\. \ D +)? untuk mencocokkan String numerik yang terdiri dari bilangan bulat positif atau negatif dan float.

Tapi sudah jelas, bahwa kami pasti dapat memodifikasi regex ini untuk mengidentifikasi dan menangani berbagai aturan. Di sini, kami akan membuatnya sederhana.

Mari kita uraikan regex ini dan lihat cara kerjanya:

  • -? - bagian ini mengidentifikasi jika nomor yang diberikan negatif, tanda hubung " - " mencari tanda hubung secara harfiah dan tanda tanya " ? "Menandai kehadirannya sebagai pilihan
  • \ d + - ini mencari satu atau lebih digit
  • (\. \ d +)? - bagian regex ini adalah untuk mengidentifikasi bilangan float. Di sini kami mencari satu atau lebih digit diikuti dengan titik. Tanda tanya, pada akhirnya, menandakan bahwa grup lengkap ini adalah opsional

Ekspresi reguler adalah topik yang sangat luas. Untuk mendapatkan ikhtisar singkat, lihat tutorial kami di API ekspresi reguler Java.

Untuk saat ini, mari buat metode menggunakan ekspresi reguler 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 pernyataan untuk metode 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 bagian ini, kita akan membahas berbagai metode yang tersedia di pustaka Apache Commons.

5.1. NumberUtils.isCreatable (String)

NumberUtils dari Apache Commons menyediakan metode statis NumberUtils.isCreatable (String) yang memeriksa apakah String adalah nomor Java yang valid atau tidak.

Metode ini menerima:

  1. Bilangan heksadesimal dimulai dengan 0x atau 0X
  2. Angka oktal dimulai dengan 0 di depan
  3. Notasi ilmiah (misalnya 1.05e-10)
  4. Angka yang ditandai dengan kualifikasi tipe (misalnya 1L atau 2.2d)

Jika string yang diberikan adalah null atau kosong / kosong , maka itu tidak dianggap sebagai angka dan metode akan mengembalikan false .

Mari kita jalankan beberapa tes menggunakan metode 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 kami mendapatkan pernyataan yang benar untuk bilangan heksadesimal, bilangan oktal, dan notasi ilmiah di baris 6, 7 dan 8 masing-masing.

Juga, pada baris 14, string "09" kembali salah karena "0" sebelumnya menunjukkan bahwa ini adalah bilangan oktal dan "09" bukan bilangan oktal yang valid.

Untuk setiap input yang mengembalikan nilai true dengan metode ini, kita dapat menggunakan NumberUtils.createNumber (String) yang akan memberi kita nomor yang valid.

5.2. NumberUtils.isParsable (String)

Metode NumberUtils.isParsable (String) memeriksa apakah String yang diberikan dapat diuraikan atau tidak.

Nomor yang dapat diuraikan adalah nomor yang berhasil diurai dengan metode parse apa pun seperti Integer.parseInt (String) , Long.parseLong (String) , Float.parseFloat (String) atau Double.parseDouble (String) .

Tidak seperti NumberUtils.isCreatable () , metode ini tidak akan menerima bilangan heksadesimal, notasi ilmiah atau string yang diakhiri dengan kualifikasi jenis apa pun, yaitu, 'f', 'F', 'd', 'D', 'l' atau ' L ' .

Mari kita lihat beberapa afirmasi:

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

Pada baris 4, tidak seperti NumberUtils.isCreatable () , angka yang dimulai dengan string “0” tidak dianggap sebagai angka oktal, tetapi angka desimal normal dan karenanya mengembalikan nilai true.

Kami dapat menggunakan metode ini sebagai pengganti untuk apa yang kami lakukan di bagian 3, di mana kami mencoba mengurai angka dan memeriksa kesalahan.

5.3. StringUtils.isNumeric (CharSequence )

Metode StringUtils.isNumeric (CharSequence) memeriksa digit Unicode secara ketat. Ini berarti:

  1. Digit apa pun dari bahasa apa pun yang merupakan digit Unicode dapat diterima
  2. Karena koma desimal tidak dianggap sebagai digit Unicode, itu tidak valid
  3. Tanda petunjuk (baik positif atau negatif) juga tidak dapat diterima

Sekarang mari kita lihat metode ini beraksi:

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

Perhatikan bahwa parameter masukan pada baris 2 dan 3 masing-masing mewakili angka 123 dalam bahasa Arab dan Dewanagari. Karena mereka merupakan digit Unicode yang valid, metode ini mengembalikan nilai true pada mereka.

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 bahwa melempar dan menangani NumberFormatException , yang terjadi hanya pada 5% kasus, memiliki dampak yang relatif besar pada kinerja secara keseluruhan. Jadi, kami menyimpulkan, bahwa solusi optimal bergantung pada masukan yang kami harapkan.

Selain itu, kita dapat dengan aman menyimpulkan bahwa kita harus menggunakan metode dari pustaka Commons atau metode yang diterapkan serupa untuk kinerja yang optimal.

7. Kesimpulan

Dalam artikel ini, kami menjelajahi berbagai cara untuk menemukan apakah String itu numerik atau bukan. Kami melihat kedua solusi - metode bawaan dan juga pustaka eksternal.

Seperti biasa, penerapan semua contoh dan cuplikan kode yang diberikan di atas termasuk kode yang digunakan untuk melakukan tolok ukur dapat ditemukan di GitHub.