Cách kiểm tra xem Chuỗi có phải là số trong Java không


887

Làm thế nào bạn sẽ kiểm tra nếu một Chuỗi là một số trước khi phân tích nó?


36
Tất cả các giải pháp được đề xuất với các biểu hiện thông thường sẽ không hoạt động đối với các số thập lục phân.
Oscar Castiblanco

và truyền chuỗi null trong hàm Match (...) sẽ ném ngoại lệ NullPulum.
Hitesh Sahu

Xem câu trả lời của Max Malysh để biết giải pháp Java 8 súc tích mà không cần thư viện của bên thứ ba.
Andy Thomas

Các chuỗi null @HiteshSahu dường như được xử lý một cách duyên dáng trong phiên bản mới nhất (bao gồm Java 6.x và 7.x)
tuổi thọ

Tất cả các giải pháp được đề xuất sử dụng Integer.parseInt()sẽ không phân tích được số điện thoại di động NumberFormatException.
Không phải là lỗi

Câu trả lời:


691

Với Apache Commons Lang 3.5 trở lên: NumberUtils.isCreatablehoặc StringUtils.isNumeric.

Với Apache Commons Lang 3.4 trở xuống: NumberUtils.isNumberhoặc StringUtils.isNumeric.

Bạn cũng có thể sử dụng StringUtils.isNumericSpacetrả về truecho chuỗi trống và bỏ qua khoảng trắng bên trong chuỗi. Một cách khác là sử dụng NumberUtils.isParsablevề cơ bản kiểm tra số có thể phân tích cú pháp theo Java. (Các javadocs được liên kết chứa các ví dụ chi tiết cho từng phương thức.)


59
StringUtils.isNumeric()có lẽ sẽ không thích hợp ở đây vì nó chỉ kiểm tra nếu chuỗi là một chuỗi các chữ số. Sẽ tốt cho hầu hết các số nguyên nhưng không phải như vậy đối với các số có số thập phân, dấu tách nhóm, v.v.
Jeff Mercado

42
phát minh lại bánh xe vì bạn không bao gồm toàn bộ thư viện vì bạn cần chức năng 3 dòng ở một nơi.
dalvarezmartinez1

12
Có thực sự đáng để thêm toàn bộ thư viện cho tính năng này không? Rõ ràng nếu nó được sử dụng với những thứ khác thì thật tuyệt, nhưng có lẽ nó quá mức cần thiết khi xem xét mọi người đã giải quyết điều này trong một dòng mã.
Nước

7
Không hoạt động với tiêu cực. Và một nửa số tất cả là số âm, vì vậy .....
Paul Draper

6
@PaulDraper: Bạn nói đúng, StringUtilskhông hỗ trợ các dấu hiệu hàng đầu nhưng bạn nên kiểm tra NumberUtils.isCreatable, nó hỗ trợ tiêu cực đúng cách.
palacsint

904

Điều này thường được thực hiện với một hàm do người dùng định nghĩa đơn giản (nghĩa là hàm "isNumeric" của chính bạn).

Cái gì đó như:

public static boolean isNumeric(String str) { 
  try {  
    Double.parseDouble(str);  
    return true;
  } catch(NumberFormatException e){  
    return false;  
  }  
}

Tuy nhiên, nếu bạn gọi hàm này rất nhiều và bạn hy vọng nhiều kiểm tra không thành công do không phải là số thì hiệu suất của cơ chế này sẽ không tốt, vì bạn dựa vào ngoại lệ bị ném cho mỗi thất bại, đó là một hoạt động khá tốn kém.

Một cách tiếp cận khác có thể là sử dụng biểu thức chính quy để kiểm tra tính hợp lệ của một số:

public static boolean isNumeric(String str) {
  return str.matches("-?\\d+(\\.\\d+)?");  //match a number with optional '-' and decimal.
}

Tuy nhiên, hãy cẩn thận với cơ chế RegEx ở trên, vì nó sẽ thất bại nếu bạn sử dụng các chữ số không phải là tiếng Ả Rập (tức là các chữ số khác từ 0 đến 9). Điều này là do phần "\ d" của RegEx sẽ chỉ khớp với [0-9] và thực tế không phải là nhận thức về số lượng quốc tế. (Cảm ơn OregonGhost đã chỉ ra điều này!)

Hoặc thậm chí một cách khác là sử dụng đối tượng java.text.NumberFormat tích hợp sẵn của Java để xem nếu sau khi phân tích chuỗi, vị trí của trình phân tích cú pháp nằm ở cuối chuỗi. Nếu có, chúng ta có thể giả sử toàn bộ chuỗi là số:

public static boolean isNumeric(String str) {
  NumberFormat formatter = NumberFormat.getInstance();
  ParsePosition pos = new ParsePosition(0);
  formatter.parse(str, pos);
  return str.length() == pos.getIndex();
}

7
\ D trong Java Regex chỉ khớp với các chữ số Latin? Nếu nó giống như .NET regexes, bạn sẽ gặp vấn đề với các chữ số khác (ví dụ tiếng Ả Rập), như được giải thích ở đây: blog.msdn.com/oldnewthing/archive/2004/03/09/86555.aspx
OregonGhost

3
giải pháp numberFormatter có lẽ chỉ tốt hơn một chút so với việc bắt giải NumberFormatException. Tôi nghi ngờ cách tốt nhất là sử dụng regex một.
Chii

11
Lưu ý rằng .trong biểu thức chính quy của bạn sẽ khớp với bất kỳ ký tự nào, không chỉ ký tự phân tách thập phân.
jqno

9
+1 để nhận ra chi phí thử / bắt. Đây thực sự là một cách tiếp cận khủng khiếp để sử dụng trong thời gian dài để sử dụng nhiều lần, nhưng thực sự chúng ta bị mắc kẹt với điều đó trong Java.
demongolem

5
Lưu ý rằng không có những thứ như "số Latin" và các chữ số 0-9 trên thực tế là các chữ số Ả Rập. Mọi người có lẽ quen thuộc với các chữ số La Mã, được sử dụng bởi những người nói tiếng Latin, dưới dạng I, II, III, IV, V, VI, v.v. en.wikipedia.org/wiki/Arabic_numemony ; vi.wikipedia.org/wiki/Roman_numemony
dantiston

152

