2011-12-19 9 views
11

मुझे पता है कि ऐसा करने के दस लाख तरीके हैं लेकिन सबसे तेज़ क्या है? इसमें वैज्ञानिक नोटेशन शामिल होना चाहिए।यह जांचने का सबसे तेज़ तरीका है कि स्ट्रिंग को जावा में डबल करने के लिए पार्स किया जा सकता है

नोट: मुझे मूल्य को डबल में बदलने में कोई दिलचस्पी नहीं है, मुझे केवल यह जानने में दिलचस्पी है कि यह संभव है या नहीं। यानी private boolean isDouble(String value)

+0

AFAIK, उस पर एक डबल .parseDouble (स्ट्रिंग) करें और यह अपवाद फेंकता है अगर यह संख्याओं से शुरू नहीं होता है। (यहां सामान्यीकृत)। यदि आप regExs करना चाहते हैं और अग्रणी गैर-संख्या वर्णों को अलग करना चाहते हैं जो एक अलग कहानी है। – Rcunn87

+0

खैर, AFAIK, कोशिश-पकड़ धीमा होने लगता है। – JHollanti

+1

मैं regex विचार पर दूसरे Rcunn87 पर जा रहा हूं, लेकिन सुनिश्चित करें कि आप इसे संकलित करें और इसे स्थिर रूप से संग्रहीत करें ताकि आप इसे बार-बार उपयोग कर सकें। –

उत्तर

6

आप इसे डबल कक्षा के उपयोग की नियमित नियमित अभिव्यक्ति का उपयोग करके देख सकते हैं। यह अच्छी तरह से यहाँ प्रलेखित है:

में अवैध स्ट्रिंग पर इस विधि बुला और होने एक NumberFormatException फेंक दिया बचने के लिए, नीचे नियमित अभिव्यक्ति के लिए इस्तेमाल किया जा सकता है:

http://docs.oracle.com/javase/6/docs/api/java/lang/Double.html#valueOf%28java.lang.String%29

यहाँ कोड हिस्सा है

final String Digits  = "(\\p{Digit}+)"; 
    final String HexDigits = "(\\p{XDigit}+)"; 

     // an exponent is 'e' or 'E' followed by an optionally 
     // signed decimal integer. 
     final String Exp  = "[eE][+-]?"+Digits; 
     final String fpRegex = 
      ("[\\x00-\\x20]*"+ // Optional leading "whitespace" 
      "[+-]?(" + // Optional sign character 
      "NaN|" +   // "NaN" string 
      "Infinity|" +  // "Infinity" string 

      // A decimal floating-point string representing a finite positive 
      // number without a leading sign has at most five basic pieces: 
      // Digits . Digits ExponentPart FloatTypeSuffix 
      // 
      // Since this method allows integer-only strings as input 
      // in addition to strings of floating-point literals, the 
      // two sub-patterns below are simplifications of the grammar 
      // productions from the Java Language Specification, 2nd 
      // edition, section 3.10.2. 

      // Digits ._opt Digits_opt ExponentPart_opt FloatTypeSuffix_opt 
      "((("+Digits+"(\\.)?("+Digits+"?)("+Exp+")?)|"+ 

      // . Digits ExponentPart_opt FloatTypeSuffix_opt 
      "(\\.("+Digits+")("+Exp+")?)|"+ 

     // Hexadecimal strings 
     "((" + 
     // 0[xX] HexDigits ._opt BinaryExponent FloatTypeSuffix_opt 
     "(0[xX]" + HexDigits + "(\\.)?)|" + 

     // 0[xX] HexDigits_opt . HexDigits BinaryExponent FloatTypeSuffix_opt 
     "(0[xX]" + HexDigits + "?(\\.)" + HexDigits + ")" + 

     ")[pP][+-]?" + Digits + "))" + 
      "[fFdD]?))" + 
      "[\\x00-\\x20]*");// Optional trailing "whitespace" 

    if (Pattern.matches(fpRegex, myString)) 
      Double.valueOf(myString); // Will not throw NumberFormatException 
     else { 
      // Perform suitable alternative action 
     } 
