בדוק אם מחרוזת היא מספרית בג'אווה

1. הקדמה

לעתים קרובות בזמן הניתוח חוּטs, עלינו להבין האם א חוּט הוא מספר חוקי או לא.

במדריך זה נבדוק מספר דרכים לזהות אם הנתון חוּט הוא מספרי, תחילה באמצעות Java רגיל, אחר כך ביטויים רגילים ולבסוף באמצעות ספריות חיצוניות.

לאחר שסיימנו לדון ביישומים שונים, נשתמש במבחני אמת כדי לקבל מושג אילו שיטות הן אופטימליות.

2. תנאים מוקדמים

נתחיל בכמה תנאים מוקדמים לפני שנמשיך לתוכן הראשי.

בחלק האחרון של מאמר זה נשתמש בספרייה חיצונית של Apache Commons לה נוסיף את התלות שלה pom.xml:

 org.apache.commons commons-lang3 3.9 

הגרסה האחרונה של הספרייה הזו נמצאת ב- Maven Central.

3. שימוש ב- Java רגיל

אולי הדרך הקלה והאמינה ביותר לבדוק האם א חוּט הוא מספרי או לא הוא על ידי ניתוחו בשיטות המובנות של Java:

  1. Integer.parseInt (מחרוזת)
  2. Float.parseFloat (מחרוזת)
  3. Double.parseDouble (מחרוזת)
  4. Long.parseLong (מחרוזת)
  5. BigInteger חדש (מחרוזת)

אם שיטות אלה לא זורקות NumberFormatException, אז זה אומר שהניתוח היה מוצלח וה- חוּט הוא מספרי:

בוליאני סטטי ציבורי isNumeric (String strNum) {if (strNum == null) {return false; } נסה {double d = Double.parseDouble (strNum); } לתפוס (NumberFormatException nfe) {return false; } להחזיר נכון; }

בואו נראה שיטה זו בפעולה:

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

בשלנו isNumeric () בשיטה, אנו בודקים רק ערכים שהם מסוגם לְהַכפִּיל, אך ניתן לשנות שיטה זו גם כדי לבדוק אם קיימת מספר שלם, לָצוּף, ארוך ומספרים גדולים באמצעות כל אחת משיטות הניתוח שגייסנו קודם.

שיטות אלה נדונות גם במאמר המרות מיתרי Java.

4. שימוש בביטויים רגולריים

עכשיו בואו נשתמש ב- regex -? \ d + (\. \ d +)? כדי להתאים את המספרי מיתרים המורכב ממספר שלם חיובי או שלילי ומצוף.

אך מובן מאליו שאנחנו בהחלט יכולים לשנות את ה- regex הזה כדי לזהות ולטפל במגוון רחב של כללים. הנה, נשמור על זה פשוט.

בואו נפרק את ה- regex הזה ונראה איך זה עובד:

  • -? - חלק זה מזהה אם המספר הנתון הוא שלילי, המקף ""מחפש מקף פשוטו כמשמעו ואת סימן השאלה"?”מסמן את נוכחותו כאופציה
  • \ d + - זה מחפש ספרה אחת או יותר
  • (\. \ d +)? - חלק זה של regex הוא זיהוי מספרים צפים. כאן אנו מחפשים ספרה אחת או יותר ואחריה נקודה. בסופו של דבר סימן השאלה מסמל שהקבוצה השלמה הזו היא אופציונאלית

ביטויים רגולריים הם נושא רחב מאוד. לקבלת סקירה קצרה, עיין במדריך שלנו על ממשק ה- API של ביטויים רגילים של Java.

לעת עתה, בוא ניצור שיטה באמצעות הביטוי הרגולרי שלעיל:

דפוס דפוס פרטי = דפוס.קומפילציה ("-? \ d + (\. \ d +)?"); בוליאני ציבורי isNumeric (String strNum) {if (strNum == null) {return false; } return patroon.matcher (strNum) .matches (); }

בואו נסתכל על כמה קביעות לשיטה הנ"ל:

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

5. שימוש באפצ'י קומונס

בחלק זה נדון בשיטות שונות הזמינות בספריית Apache Commons.

5.1. NumberUtils.isCreatable (מחרוזת)

NumberUtils מ- Apache Commons מספק שיטה סטטית NumberUtils.isCreatable (מחרוזת) שבודק האם א חוּט הוא מספר Java תקף או לא.

שיטה זו מקבלת:

  1. מספרים הקסדצימליים המתחילים ב- 0x או 0X
  2. מספרים אוקטלים המתחילים ב 0 מוביל
  3. סימון מדעי (לדוגמא 1.05e-10)
  4. מספרים המסומנים במוקדמות סוג (למשל 1 ליטר או 2.2 ד)

אם המחרוזת שסופקה היא ריק אוֹ ריק / ריק, אז זה לא נחשב למספר והשיטה תחזור שֶׁקֶר.

בואו נבצע כמה בדיקות בשיטה זו:

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

שימו לב איך אנחנו מקבלים נָכוֹן קביעות למספרים הקסדצימאליים, למספרים אוקטלים ולסימונים מדעיים בשורות 6, 7 ו- 8 בהתאמה.

כמו כן, בשורה 14, המחרוזת “09” החזרות שֶׁקֶר כי הקודם “0” מציין שמדובר במספר אוקטלי ו- “09” אינו מספר אוקטלי תקף.