Nếu bạn đang dùng Android, thì bạn nên sử dụng:

android.text.TextUtils.isDigitsOnly(CharSequence str)

tài liệu có thể được tìm thấy ở đây

giữ cho nó đơn giản . hầu hết mọi người đều có thể "lập trình lại" (điều tương tự).


4
@ kape123 :) chắc chắn "123.456" không có chữ số.
Ahmed Alejo

8
Lưu ý: điều này dẫn đến NPE cho đầu vào null. Ngoài ra, không hoạt động với số âm hoặc số thập phân.
gMale

2
Tôi thích nó!! Tôi nghĩ rằng điều này là hoàn toàn cho chữ số. Không dành cho .,-
ảo

Đây chỉ là những gì tôi đang tìm kiếm. Một cái gì đó đơn giản để kiểm tra chỉ các chữ số 0-9. Tôi đã đặt một bộ lọc trong phần khai báo EditText của mình nhưng chỉ trong trường hợp được thay đổi hoặc thay thế trên đường, thật tuyệt khi có một kiểm tra lập trình đơn giản.
jwehrle

127

Các biểu thức lambda 8.

String someString = "123123";
boolean isNumeric = someString.chars().allMatch( Character::isDigit );

4
Bạn cũng có thể sử dụng tham chiếu phương thức: someString.chars (). AllMatch (Ký tự :: isDigit)
Wienczny 8/2/2016

3
Đẹp nhưng vẫn là phát minh lại bánh xe như hầu hết các "giải pháp" ở đây. Ngoài ra, thất bại trên 'null' (như hầu hết những người khác).
qben

8
Câu trả lời này ngắn gọn, đơn giản và dễ đọc. Bạn gần như có thể đọc nó như tiếng Anh - "ký tự tất cả các chữ số khớp". Nó không yêu cầu thư viện của bên thứ ba. Nó không sử dụng ngoại lệ trong các trường hợp không ngoại lệ. Điều này sẽ trở thành câu trả lời được chấp nhận.
Andy Thomas

14
Nó sẽ tạo ra cái gì cho "-1"?
Balázs Németh

2
Không phải là câu trả lời đúng. Một chuỗi số có thể có các ký tự không phải là số (ví dụ: "." Hoặc "-") và vẫn hoàn toàn là số. Ví dụ 0,5, -1 và 1.000 sẽ thất bại với câu trả lời này và chúng hoàn toàn bằng số.
Simeon G

126

Như @CraigTP đã đề cập trong câu trả lời xuất sắc của anh ấy, tôi cũng có những lo ngại về hiệu suất tương tự khi sử dụng Ngoại lệ để kiểm tra xem chuỗi có phải là số hay không. Vì vậy, tôi kết thúc việc tách chuỗi và sử dụng java.lang.Character.isDigit().

public static boolean isNumeric(String str)
{
    for (char c : str.toCharArray())
    {
        if (!Character.isDigit(c)) return false;
    }
    return true;
}

Theo Javadoc , Character.isDigit(char)sẽ nhận dạng chính xác các chữ số không phải là tiếng Latinh. Hiệu suất khôn ngoan, tôi nghĩ rằng một số so sánh N đơn giản trong đó N là số lượng ký tự trong chuỗi sẽ hiệu quả hơn về mặt tính toán so với thực hiện so khớp regex.

CẬP NHẬT: Như được chỉ ra bởi Jean-François Corbett trong bình luận, đoạn mã trên sẽ chỉ xác nhận các số nguyên dương, bao gồm phần lớn trường hợp sử dụng của tôi. Dưới đây là mã được cập nhật xác nhận chính xác các số thập phân theo ngôn ngữ mặc định được sử dụng trong hệ thống của bạn, với giả định rằng dấu tách thập phân chỉ xảy ra một lần trong chuỗi.

public static boolean isStringNumeric( String str )
{
    DecimalFormatSymbols currentLocaleSymbols = DecimalFormatSymbols.getInstance();
    char localeMinusSign = currentLocaleSymbols.getMinusSign();

    if ( !Character.isDigit( str.charAt( 0 ) ) && str.charAt( 0 ) != localeMinusSign ) return false;

    boolean isDecimalSeparatorFound = false;
    char localeDecimalSeparator = currentLocaleSymbols.getDecimalSeparator();

    for ( char c : str.substring( 1 ).toCharArray() )
    {
        if ( !Character.isDigit( c ) )
        {
            if ( c == localeDecimalSeparator && !isDecimalSeparatorFound )
            {
                isDecimalSeparatorFound = true;
                continue;
            }
            return false;
        }
    }
    return true;
}

4
Không phải dấu tách thập phân cũng khiến điều này thất bại?
Jean-François Corbett

1
@ Jean-FrançoisCorbett: Điểm hay, tôi đã cập nhật mã với mã mới hơn chấp nhận dấu tách thập phân.
Ibrahim Arief

2
Có dấu -ve thất bại chức năng này?
java_mouse

3
Việc gọi toCharArray()sẽ tạo một bản sao của mảng trong đối tượng String vì String là bất biến. Có lẽ nhanh hơn để sử dụng charAt(int index)phương thức trên đối tượng String trực tiếp.
Mike Kucera

2
Sẽ tạo StringIndexOutOfBoundsExceptionkhi vượt qua một chuỗi có độ dài 0. Có thể được sửa bằngif(str.length() == 0) return false;
samgak

43

Thư viện Guava của Google cung cấp một phương pháp trợ giúp tốt để thực hiện việc này : Ints.tryParse. Bạn sử dụng nó như thế Integer.parseIntnhưng nó trả về nullthay vì ném Ngoại lệ nếu chuỗi không phân tích thành số nguyên hợp lệ. Lưu ý rằng nó trả về Integer, không phải int, vì vậy bạn phải chuyển đổi / autobox nó trở lại int.

Thí dụ:

String s1 = "22";
String s2 = "22.2";
Integer oInt1 = Ints.tryParse(s1);
Integer oInt2 = Ints.tryParse(s2);

int i1 = -1;
if (oInt1 != null) {
    i1 = oInt1.intValue();
}
int i2 = -1;
if (oInt2 != null) {
    i2 = oInt2.intValue();
}

System.out.println(i1);  // prints 22
System.out.println(i2);  // prints -1