+0

दरअसल मेरे मामले में सबसे तेज़ समाधान केवल तभी होता था जब बाकी झंडे के माध्यम से झंडे और क्या नहीं। लेकिन ऐसा इसलिए है क्योंकि मेरे मामले में स्ट्रिंग अक्सर वास्तव में छोटी होती है (जैसे 3 या 4 वर्ण)। एक सामान्य समाधान के रूप में, मुझे लगता है कि यह सबसे अच्छा है। – JHollanti

0

मुझे लगता है कि इसे दोबारा बदलने और अपवाद को पकड़ने का प्रयास करना जांचने का सबसे तेज़ तरीका होगा ... एक और तरीका जिसे मैं सोच सकता हूं, अवधि ('।') द्वारा स्ट्रिंग को विभाजित कर रहा है और फिर जांच कर रहा है कि स्प्लिट सरणी के प्रत्येक भाग में केवल पूर्णांक होते हैं ... लेकिन मुझे लगता है कि पहला तरीका तेज़ होगा

+0

तेजी से फेंकना और पकड़ना कैसा है? बुरा अभ्यास का जिक्र नहीं है? और एक अवधि का उपयोग लोकेल सुरक्षित नहीं है। – JHollanti

5

Apache Commons Lang में एक आसान NumberUtils#isNumber है। यह थोड़ा दूर प्राप्त किए गए है:

मान्य संख्या 0x क्वालीफायर वैज्ञानिक अंकन और नंबर एक प्रकार क्वालीफायर (जैसे 123L) से चिह्नित के साथ चिह्नित हेक्साडेसिमल शामिल हैं।

लेकिन मुझे लगता है कि यह नियमित अभिव्यक्ति या फेंक और एक अपवाद को पकड़ने की तुलना में तेजी हो सकती है।

+0

क्या आपने उस विधि के लिए स्रोत कोड देखा है? मैं नहीं देखता कि यह एक नियमित अभिव्यक्ति की तुलना में तेज़ क्यों होगा - यह लूप, तुलना, झंडे का झटका है ... संभवतः एक रेगेक्स के साथ हुड के नीचे क्या चल रहा है लेकिन यह सुनिश्चित करने के लिए बदसूरत है। – Paul

+0

@ पॉल: मैंने वहां एक त्वरित रूप से देखा था (मुझे अब अफसोस है ;-)) लेकिन जब तक यह काम करता है, मुझे परवाह नहीं है। मुझे यह भी नहीं पता कि यह नियमित अभिव्यक्ति से तेज़ होगा या नहीं। याद रखें कि रेगेक्स एक गतिशील रूप से जेनरेट की गई राज्य-मशीन है (हालांकि शायद * बहुत * अनुकूलित)। –

0

मैं कोड ब्लॉक नीचे की कोशिश की और अपवाद फेंकने की तरह लगता है है: इनपुट स्ट्रिंग स्क्रीन अधिक तेजी से

String a = "123f15512551"; 
     System.out.println(System.currentTimeMillis()); 
     a.matches("^\\d+\\.\\d+$"); 
     System.out.println(System.currentTimeMillis()); 

     try{ 
      Double.valueOf(a); 
     }catch(Exception e){ 
      System.out.println(System.currentTimeMillis()); 
     } 

आउटपुट:

1324316024735 
1324316024737 
1324316024737 
+0

आप बेंचमार्क निर्धारित करने के लिए इसे एक बार करने पर भरोसा नहीं कर सकते हैं। ऐसा हो सकता है कि बहुत अधिक भिन्नता हो, और आप मिली की घड़ी के संकल्प को नहीं जानते हैं। – corsiKa

+0

@glowcoder आप बहुत सारे संभावित विविधता, शायद हार्डवेयर भी हैं। मिली के बारे में: क्या यह 1.1.1 9 70 के बाद से सभी मिलिस समेत एक लंबा मूल्य नहीं है? – HRgiger