על כל קלט שחוזר נָכוֹן בשיטה זו נוכל להשתמש NumberUtils.createNumber (מחרוזת) אשר ייתן לנו את המספר התקף.

5.2. NumberUtils.isParsable (מחרוזת)

ה NumberUtils.isParsable (מחרוזת) השיטה בודקת אם הנתון חוּט ניתן לניתוח או לא.

מספרים ניתנים לניתוח הם אלה שמנותחים בהצלחה בכל שיטת ניתוח כמו Integer.parseInt (מחרוזת), Long.parseLong (מחרוזת), Float.parseFloat (מחרוזת) אוֹ Double.parseDouble (מחרוזת).

בניגוד NumberUtils.isCreatable (), שיטה זו לא תקבל מספרים הקסדצימאליים, סימונים מדעיים או מחרוזות המסתיימים בשום סוג מסוים, כלומר, 'F', 'F', 'd', 'D', 'l'אוֹ'ל'.

בואו נסתכל על כמה אישורים:

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

בקו 4, שלא כמו NumberUtils.isCreatable (), המספר שמתחיל במחרוזת “0” אינו נחשב למספר אוקטלי, אלא למספר עשרוני רגיל ומכאן שהוא מחזיר נכון.

אנו יכולים להשתמש בשיטה זו כתחליף למה שעשינו בסעיף 3, שם אנו מנסים לנתח מספר ולבדוק אם קיימת שגיאה.

5.3. StringUtils.isNumeric (CharSequence)

השיטה StringUtils.isNumeric (CharSequence) בודק אך ורק ספרות Unicode. זה אומר:

  1. כל ספרות מכל שפה שהיא ספרת Unicode מקובלת
  2. מכיוון שנקודה עשרונית אינה נחשבת לספרת Unicode, היא אינה תקפה
  3. גם סימנים מובילים (חיוביים או שליליים) אינם מקובלים

בואו נראה את השיטה הזו בפעולה:

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

שים לב שפרמטרי הקלט בשורות 2 ו- 3 מייצגים מספרים 123 בערבית ובדוונגרי בהתאמה. מכיוון שהם ספרות Unicode תקפות, שיטה זו חוזרת נָכוֹן עליהם.

5.4. StringUtils.isNumericSpace (CharSequence)

ה StringUtils.isNumericSpace (CharSequence) בודק אך ורק ספרות ו / או רווח של Unicode. זה אותו דבר כמו StringUtils.isNumeric () כאשר ההבדל היחיד הוא שהוא מקבל גם רווחים, לא רק רווחים מובילים ונגררים אלא גם אם הם נמצאים בין המספרים:

assertThat (StringUtils.isNumericSpace ("123")). isTrue (); assertThat (StringUtils.isNumericSpace ( "123")) 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. מידות

לפני שנסיים מאמר זה, בוא נעבור כמה תוצאות אמת מידה שיעזרו לנו לנתח אילו מהשיטות הנ"ל הן הטובות ביותר למקרה השימוש שלנו.

6.1. אמת מידה פשוטה

ראשית, אנו נוקטים גישה פשוטה. אנו בוחרים ערך מחרוזת אחד - לצורך הבדיקה שלנו אנו משתמשים מספר שלם.MAX_VALUE. לאחר מכן, הערך הזה ייבדק מול כל היישומים שלנו:

מידות מידוד Cnt ציון שגיאה יחידות 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

כפי שאנו רואים, הפעולות היקרות ביותר הן ביטויים רגילים. לאחר מכן הוא הפתרון המרכזי מבוסס Java.

יתר על כן, שים לב כי הפעולות המשתמשות בספריית Apache Commons הן בגדול זהות.

6.2. מידוד משופר

בואו נשתמש במערך מבחנים מגוון יותר, לקבלת אמת מידה מייצגת יותר:

  • 95 ערכים הם מספריים (0-94 ו- מספר שלם.MAX_VALUE)
  • 3 מכילים מספרים אך הם עדיין בתבנית שגויה - 'x0‘, ‘0..005′, ו'–11
  • 1 מכיל טקסט בלבד
  • 1 הוא א ריק

לאחר ביצוע אותן בדיקות נראה את התוצאות:

יחידות מידה Cnt ציון שגיאות יחידות 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

ההבדל החשוב ביותר הוא ששני הבדיקות שלנו - פתרון הביטויים הרגילים והפתרון הליבה מבוסס Java - החליפו מקומות.

מתוצאה זו אנו למדים כי השלכה והטיפול ב NumberFormatException, המתרחשת רק ב -5% מהמקרים, יש השפעה גדולה יחסית על הביצועים הכוללים. לכן, אנו מסיקים כי הפיתרון האופטימלי תלוי בתשומתנו הצפויה.

כמו כן, אנו יכולים להסיק בבטחה שעלינו להשתמש בשיטות מספריית Commons או בשיטה המיושמת באופן דומה לצורך ביצועים אופטימליים.

7. מסקנה

במאמר זה בדקנו דרכים שונות למצוא אם חוּט הוא מספרי או לא. בדקנו את שני הפתרונות - שיטות מובנות וגם ספריות חיצוניות.

כמו תמיד, ניתן למצוא ב- GitHub את היישום של כל הדוגמאות וקטעי הקוד המופיעים לעיל, כולל הקוד המשמש לביצוע אמות מידה.