Tuy nhiên, kể từ phiên bản hiện tại - Guava r11 - nó vẫn được đánh dấu @Beta.

Tôi đã không điểm chuẩn nó. Nhìn vào mã nguồn có một số chi phí từ rất nhiều kiểm tra sự tỉnh táo nhưng cuối cùng họ sử dụng Character.digit(string.charAt(idx)), tương tự, nhưng hơi khác so với, câu trả lời từ @Ibrahim ở trên. Không có ngoại lệ xử lý chi phí dưới vỏ bọc trong việc thực hiện của họ.


Coi chừng việc này sẽ ném NPE trong trường hợp đối số là null.
Vadzim

30

Không sử dụng Ngoại lệ để xác thực giá trị của bạn. Sử dụng libil Util thay vì apache NumberUtils:

NumberUtils.isNumber(myStringValue);

Chỉnh sửa :

Xin lưu ý rằng, nếu chuỗi của bạn bắt đầu bằng 0, NumberUtils sẽ diễn giải giá trị của bạn dưới dạng thập lục phân.

NumberUtils.isNumber("07") //true
NumberUtils.isNumber("08") //false

7
Câu trả lời được chấp nhận, ba năm trước, đã được bảo hiểm Number.isNumber().
Andy Thomas

Tôi không nghĩ vậy. Nó đã được cập nhật hoặc op thay đổi câu trả lời được chấp nhận. Tôi nhớ câu trả lời được chấp nhận không bao gồm NumberUtils đó là lý do tại sao tôi thêm câu trả lời của mình. Nhưng cảm ơn vì nhận xét
Goot

2
@Goot - Lịch sử của câu trả lời được chấp nhận cho thấy Number.isNumber()đã có mặt từ phiên bản đầu tiên của câu trả lời, ngày 24 tháng 9 '12 lúc 17:01.
Andy Thomas

@Goot, điều này khá tốt vì nó cũng bao gồm kiểm tra giá trị thập phân, không giống như StringUtils.
Heena Hussain

24

Tại sao mọi người đều thúc đẩy các giải pháp ngoại lệ / regex?

Mặc dù tôi có thể hiểu hầu hết mọi người đều ổn khi sử dụng thử / bắt, nhưng nếu bạn muốn làm điều đó thường xuyên ... nó có thể cực kỳ đánh thuế.

Những gì tôi đã làm ở đây là lấy regex, các phương thức parseNumber () và phương thức tìm kiếm mảng để xem cái nào hiệu quả nhất. Lần này, tôi chỉ nhìn vào số nguyên.

public static boolean isNumericRegex(String str) {
    if (str == null)
        return false;
    return str.matches("-?\\d+");
}

public static boolean isNumericArray(String str) {
    if (str == null)
        return false;
    char[] data = str.toCharArray();
    if (data.length <= 0)
        return false;
    int index = 0;
    if (data[0] == '-' && data.length > 1)
        index = 1;
    for (; index < data.length; index++) {
        if (data[index] < '0' || data[index] > '9') // Character.isDigit() can go here too.
            return false;
    }
    return true;
}

public static boolean isNumericException(String str) {
    if (str == null)
        return false;
    try {  
        /* int i = */ Integer.parseInt(str);
    } catch (NumberFormatException nfe) {  
        return false;  
    }
    return true;
}

Kết quả về tốc độ tôi nhận được là:

Done with: for (int i = 0; i < 10000000; i++)...

With only valid numbers ("59815833" and "-59815833"):
    Array numeric took 395.808192 ms [39.5808192 ns each]
    Regex took 2609.262595 ms [260.9262595 ns each]
    Exception numeric took 428.050207 ms [42.8050207 ns each]
    // Negative sign
    Array numeric took 355.788273 ms [35.5788273 ns each]
    Regex took 2746.278466 ms [274.6278466 ns each]
    Exception numeric took 518.989902 ms [51.8989902 ns each]
    // Single value ("1")
    Array numeric took 317.861267 ms [31.7861267 ns each]
    Regex took 2505.313201 ms [250.5313201 ns each]
    Exception numeric took 239.956955 ms [23.9956955 ns each]
    // With Character.isDigit()
    Array numeric took 400.734616 ms [40.0734616 ns each]
    Regex took 2663.052417 ms [266.3052417 ns each]
    Exception numeric took 401.235906 ms [40.1235906 ns each]

With invalid characters ("5981a5833" and "a"):
    Array numeric took 343.205793 ms [34.3205793 ns each]
    Regex took 2608.739933 ms [260.8739933 ns each]
    Exception numeric took 7317.201775 ms [731.7201775 ns each]
    // With a single character ("a")
    Array numeric took 291.695519 ms [29.1695519 ns each]
    Regex took 2287.25378 ms [228.725378 ns each]
    Exception numeric took 7095.969481 ms [709.5969481 ns each]

With null:
    Array numeric took 214.663834 ms [21.4663834 ns each]
    Regex took 201.395992 ms [20.1395992 ns each]
    Exception numeric took 233.049327 ms [23.3049327 ns each]
    Exception numeric took 6603.669427 ms [660.3669427 ns each] if there is no if/null check

Tuyên bố miễn trừ trách nhiệm: Tôi không khẳng định các phương pháp này được tối ưu hóa 100%, chúng chỉ để trình diễn dữ liệu

Các ngoại lệ giành được khi và chỉ khi số đó có 4 ký tự trở xuống và mỗi chuỗi luôn là một số ... trong trường hợp nào, tại sao thậm chí có một kiểm tra?

Nói tóm lại, sẽ vô cùng đau đớn nếu bạn thường xuyên gặp phải những con số không hợp lệ với lần thử / bắt, điều này có ý nghĩa. Một quy tắc quan trọng tôi luôn tuân theo là KHÔNG BAO GIỜ sử dụng thử / bắt cho luồng chương trình . Đây là một ví dụ tại sao.

Thật thú vị, đơn giản nếu char <0 || > 9 cực kỳ đơn giản để viết, dễ nhớ (và nên hoạt động với nhiều ngôn ngữ) và chiến thắng hầu hết các kịch bản thử nghiệm.

Nhược điểm duy nhất là tôi đoán Integer.parseInt () có thể xử lý các số không phải ASCII, trong khi phương thức tìm kiếm mảng thì không.