+0

क्या @glowcoder ने कहा - इसे पूर्व-संकलित पैटर्न के साथ दस लाख बार करें और हमें वापस आएं। – Paul

0

अपवाद, प्रवाह नियंत्रण के लिए नहीं किया जाना चाहिए, हालांकि जावा के लेखकों बना यह मुश्किल NumberFormatException कि जिस तरह से उपयोग नहीं करने के लिए।

कक्षा java.util.Scanner में hasNextDouble एक विधि है ताकि यह जांच सके कि String को डबल के रूप में पढ़ा जा सकता है या नहीं।

हुड के तहत Scanner नियमित अभिव्यक्तियों (पूर्व-संकलित पैटर्न के माध्यम से) का उपयोग यह निर्धारित करने के लिए करता है कि String को पूर्णांक या फ़्लोटिंग पॉइंट नंबर में परिवर्तित किया जा सकता है या नहीं। पैटर्न buildFloatAndDecimalPattern विधि में संकलित किए गए हैं जिन्हें आप GrepCode here पर देख सकते हैं।

एक पूर्व-संकलित पैटर्न में प्रयास/पकड़ ब्लॉक का उपयोग करने से तेज़ होने का अतिरिक्त लाभ होता है।

यहाँ विधि ऊपर संदर्भित है, मामले में GrepCode एक दिन गायब हो जाता है:

private void buildFloatAndDecimalPattern() { 
    // \\p{javaDigit} may not be perfect, see above 
    String digit = "([0-9]|(\\p{javaDigit}))"; 
    String exponent = "([eE][+-]?"+digit+"+)?"; 
    String groupedNumeral = "("+non0Digit+digit+"?"+digit+"?("+ 
          groupSeparator+digit+digit+digit+")+)"; 
    // Once again digit++ is used for performance, as above 
    String numeral = "(("+digit+"++)|"+groupedNumeral+")"; 
    String decimalNumeral = "("+numeral+"|"+numeral + 
     decimalSeparator + digit + "*+|"+ decimalSeparator + 
     digit + "++)"; 
    String nonNumber = "(NaN|"+nanString+"|Infinity|"+ 
          infinityString+")"; 
    String positiveFloat = "(" + positivePrefix + decimalNumeral + 
         positiveSuffix + exponent + ")"; 
    String negativeFloat = "(" + negativePrefix + decimalNumeral + 
         negativeSuffix + exponent + ")"; 
    String decimal = "(([-+]?" + decimalNumeral + exponent + ")|"+ 
     positiveFloat + "|" + negativeFloat + ")"; 
    String hexFloat = 
     "[-+]?0[xX][0-9a-fA-F]*\\.[0-9a-fA-F]+([pP][-+]?[0-9]+)?"; 
    String positiveNonNumber = "(" + positivePrefix + nonNumber + 
         positiveSuffix + ")"; 
    String negativeNonNumber = "(" + negativePrefix + nonNumber + 
         negativeSuffix + ")"; 
    String signedNonNumber = "(([-+]?"+nonNumber+")|" + 
          positiveNonNumber + "|" + 
          negativeNonNumber + ")"; 
    floatPattern = Pattern.compile(decimal + "|" + hexFloat + "|" + 
            signedNonNumber); 
    decimalPattern = Pattern.compile(decimal); 
} 
2

अपाचे कॉमन्स NumberUtil काफी तेजी से वास्तव में है।मैं अनुमान लगा रहा हूं कि यह किसी भी regexp कार्यान्वयन से तेज़ तरीका है।

+2

क्या आप एक बेंचमार्क प्रदान कर सकते हैं जो इस अनुमान को हार्ड तथ्यों से बदल देता है? – joergl

+1

मैं 'org.apache.commons.lang.math.NumberUtils' में' isDigits' और 'isNumber' देखता हूं, लेकिन' isDouble' की जांच करने के लिए कुछ भी नहीं है। तो आप किस विधि का उपयोग करने का सुझाव दे रहे थे? –

+0

सभी संख्याओं के लिए संख्या जांच नहीं है (डॉकू को देखें ...) 'वैध संख्या में हेक्सडेसिमल 0x क्वालीफायर, वैज्ञानिक नोटेशन और एक प्रकार क्वालीफायर (उदाहरण के लिए 123 एल) ' – Seega

2

मैं जाँच करने के लिए निम्नलिखित कोड का उपयोग करता है, तो एक स्ट्रिंग डबल करने के लिए पार्स किया जा सकता:

public static boolean isDouble(String str) { 
    if (str == null) { 
     return false; 
    } 
    int length = str.length(); 
    if (length == 0) { 
     return false; 
    } 
    int i = 0; 
    if (str.charAt(0) == '-') { 
     if (length == 1) { 
      return false; 
     } 
     ++i; 
    } 
    int integerPartSize = 0; 
    int exponentPartSize = -1; 
    while (i < length) { 
     char c = str.charAt(i); 
     if (c < '0' || c > '9') { 
      if (c == '.' && integerPartSize > 0 && exponentPartSize == -1) { 
       exponentPartSize = 0; 
      } else { 
       return false; 
      } 
     } else if (exponentPartSize > -1) { 
      ++exponentPartSize; 
     } else { 
      ++integerPartSize; 
     } 
     ++i; 
    } 
    if ((str.charAt(0) == '0' && i > 1 && exponentPartSize < 1) 
      || exponentPartSize == 0 || (str.charAt(length - 1) == '.')) { 
     return false; 
    } 
    return true; 
} 

मुझे पता है कि उत्पादन बिल्कुल डबल वर्ग में नियमित अभिव्यक्ति लेकिन इस विधि के लिए के रूप में ही नहीं है कर रहा हूँ बहुत तेज़ है और नतीजा मेरी जरूरतों के लिए काफी अच्छा है। ये विधि के लिए मेरे यूनिट परीक्षण हैं।

@Test 
public void shouldReturnTrueIfStringIsDouble() { 
    assertThat(Utils.isDouble("0.0")).isTrue(); 
    assertThat(Utils.isDouble("0.1")).isTrue(); 
    assertThat(Utils.isDouble("-0.0")).isTrue(); 
    assertThat(Utils.isDouble("-0.1")).isTrue(); 
    assertThat(Utils.isDouble("1.0067890")).isTrue(); 
    assertThat(Utils.isDouble("0")).isTrue(); 
    assertThat(Utils.isDouble("1")).isTrue(); 
} 

@Test 
public void shouldReturnFalseIfStringIsNotDouble() { 
    assertThat(Utils.isDouble(".01")).isFalse(); 
    assertThat(Utils.isDouble("0.1f")).isFalse(); 
    assertThat(Utils.isDouble("a")).isFalse(); 
    assertThat(Utils.isDouble("-")).isFalse(); 
    assertThat(Utils.isDouble("-1.")).isFalse(); 
    assertThat(Utils.isDouble("-.1")).isFalse(); 
    assertThat(Utils.isDouble("123.")).isFalse(); 
    assertThat(Utils.isDouble("1.2.3")).isFalse(); 
    assertThat(Utils.isDouble("1,3")).isFalse(); 
} 
+0

धन्यवाद! मैंने reg exp संस्करण के बजाय इस विधि को कार्यान्वित किया है और इसमें बड़े प्रदर्शन में सुधार हुआ है। जावा प्रोफाइलर का उपयोग करके मैं देख सकता हूं कि मैं सिर्फ 27,000 एमएमएस से चला गया हूं, केवल रेग एक्सप पर कॉल पर आपके द्वारा उपयोग किए जाने वाले 97 एमएमएस के लिए डबल फंक्शन है - कॉल की एक ही संख्या के साथ। –