Đối với những người thắc mắc tại sao tôi nói thật dễ nhớ mảng ký tự một, nếu bạn biết không có dấu hiệu tiêu cực, bạn có thể dễ dàng thoát khỏi thứ gì đó cô đọng như thế này:

public static boolean isNumericArray(String str) {
    if (str == null)
        return false;
    for (char c : str.toCharArray())
        if (c < '0' || c > '9')
            return false;
    return true;

Cuối cùng, như là một lưu ý cuối cùng, tôi tò mò về toán tử ám sát trong ví dụ được chấp nhận với tất cả các phiếu bầu. Thêm vào sự phân công của

double d = Double.parseDouble(...)

không chỉ vô dụng vì bạn thậm chí không sử dụng giá trị, mà còn lãng phí thời gian xử lý và tăng thời gian chạy thêm vài nano giây (dẫn đến tăng 100-200 ms trong các thử nghiệm). Tôi không thể hiểu tại sao mọi người sẽ làm điều đó vì nó thực sự là công việc làm thêm để giảm hiệu suất.

Bạn sẽ nghĩ rằng điều đó sẽ được tối ưu hóa ... mặc dù có lẽ tôi nên kiểm tra mã byte và xem trình biên dịch đang làm gì. Điều đó không giải thích được tại sao nó luôn xuất hiện dài hơn đối với tôi mặc dù nếu nó bằng cách nào đó được tối ưu hóa ... do đó tôi tự hỏi chuyện gì đang xảy ra. Như một lưu ý: Bằng cách dài hơn, tôi có nghĩa là chạy thử nghiệm cho 10000000 lần lặp và chạy chương trình đó nhiều lần (10 lần +) luôn cho thấy nó chậm hơn.

EDIT: Đã cập nhật một bài kiểm tra cho Character.isDigit ()


4
Điều này không phải biên dịch một biểu thức chính quy mới mỗi lần sao? Điều đó dường như không hiệu quả lắm.
Phường Samuel Edwin

1
@SamuelEdwinWard Đó là toàn bộ lý do tôi đưa ra bài đăng này ... ví dụ regex đã sử dụng câu trả lời của người khác và cho thấy nó không hiệu quả như thế nào. Ngay cả khi bạn thử regex với việc biên dịch trước nó và chỉ sử dụng nó, sự khác biệt về thời gian là: 2587 ms cho regex tôi đã đăng từ những người được cung cấp khác, 950 ms khi được biên dịch trước, 144 ms khi thực hiện như một mảng số (cho 1 triệu lần lặp của cùng một chuỗi). Biên dịch trước thời hạn rõ ràng sẽ có ích, nhưng thật đáng buồn là nó vẫn còn kém hơn so với cách thức mảng ... trừ khi có một số tối ưu hóa điên rồ mà tôi không biết.
Nước

Tin rằng Regex làm cho mọi thứ nhanh hơn gần như là một lời ngụy biện. Nếu đó là một lần tìm kiếm, vâng, tôi hiểu rồi ... nhưng tôi đã nhận thấy mã được viết một cách hiệu quả thực sự vượt xa regex đủ để gây sốc cho bạn! Bài đăng tuyệt vời @Water
Yo Apps

19
public static boolean isNumeric(String str)
{
    return str.matches("-?\\d+(.\\d+)?");
}

Biểu thức chính quy của CraigTP (hiển thị ở trên) tạo ra một số dương tính giả. Ví dụ: "23y4" sẽ được tính là một số vì '.' phù hợp với bất kỳ ký tự không phải là dấu thập phân.

Ngoài ra, nó sẽ từ chối bất kỳ số nào có dấu '+' hàng đầu

Một giải pháp thay thế để tránh hai vấn đề nhỏ này là

public static boolean isNumeric(String str)
{
    return str.matches("[+-]?\\d*(\\.\\d+)?");
}

cái này sẽ trả lại truecho một cộng "+"hoặc trừ "-"falsecho"0."
user85421

Bắt tốt trên đơn cộng hoặc trừ. Là "0." một số hợp lệ?
dùng872985

"0."là hợp lệ cho Double.parseDouble()và là một chữ hợp lệ theo JLS ( §3.10.2 )!
dùng85421

Tạo một biểu thức thông thường là tốn kém là tốt. Biểu thức chính quy phải được tạo một lần và được sử dụng lại
Daniel Nuriyev

1
bạn nên đổi nó thànhmatches("-?\\d+([.]\\d+)?")
Bobs

14

Chúng ta có thể thử thay thế tất cả các số từ chuỗi đã cho bằng ("") tức là khoảng trống và nếu sau đó độ dài của chuỗi bằng 0 thì chúng ta có thể nói rằng chuỗi đã cho chỉ chứa các số. [Nếu bạn thấy câu trả lời này hữu ích thì vui lòng xem xét bỏ phiếu] Ví dụ:

boolean isNumber(String str){
        if(str.length() == 0)
            return false; //To check if string is empty

        if(str.charAt(0) == '-')
            str = str.replaceFirst("-","");// for handling -ve numbers

        System.out.println(str);

        str = str.replaceFirst("\\.",""); //to check if it contains more than one decimal points

        if(str.length() == 0)
            return false; // to check if it is empty string after removing -ve sign and decimal point
        System.out.println(str);

        return str.replaceAll("[0-9]","").length() == 0;
    }

Vì vậy, ""một số nhưng "3.14""-1"không?
Eric Duminil

Rõ ràng là không áp dụng cho tất cả các dạng số, nhưng đây là một cách nâng cao cho suy nghĩ khác biệt ... nếu suy nghĩ ban đầu là của bạn, đó là.
gbenroscience

12

Bạn có thể sử dụng NumberFormat#parse:

try
{
     NumberFormat.getInstance().parse(value);
}
catch(ParseException e)
{
    // Not a number.
}

Đã cung cấp một chỉnh sửa - .getInstance () bị thiếu. +1 vì đây là câu trả lời tôi đã đi khi tìm thấy câu hỏi này.
8bitjunkie

5
Tốn kém nếu được sử dụng rộng rãi
Daniel Nuriyev

1
Nó cũng sẽ vượt qua nếu có các ký tự rác ở cuối value.
Brian White

Nó sẽ tạo ra sự cố sonar nếu bạn không đăng nhập ngoại lệ
jmhostalet

1
Điều này hoạt động với định dạng số 0x0001 trong đó Double.parseDouble không hoạt động. +1
Seabass77


8

Đây là câu trả lời của tôi cho vấn đề.

Một phương thức bắt tất cả tiện lợi mà bạn có thể sử dụng để phân tích bất kỳ Chuỗi nào với bất kỳ loại trình phân tích cú pháp nào : isParsable(Object parser, String str). Trình phân tích cú pháp có thể là một Classhoặc một object. Điều này cũng sẽ cho phép bạn sử dụng các trình phân tích cú pháp tùy chỉnh mà bạn đã viết và sẽ hoạt động cho mọi tình huống, ví dụ:

isParsable(Integer.class, "11");
isParsable(Double.class, "11.11");
Object dateFormater = new java.text.SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss z");
isParsable(dateFormater, "2001.07.04 AD at 12:08:56 PDT");

Đây là mã của tôi hoàn thành với các mô tả phương pháp.

import java.lang.reflect.*;

/**
 * METHOD: isParsable<p><p>
 * 
 * This method will look through the methods of the specified <code>from</code> parameter
 * looking for a public method name starting with "parse" which has only one String
 * parameter.<p>
 * 
 * The <code>parser</code> parameter can be a class or an instantiated object, eg:
 * <code>Integer.class</code> or <code>new Integer(1)</code>. If you use a
 * <code>Class</code> type then only static methods are considered.<p>
 * 
 * When looping through potential methods, it first looks at the <code>Class</code> associated
 * with the <code>parser</code> parameter, then looks through the methods of the parent's class
 * followed by subsequent ancestors, using the first method that matches the criteria specified
 * above.<p>
 * 
 * This method will hide any normal parse exceptions, but throws any exceptions due to
 * programmatic errors, eg: NullPointerExceptions, etc. If you specify a <code>parser</code>
 * parameter which has no matching parse methods, a NoSuchMethodException will be thrown
 * embedded within a RuntimeException.<p><p>
 * 
 * Example:<br>
 * <code>isParsable(Boolean.class, "true");<br>
 * isParsable(Integer.class, "11");<br>
 * isParsable(Double.class, "11.11");<br>
 * Object dateFormater = new java.text.SimpleDateFormat("yyyy.MM.dd G 'at' HH:mm:ss z");<br>
 * isParsable(dateFormater, "2001.07.04 AD at 12:08:56 PDT");<br></code>
 * <p>
 * 
 * @param parser    The Class type or instantiated Object to find a parse method in.
 * @param str   The String you want to parse
 * 
 * @return true if a parse method was found and completed without exception
 * @throws java.lang.NoSuchMethodException If no such method is accessible 
 */
public static boolean isParsable(Object parser, String str) {
    Class theClass = (parser instanceof Class? (Class)parser: parser.getClass());
    boolean staticOnly = (parser == theClass), foundAtLeastOne = false;
    Method[] methods = theClass.getMethods();

    // Loop over methods
    for (int index = 0; index < methods.length; index++) {
        Method method = methods[index];

        // If method starts with parse, is public and has one String parameter.
        // If the parser parameter was a Class, then also ensure the method is static. 
        if(method.getName().startsWith("parse") &&
            (!staticOnly || Modifier.isStatic(method.getModifiers())) &&
            Modifier.isPublic(method.getModifiers()) &&
            method.getGenericParameterTypes().length == 1 &&
            method.getGenericParameterTypes()[0] == String.class)
        {
            try {
                foundAtLeastOne = true;
                method.invoke(parser, str);
                return true; // Successfully parsed without exception
            } catch (Exception exception) {
                // If invoke problem, try a different method
                /*if(!(exception instanceof IllegalArgumentException) &&
                   !(exception instanceof IllegalAccessException) &&
                   !(exception instanceof InvocationTargetException))
                        continue; // Look for other parse methods*/

                // Parse method refuses to parse, look for another different method
                continue; // Look for other parse methods
            }
        }
    }

    // No more accessible parse method could be found.
    if(foundAtLeastOne) return false;
    else throw new RuntimeException(new NoSuchMethodException());
}


/**
 * METHOD: willParse<p><p>
 * 
 * A convienence method which calls the isParseable method, but does not throw any exceptions
 * which could be thrown through programatic errors.<p>
 * 
 * Use of {@link #isParseable(Object, String) isParseable} is recommended for use so programatic
 * errors can be caught in development, unless the value of the <code>parser</code> parameter is
 * unpredictable, or normal programtic exceptions should be ignored.<p>
 * 
 * See {@link #isParseable(Object, String) isParseable} for full description of method
 * usability.<p>
 * 
 * @param parser    The Class type or instantiated Object to find a parse method in.
 * @param str   The String you want to parse
 * 
 * @return true if a parse method was found and completed without exception
 * @see #isParseable(Object, String) for full description of method usability 
 */
public static boolean willParse(Object parser, String str) {
    try {
        return isParsable(parser, str);
    } catch(Throwable exception) {
        return false;
    }
}

5

Để chỉ khớp với mười số nguyên cơ bản, chỉ chứa các chữ số ASCII, hãy sử dụng:

public static boolean isNumeric(String maybeNumeric) {
    return maybeNumeric != null && maybeNumeric.matches("[0-9]+");
}

5

Một cách tiếp cận thực hiện tốt tránh bắt thử và xử lý số âm và ký hiệu khoa học.

Pattern PATTERN = Pattern.compile( "^(-?0|-?[1-9]\\d*)(\\.\\d+)?(E\\d+)?$" );

public static boolean isNumeric( String value ) 
{
    return value != null && PATTERN.matcher( value ).matches();
}

5

Đây là lớp của tôi để kiểm tra nếu một chuỗi là số. Nó cũng sửa các chuỗi số:

Đặc trưng:

  1. Xóa các số không cần thiết ["12.0000000" -> "12"]
  2. Xóa các số không cần thiết ["12.0580000" -> "12.058"]
  3. Xóa các ký tự không phải số ["12.00sdfsdf00" -> "12"]
  4. Xử lý các giá trị chuỗi âm ["-12,020000" -> "-12.02"]
  5. Xóa nhiều dấu chấm ["-12.0.20.000" -> "-12.02"]
  6. Không có thư viện bổ sung, chỉ là Java tiêu chuẩn

Bạn đi đây ...

public class NumUtils {
    /**
     * Transforms a string to an integer. If no numerical chars returns a String "0".
     *
     * @param str
     * @return retStr
     */
    static String makeToInteger(String str) {
        String s = str;
        double d;
        d = Double.parseDouble(makeToDouble(s));
        int i = (int) (d + 0.5D);
        String retStr = String.valueOf(i);
        System.out.printf(retStr + "   ");
        return retStr;
    }

    /**
     * Transforms a string to an double. If no numerical chars returns a String "0".
     *
     * @param str
     * @return retStr
     */
    static String makeToDouble(String str) {

        Boolean dotWasFound = false;
        String orgStr = str;
        String retStr;
        int firstDotPos = 0;
        Boolean negative = false;

        //check if str is null
        if(str.length()==0){
            str="0";
        }

        //check if first sign is "-"
        if (str.charAt(0) == '-') {
            negative = true;
        }

        //check if str containg any number or else set the string to '0'
        if (!str.matches(".*\\d+.*")) {
            str = "0";
        }

        //Replace ',' with '.'  (for some european users who use the ',' as decimal separator)
        str = str.replaceAll(",", ".");
        str = str.replaceAll("[^\\d.]", "");

        //Removes the any second dots
        for (int i_char = 0; i_char < str.length(); i_char++) {
            if (str.charAt(i_char) == '.') {
                dotWasFound = true;
                firstDotPos = i_char;
                break;
            }
        }
        if (dotWasFound) {
            String befDot = str.substring(0, firstDotPos + 1);
            String aftDot = str.substring(firstDotPos + 1, str.length());
            aftDot = aftDot.replaceAll("\\.", "");
            str = befDot + aftDot;
        }

        //Removes zeros from the begining
        double uglyMethod = Double.parseDouble(str);
        str = String.valueOf(uglyMethod);

        //Removes the .0
        str = str.replaceAll("([0-9])\\.0+([^0-9]|$)", "$1$2");

        retStr = str;

        if (negative) {
            retStr = "-"+retStr;
        }

        return retStr;

    }

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

}

5

Kết hợp Regex

Dưới đây là một ví dụ khác được nâng cấp regex "CraigTP" phù hợp với nhiều xác nhận hơn.

public static boolean isNumeric(String str)
{
    return str.matches("^(?:(?:\\-{1})?\\d+(?:\\.{1}\\d+)?)$");
}
  1. Chỉ có một dấu hiệu tiêu cực -được phép và phải được bắt đầu.
  2. Sau dấu âm phải có chữ số.
  3. Chỉ .cho phép một dấu thập phân .
  4. Sau dấu thập phân phải có chữ số.

Kiểm tra Regex

1                  --                   **VALID**
1.                 --                   INVALID
1..                --                   INVALID
1.1                --                   **VALID**
1.1.1              --                   INVALID

-1                 --                   **VALID**
--1                --                   INVALID
-1.                --                   INVALID
-1.1               --                   **VALID**
-1.1.1             --                   INVALID

5

Các ngoại lệ đắt tiền, nhưng trong trường hợp này, RegEx mất nhiều thời gian hơn. Mã dưới đây cho thấy một thử nghiệm đơn giản của hai chức năng - một sử dụng ngoại lệ và một sử dụng regex. Trên máy của tôi, phiên bản RegEx chậm hơn 10 lần so với ngoại lệ.

import java.util.Date;


public class IsNumeric {

public static boolean isNumericOne(String s) {
    return s.matches("-?\\d+(\\.\\d+)?");  //match a number with optional '-' and decimal.      
}

public static boolean isNumericTwo(String s) {
    try {
        Double.parseDouble(s);
        return true;
    } catch (Exception e) {
        return false;
    }
}

public static void main(String [] args) {

    String test = "12345.F";

    long before = new Date().getTime();     
    for(int x=0;x<1000000;++x) {
        //isNumericTwo(test);
        isNumericOne(test);
    }
    long after = new Date().getTime();

    System.out.println(after-before);

}

}

Nói chung, tôi nghĩ loại mã này sẽ được sử dụng để kiểm tra những thứ như nhập dữ liệu. Trong trường hợp đó, tốc độ không phải là một sự cân nhắc và làm điều gì đó xấu xí như ném một ngoại lệ để kiểm tra số hoặc không số là sai.
dùng872985

Có thể không. Đầu vào gõ thường được kiểm tra bởi thành phần UI nơi các lỗi có thể được hiển thị ngay trước khi gửi giá trị. Có thể phổ biến hơn là xác thực các chuỗi từ các tệp văn bản đầu vào lớn - trong đó vấn đề hiệu năng. Mục tiêu trong câu trả lời của tôi ở đây là để giải quyết câu lệnh "ngoại lệ là chậm" trong câu trả lời được chấp nhận. Regex phức tạp đắt hơn nhiều. Và không có "cú ném xấu xí" nào trong mã của tôi cả - chỉ là một cách nhanh hơn để phát hiện vi phạm. Với cách tiếp cận kiểm tra trước, sau đó tính toán, bạn thực hiện hai lần chuyển qua đầu vào: một để xác minh và sau đó chuyển đổi.
ChrisCantrell

5

// vui lòng kiểm tra mã bên dưới

public static boolean isDigitsOnly(CharSequence str) {
    final int len = str.length();
    for (int i = 0; i < len; i++) {
        if (!Character.isDigit(str.charAt(i))) {
            return false;
        }
    }
    return true;
}

Câu hỏi cho biết "số" có thể bao gồm các giá trị không nguyên.
rghome

3
// only int
public static boolean isNumber(int num) 
{
    return (num >= 48 && c <= 57); // 0 - 9
}

// is type of number including . - e E 
public static boolean isNumber(String s) 
{
    boolean isNumber = true;
    for(int i = 0; i < s.length() && isNumber; i++) 
    {
        char c = s.charAt(i);
        isNumber = isNumber & (
            (c >= '0' && c <= '9') || (c == '.') || (c == 'e') || (c == 'E') || (c == '')
        );
    }
    return isInteger;
}

// is type of number 
public static boolean isInteger(String s) 
{
    boolean isInteger = true;
    for(int i = 0; i < s.length() && isInteger; i++) 
    {
        char c = s.charAt(i);
        isInteger = isInteger & ((c >= '0' && c <= '9'));
    }
    return isInteger;
}

public static boolean isNumeric(String s) 
{
    try
    {
        Double.parseDouble(s);
        return true;
    }
    catch (Exception e) 
    {
        return false;
    }
}

3

Đây là một ví dụ đơn giản cho kiểm tra này:

public static boolean isNumericString(String input) {
    boolean result = false;

    if(input != null && input.length() > 0) {
        char[] charArray = input.toCharArray();

        for(char c : charArray) {
            if(c >= '0' && c <= '9') {
                // it is a digit
                result = true;
            } else {
                result = false;
                break;
            }
        }
    }

    return result;
}

3

Bạn có thể sử dụng đối tượng java.util.Scanner.

public static boolean isNumeric(String inputData) {
      Scanner sc = new Scanner(inputData);
      return sc.hasNextInt();
    }

2

Tôi đã sửa đổi giải pháp của CraigTP để chấp nhận ký hiệu khoa học và cả dấu chấm và dấu phẩy dưới dạng dấu tách thập phân

^-?\d+([,\.]\d+)?([eE]-?\d+)?$

thí dụ

var re = new RegExp("^-?\d+([,\.]\d+)?([eE]-?\d+)?$");
re.test("-6546"); // true
re.test("-6546355e-4456"); // true
re.test("-6546.355e-4456"); // true, though debatable
re.test("-6546.35.5e-4456"); // false
re.test("-6546.35.5e-4456.6"); // false

2

Đó là lý do tại sao tôi thích cách tiếp cận Thử * trong .NET. Ngoài phương thức Parse truyền thống giống như phương thức Java, bạn cũng có một phương thức TryPude. Tôi không giỏi về cú pháp Java (ngoài tham số?), Vì vậy vui lòng coi sau đây là một loại mã giả. Nó sẽ làm cho khái niệm rõ ràng mặc dù.

boolean parseInteger(String s, out int number)
{
    try {
        number = Integer.parseInt(myString);
        return true;
    } catch(NumberFormatException e) {
        return false;
    }
}

Sử dụng:

int num;
if (parseInteger("23", out num)) {
    // Do something with num.
}

vâng, không có "tham số ngoài" trong Java và vì trình bao bọc Integer là bất biến (do đó không thể được sử dụng làm tham chiếu hợp lệ để lưu trữ đầu ra), tùy chọn thành ngữ hợp lý sẽ trả về một đối tượng Integer có thể là null nếu phân tích cú pháp thất bại. Một tùy chọn xấu hơn có thể là vượt qua int [1] làm tham số đầu ra.
fortran

Vâng, tôi nhớ một cuộc thảo luận về lý do tại sao Java không có tham số đầu ra. nhưng trả về một Integer (dưới dạng null, nếu cần) cũng sẽ ổn, tôi đoán, mặc dù tôi không biết về hiệu suất của Java liên quan đến quyền anh / unboxing.
OregonGhost

4
Tôi thích C # nhiều như anh chàng tiếp theo, nhưng không sử dụng thêm đoạn mã .NET C # cho câu hỏi Java khi các tính năng không tồn tại trong Java
Shane

Nó sẽ tạo ra sự cố sonar nếu bạn không đăng nhập ngoại lệ
jmhostalet

2

Phân tích cú pháp (nghĩa là với Integer#parseInt) và chỉ cần bắt ngoại lệ. =)

Để làm rõ: Hàm parseInt kiểm tra xem nó có thể phân tích số trong mọi trường hợp (rõ ràng) không và nếu bạn muốn phân tích cú pháp bằng mọi cách, bạn sẽ không thực hiện bất kỳ hiệu suất nào bằng cách thực hiện phân tích cú pháp.

Nếu bạn không muốn phân tích nó (hoặc phân tích nó rất, rất hiếm khi), bạn có thể muốn làm điều đó khác đi tất nhiên.


1
Tốn kém nếu được sử dụng rộng rãi
Daniel Nuriyev

Nó sẽ tạo ra sự cố sonar nếu bạn không đăng nhập ngoại lệ
jmhostalet

Double.parseDouble
Alex78191

2

Bạn có thể sử dụng NumberUtils.isCreatable () từ Apache Commons Lang .

Vì NumberUtils.isNumber sẽ không được chấp nhận trong 4.0, nên sử dụng NumberUtils.isCreatable () để thay thế.


2

Luồng Java 8, biểu thức lambda, giao diện chức năng

Tất cả các trường hợp được xử lý ( chuỗi null, chuỗi rỗng, v.v. )

String someString = null; // something="", something="123abc", something="123123"

boolean isNumeric = Stream.of(someString)
            .filter(s -> s != null && !s.isEmpty())
            .filter(Pattern.compile("\\D").asPredicate().negate())
            .mapToLong(Long::valueOf)
            .boxed()
            .findAny()
            .isPresent();

2

Tôi đã minh họa một số điều kiện để kiểm tra số và số thập phân mà không sử dụng bất kỳ API nào,

Kiểm tra độ dài số 1 chữ số

Character.isDigit(char)

Kiểm tra số Chiều dài sửa chữa (Giả sử chiều dài là 6)

String number = "132452";
if(number.matches("([0-9]{6})"))
System.out.println("6 digits number identified");

Kiểm tra số Độ dài thay đổi giữa (Giả sử 4 đến 6 chiều dài)

//  {n,m}  n <= length <= m
String number = "132452";
if(number.matches("([0-9]{4,6})"))
System.out.println("Number Identified between 4 to 6 length");

String number = "132";
if(!number.matches("([0-9]{4,6})"))
System.out.println("Number not in length range or different format");

Kiểm tra số thập phân Độ dài thay đổi giữa (Giả sử 4 đến 7 chiều dài)

//  It will not count the '.' (Period) in length
String decimal = "132.45";
if(decimal.matches("(-?[0-9]+(\.)?[0-9]*){4,6}"))
System.out.println("Numbers Identified between 4 to 7");

String decimal = "1.12";
if(decimal.matches("(-?[0-9]+(\.)?[0-9]*){4,6}"))
System.out.println("Numbers Identified between 4 to 7");

String decimal = "1234";
if(decimal.matches("(-?[0-9]+(\.)?[0-9]*){4,6}"))
System.out.println("Numbers Identified between 4 to 7");

String decimal = "-10.123";
if(decimal.matches("(-?[0-9]+(\.)?[0-9]*){4,6}"))
System.out.println("Numbers Identified between 4 to 7");

String decimal = "123..4";
if(!decimal.matches("(-?[0-9]+(\.)?[0-9]*){4,6}"))
System.out.println("Decimal not in range or different format");

String decimal = "132";
if(!decimal.matches("(-?[0-9]+(\.)?[0-9]*){4,6}"))
System.out.println("Decimal not in range or different format");

String decimal = "1.1";
if(!decimal.matches("(-?[0-9]+(\.)?[0-9]*){4,6}"))
System.out.println("Decimal not in range or different format");

Hy vọng nó sẽ giúp được nhiều người.


2

Dựa trên các câu trả lời khác, tôi đã tự viết và nó không sử dụng các mẫu hoặc phân tích cú pháp với kiểm tra ngoại lệ.

Nó kiểm tra tối đa một dấu trừ và kiểm tra tối đa một dấu thập phân.

Dưới đây là một số ví dụ và kết quả của họ:

"1", "-1", "-1.5" và "-1.556" trả về đúng

"1..5", "1A.5", "1.5D", "-" và "--1" trả về sai

Lưu ý: Nếu cần, bạn có thể sửa đổi điều này để chấp nhận tham số Địa điểm và chuyển thông số đó vào các lệnh gọi DecimalFormatSymbols.getInstance () để sử dụng một Địa điểm cụ thể thay vì tham số hiện tại.

 public static boolean isNumeric(final String input) {
    //Check for null or blank string
    if(input == null || input.isBlank()) return false;

    //Retrieve the minus sign and decimal separator characters from the current Locale
    final var localeMinusSign = DecimalFormatSymbols.getInstance().getMinusSign();
    final var localeDecimalSeparator = DecimalFormatSymbols.getInstance().getDecimalSeparator();

    //Check if first character is a minus sign
    final var isNegative = input.charAt(0) == localeMinusSign;
    //Check if string is not just a minus sign
    if (isNegative && input.length() == 1) return false;

    var isDecimalSeparatorFound = false;

    //If the string has a minus sign ignore the first character
    final var startCharIndex = isNegative ? 1 : 0;

    //Check if each character is a number or a decimal separator
    //and make sure string only has a maximum of one decimal separator
    for (var i = startCharIndex; i < input.length(); i++) {
        if(!Character.isDigit(input.charAt(i))) {
            if(input.charAt(i) == localeDecimalSeparator && !isDecimalSeparatorFound) {
                isDecimalSeparatorFound = true;
            } else return false;
        }
    }
    return true;
}

1

Đây là hai phương pháp có thể hoạt động. (Không sử dụng Ngoại lệ). Lưu ý: Java là một giá trị truyền theo mặc định và giá trị của Chuỗi là địa chỉ của dữ liệu đối tượng của Chuỗi. Vì vậy, khi bạn đang làm

stringNumber = stringNumber.replaceAll(" ", "");

Bạn đã thay đổi giá trị đầu vào để không có khoảng trắng. Bạn có thể loại bỏ dòng đó nếu bạn muốn.

private boolean isValidStringNumber(String stringNumber)
{
    if(stringNumber.isEmpty())
    {
        return false;
    }

    stringNumber = stringNumber.replaceAll(" ", "");

    char [] charNumber = stringNumber.toCharArray();
    for(int i =0 ; i<charNumber.length ;i++)
    {
        if(!Character.isDigit(charNumber[i]))
        {
            return false;
        }
    }
    return true;
}

Đây là một phương pháp khác trong trường hợp bạn muốn cho phép thả nổi Phương pháp này được cho là cho phép các số trong biểu mẫu vượt qua 1.123,123,123,123,123.123 tôi vừa thực hiện và tôi nghĩ rằng nó cần thử nghiệm thêm để đảm bảo nó hoạt động.

private boolean isValidStringTrueNumber(String stringNumber)
{
    if(stringNumber.isEmpty())
    {
        return false;
    }

    stringNumber = stringNumber.replaceAll(" ", "");
    int countOfDecimalPoint = 0;
    boolean decimalPointPassed = false;
    boolean commaFound = false;
    int countOfDigitsBeforeDecimalPoint = 0;
    int countOfDigitsAfterDecimalPoint =0 ;
    int commaCounter=0;
    int countOfDigitsBeforeFirstComma = 0;

    char [] charNumber = stringNumber.toCharArray();
    for(int i =0 ; i<charNumber.length ;i++)
    {
        if((commaCounter>3)||(commaCounter<0))
        {
            return false;
        }
        if(!Character.isDigit(charNumber[i]))//Char is not a digit.
        {
            if(charNumber[i]==',')
            {
                if(decimalPointPassed)
                {
                    return false;
                }
                commaFound = true;
                //check that next three chars are only digits.
                commaCounter +=3;
            }
            else if(charNumber[i]=='.')
            {
                decimalPointPassed = true;
                countOfDecimalPoint++;
            }
            else
            {
                return false;
            }
        }
        else //Char is a digit.
        {
            if ((commaCounter>=0)&&(commaFound))
            {
                if(!decimalPointPassed)
                {
                    commaCounter--;
                }
            }

            if(!commaFound)
            {
                countOfDigitsBeforeFirstComma++;
            }

            if(!decimalPointPassed)
            {
                countOfDigitsBeforeDecimalPoint++;
            }
            else
            {
                countOfDigitsAfterDecimalPoint++;
            }
        }
    }
    if((commaFound)&&(countOfDigitsBeforeFirstComma>3))
    {
        return false;
    }
    if(countOfDecimalPoint>1)
    {
        return false;
    }

    if((decimalPointPassed)&&((countOfDigitsBeforeDecimalPoint==0)||(countOfDigitsAfterDecimalPoint==0)))
    {
        return false;
    }
    return true;
}

Ồ, câu hỏi hay. Tôi đoán cái này chỉ hoạt động số nguyên loại bình thường. Phương pháp ban đầu được tạo để lọc số điện thoại đầu vào và số đếm.
XForCE07
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.