2009-06-04 24 views
5

में स्ट्रिंग से अंकों को पार करने के बेहतर तरीके पर सहायता करें मेरे पास एक स्ट्रिंग है जिसमें अंक और अक्षर हैं। मैं स्ट्रिंग को अंकों के संगत हिस्सों और अक्षरों के संगत हिस्सों में विभाजित करना चाहता हूं।जावा

स्ट्रिंग "34A312O5M444123A" पर विचार करें। [ "34", "ए ',' 312 '," ओ "," 5 "," एम "," 444,123 "," A "है]

मेरे पास है:

मैं करने के लिए उत्पादन करना चाहते हैं कोड है जो काम करता है और जैसा दिखता है:

List<String> digitsAsElements(String str){ 
    StringBuilder digitCollector = new StringBuilder(); 

    List<String> output = new ArrayList<String>(); 

    for (int i = 0; i < str.length(); i++){ 
    char cChar = str.charAt(i); 

    if (Character.isDigit(cChar)) 
     digitCollector.append(cChar); 
    else{ 
     output.add(digitCollector.toString()); 
     output.add(""+cChar); 

     digitCollector = new StringBuilder(); 
    }   
    } 

    return output; 
} 

मैं बंटवारे str दो बार माना सभी नंबरों हिस्सा और एक सरणी सभी पत्र मात्रा युक्त युक्त एक सरणी प्राप्त करने के लिए। फिर परिणाम विलय। मैं इससे दूर झुक गया क्योंकि यह पठनीयता को नुकसान पहुंचाएगा।

मैंने जानबूझकर इसे रेगेक्स पैटर्न के साथ हल करने से बचाया है क्योंकि मुझे रेगेक्स पैटर्न को पठनीयता के लिए एक बड़ी बाधा माना जाता है।

  • डिबगर्स उन्हें अच्छी तरह से संभाल नहीं पाते हैं।
  • वे स्रोत कोड पढ़ने वाले किसी के प्रवाह को बाधित करते हैं।
  • ओवरटाइम रेगेक्स व्यवस्थित रूप से बढ़ता है और राक्षस बन जाता है।
  • वे गहराई से सहज नहीं हैं।

मेरे प्रश्न हैं:

  • कैसे मैं ऊपर कोड की पठनीयता में सुधार कर सकता है?
  • क्या ऐसा करने का कोई बेहतर तरीका है? एक ऐसी कक्षा जो इस समस्या को सुन्दरता से हल करती है।
  • आप regEx का उपयोग करने और ऊपर लिखे गए कुछ के समान कुछ कोडिंग के बीच रेखा को कहां खींचते हैं?
  • आप regExes की पठनीयता/रखरखाव कैसे बढ़ाते हैं?
+0

ध्यान दें कि लिखा गया आपका कोड स्ट्रिंग को "[34, ए, 312, ओ, 5, एम, एन, आई, 444123, ए]" में बदल देता है, जो आप नहीं कह रहे हैं कि आप उम्मीद कर रहे हैं । मैं सही कोड पोस्ट करने जा रहा था, लेकिन जोहान्स रोस्सेल ने मुझे बेहतर जवाब दिया। –

+0

शब्द, अच्छी पकड़। –

उत्तर

5

आप अगर यह कोड की एक पंक्ति में समस्या को हल करने का मतलब regexes प्रयोग करने को तैयार होगा?

// Split at any position that's either: 
// preceded by a digit and followed by a non-digit, or 
// preceded by a non-digit and followed by a digit. 
String[] parts = str.split("(?<=\\d)(?=\\D)|(?<=\\D)(?=\\d)"); 
टिप्पणी regex की व्याख्या करने के साथ

, मुझे लगता है कि गैर regex समाधान की तुलना में अधिक पठनीय है (या अन्य regex समाधान के किसी भी, उस बात के लिए)।

+0

+1 के लिए कम से कम अनबॉक्सिंग तय की है, जो वाकई चालाक है! ठीक है श्रीमान –

13

इस विशेष कार्य के लिए मैं हमेशा कुछ लिखने के बजाय एक रेगेक्स का उपयोग करता हूं। आपके द्वारा ऊपर दिया गया कोड, कम से कम मेरे लिए, एक साधारण नियमित अभिव्यक्ति से कम पठनीय है (जो इस मामले में (\d+|[^\d]+) होगा, जहां तक ​​मैं देख सकता हूं)।

आप कुछ पंक्तियों से अधिक नियमित अभिव्यक्तियों को लिखने से बचना चाह सकते हैं। वे हो सकते हैं और आमतौर पर पढ़ने के लिए अपठनीय और कठिन हैं, लेकिन कोड भी उन्हें प्रतिस्थापित किया जा सकता है! पार्सर्स लगभग कभी सुंदर नहीं होते हैं और आप आमतौर पर जेनरेट (या हस्तलिखित) पार्सर की भावना बनाने की कोशिश करने से मूल व्याकरण को पढ़ने से बेहतर होते हैं। Regexes के लिए वही जाता है (imho) जो एक नियमित व्याकरण का एक संक्षिप्त विवरण हैं।

तो, सामान्य रूप से मैं कहूंगा कि आपके प्रश्न में दिए गए कोड के पक्ष में रेगेक्स को प्रतिबंधित करना बहुत बेवकूफ विचार की तरह लगता है। और नियमित अभिव्यक्ति सिर्फ एक उपकरण हैं, कुछ भी कम नहीं, और कुछ भी नहीं। अगर कुछ और पाठ पार्सिंग का बेहतर काम करता है (कहें, एक वास्तविक पार्सर, कुछ सबस्ट्रिंग जादू, आदि) तो इसका इस्तेमाल करें। लेकिन संभावनाओं को दूर न करें क्योंकि आप उनके साथ असहज महसूस करते हैं - दूसरों को उनके साथ मुकाबला करने में कम समस्याएं हो सकती हैं और सभी लोग सीखने में सक्षम हैं।

संपादित करें: mmyers द्वारा टिप्पणी के बाद अद्यतन regex।

+2

+1, सभी regex बुराई या बदसूरत नहीं है। –

+0

+1, निश्चित रूप से regEx की जगह है! समस्या यह नहीं है कि प्रारंभिक रेगेक्स कैसा दिखता है, लेकिन 5 वर्षों में 10 लोगों के बाद रेगेक्स कैसा दिखता है, विशेष मामलों के साथ इसमें संशोधन किया गया है। अगर रेगेक्स के लालित्य के साथ कुछ था, तो यह साफ होगा, लेकिन जावा के स्वयं दस्तावेज प्रकृति (और डिबगबिलिटी) के साथ। –

+1

रेगेक्स होना चाहिए (\ d + | [^ \ d] +), या अन्यथा यह पहले गैर-अंकों से शुरू होने वाली हर चीज़ को पकड़ लेगा। वास्तव में कोड पोस्ट करने वाले लोगों को गुमराह करने के लिए आप पर शर्म आती है। : पी –

2

मैं इस तरह कुछ (चेतावनी, अवांछित कोड) का उपयोग करूंगा। मेरे लिए regexps से बचने की कोशिश करने से यह बहुत अधिक पठनीय है। Regexps सही जगह पर उपयोग किए जाने पर एक शानदार उपकरण है।

टिप्पणियों पर टिप्पणी और इनपुट और आउटपुट मूल्यों के उदाहरण प्रदान करने से भी मदद मिलती है।

List<String> digitsAsElements(String str){ 
    Pattern p = Pattern.compile("(\\d+|\\w+)*"); 
    Matcher m = p.matcher(str); 

    List<String> output = new ArrayList<String>(); 
    for(int i = 1; i <= m.groupCount(); i++) { 
     output.add(m.group(i)); 
    } 
    return output; 
} 
1

एएएस, किसी ने मुझे कोड पर हराया। मुझे लगता है कि regex संस्करण पढ़ने/बनाए रखने के लिए आसान है।

digitsAsElements1("34A312O5MNI444123A") = [34, A, 312, O, 5, M, , N, , I, 444123, A] 
digitsAsElements2("34A312O5MNI444123A") = [34, A, 312, O, 5, MNI, 444123, A] 
Expected: [34, A, 312, O, 5, MN, 444123, A] 

की तुलना करें::

DigitsAsElements.java:

इसके अलावा, बनाम उम्मीद उत्पादन 2 कार्यान्वयन के बीच उत्पादन में अंतर ...

आउटपुट ध्यान दें

import java.util.ArrayList; import java.util.List; import java.util.regex.Matcher; import java.util.regex.Pattern; public class DigitsAsElements { static List<String> digitsAsElements1(String str){ StringBuilder digitCollector = new StringBuilder(); List<String> output = new ArrayList<String>(); for (int i = 0; i < str.length(); i++){ char cChar = str.charAt(i); if (Character.isDigit(cChar)) digitCollector.append(cChar); else{ output.add(digitCollector.toString()); output.add(""+cChar); digitCollector = new StringBuilder(); } } return output; } static List<String> digitsAsElements2(String str){ // Match a consecutive series of digits or non-digits final Pattern pattern = Pattern.compile("(\\d+|\\D+)"); final Matcher matcher = pattern.matcher(str); final List<String> output = new ArrayList<String>(); while (matcher.find()) { output.add(matcher.group()); } return output; } /** * @param args */ public static void main(String[] args) { System.out.println("digitsAsElements(\"34A312O5MNI444123A\") = " + digitsAsElements1("34A312O5MNI444123A")); System.out.println("digitsAsElements2(\"34A312O5MNI444123A\") = " + digitsAsElements2("34A312O5MNI444123A")); System.out.println("Expected: [" + "34, A, 312, O, 5, MN, 444123, A"+"]"); } } 
+0

अपेक्षित मूल्य में एमएनआई होना चाहिए, न कि एनएम? –

+0

"अपेक्षित" पोस्टर ने कहा है कि वे उम्मीद कर रहे हैं कि उनके कार्यान्वयन (अंकएस एलिमेंट्स 1) और रेगेक्स संस्करण (अंकएएस एलिमेंट्स 2) वास्तव में आउटपुट क्या बनाते हैं। –

+1

कोई मेला नहीं - उन्होंने पोस्ट संपादित किया :-) –

7

एक उपयोगिता वर्ग के लिए, ओ जांचें यू । आपकी समस्या को हल करने के तरीके के बारे में कई विकल्प हैं। मेरे पास आपके प्रश्नों पर कुछ टिप्पणियां हैं I

Debuggers उन्हें (नियमित अभिव्यक्ति) अच्छी तरह से

एक regex काम करता है या नहीं, संभाल नहीं है आपके डेटा में क्या पर निर्भर करता है। कुछ अच्छे प्लगइन्स हैं जिनका उपयोग आप रेगेक्स बनाने में मदद के लिए कर सकते हैं, जैसे कि QuickREx ग्रहण के लिए, क्या डीबगर वास्तव में आपको अपने डेटा के लिए सही पार्सर लिखने में मदद करता है?

वे स्रोत कोड पढ़ने वाले किसी के प्रवाह को बाधित करते हैं।

मुझे लगता है कि यह इस बात पर निर्भर करता है कि आप उनके साथ कितने आरामदायक हैं। निजी तौर पर, मैं स्ट्रिंग पार्सिंग कोड की 50 और लाइनों की तुलना में उचित रेगेक्स पढ़ता हूं, लेकिन हो सकता है कि यह एक निजी चीज़ हो।

ओवरटाइम रेगेक्स व्यवस्थित रूप से बढ़ता है और राक्षस बन जाता है।

मुझे लगता है कि वे शायद हो सकते हैं, लेकिन संभवतया वे कोड के साथ एक समस्या है जो वे असफल हो रहे हैं। स्रोत डेटा की जटिलता बढ़ती जा रही है, तो आप शायद आप एक अधिक अर्थपूर्ण समाधान (शायद ANTLR की तरह एक पार्सर जेनरेटर)

जरूरत है वे गहराई से गैर सहज ज्ञान युक्त हैं कि क्या पर नजर रखने की जरूरत है।

वे एक पैटर्न मिलान करने वाली भाषा हैं। मैं कहूंगा कि वे उस संदर्भ में बहुत सहज हैं।

मैं उपरोक्त कोड की पठनीयता में सुधार कैसे कर सकता हूं?

सुनिश्चित नहीं है कि, रेगेक्स का उपयोग करने के अलावा।

क्या ऐसा करने का कोई बेहतर तरीका है? एक ऐसी कक्षा जो इस समस्या को सुन्दरता से हल करती है।

ऊपर उल्लिखित, java.util.Scanner।

आप regEx का उपयोग करने और ऊपर लिखे गए कुछ के समान कुछ कोडिंग के बीच रेखा को कहां खींचते हैं?

व्यक्तिगत रूप से मैं कुछ भी उचित रूप से सरल के लिए रेगेक्स का उपयोग करता हूं।

आप regExes की पठनीयता/रखरखाव कैसे बढ़ाते हैं?

विस्तार से पहले ध्यान से सोचें, कोड और रेगेक्स को विस्तार से टिप्पणी करने के लिए अतिरिक्त देखभाल करें ताकि यह स्पष्ट हो कि आप क्या कर रहे हैं।

+0

वाह यह मुझे लिखने के लिए एक लंबा समय ले गया होगा! इस बीच में बहुत सारे जवाब थे, क्षमा करें अगर मैं ओवरलैप करता हूं। – Brabster

+0

+1, ठीक है! यह वही जवाब है जिसे मैं ढूंढ रहा था, काश मैं आपको +10 दे सकता था। = डी –

+0

मदद करने के लिए बहुत खुश हैं। सौभाग्य! – Brabster

1

आप क्रम में इस वर्ग के इस्तेमाल कर सकते हैं अपने पाश सरल करने के लिए:

public class StringIterator implements Iterator<Character> { 

    private final char[] chars; 
    private int i; 

    private StringIterator(char[] chars) { 
     this.chars = chars; 
    } 

    public boolean hasNext() { 
     return i < chars.length; 
    } 

    public Character next() { 
     return chars[i++]; 
    } 

    public void remove() { 
     throw new UnsupportedOperationException("Not supported."); 
    } 

    public static Iterable<Character> of(String string) { 
     final char[] chars = string.toCharArray(); 

     return new Iterable<Character>() { 

      @Override 
      public Iterator<Character> iterator() { 
       return new StringIterator(chars); 
      } 
     }; 
    } 
} 

अब आप इस पुनर्लेखन कर सकते हैं:

for (int i = 0; i < str.length(); i++){ 
    char cChar = str.charAt(i); 
    ... 
} 

साथ:

for (Character cChar : StringIterator.of(str)) { 
    ... 
} 

मेरी 2 सेंट

बीटीडब्ल्यू इस क्लास एस अन्य संदर्भ में भी पुन: प्रयोज्य है।

+0

+1, स्ट्रिंगइटरेटर बहुत साफ दिखता है। –

+1

हालांकि, यह स्केल नहीं करता है। प्रत्येक चरित्र को इटरेटर के लिए बॉक्स किया जाना चाहिए, फिर फ़ोरैच लूप के लिए अनबॉक्स किया जाना चाहिए; वह हत्यारों का प्रदर्शन। –

+0

आप सही हैं। मैंने लूप – dfa

1

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

उदाहरण के लिए, यदि आपने "संख्याओं या अक्षरों के ग्रैब ब्लॉक" कोड को कोड किया है, तो कॉलर एक बहुत ही सरल, सीधा-आगे लूप होगा जो प्रत्येक कॉल के परिणामों को प्रिंट करेगा, और जिस विधि को आप बुला रहे थे वह अच्छी तरह से होगा परिभाषित किया गया है कि रेगेक्स का इरादा स्पष्ट होगा, भले ही आपको सिंटैक्स के बारे में कुछ भी पता न हो, और विधि को बाध्य किया जाएगा ताकि लोगों को समय के साथ इसे मक्खन करने की संभावना न हो।

इसके साथ समस्या यह है कि रेगेक्स उपकरण इस उपयोग के लिए इतना आसान और अच्छी तरह अनुकूलित हैं कि इसके लिए एक विधि कॉल को उचित ठहराना मुश्किल है।

1

चूंकि कोई भी अभी तक सही कोड पोस्ट नहीं कर रहा है, इसलिए मैं इसे एक शॉट दूंगा।

पहले गैर-रेगेक्स संस्करण। ध्यान दें कि मैं स्ट्रिंगबिल्डर का उपयोग जो भी प्रकार के चरित्र को अंतिम (अंक या गैर-अंक) देखा गया था, जमा करने के लिए करता हूं। यदि राज्य बदलता है, तो मैं अपनी सामग्री को सूची में डंप करता हूं और एक नया स्ट्रिंगबिल्डर शुरू करता हूं। इस तरह लगातार निरंतर गैर अंकों को लगातार अंकों की तरह समूहीकृत किया जाता है।

static List<String> digitsAsElements(String str) { 
    StringBuilder collector = new StringBuilder(); 

    List<String> output = new ArrayList<String>(); 
    boolean lastWasDigit = false; 
    for (int i = 0; i < str.length(); i++) { 
     char cChar = str.charAt(i); 

     boolean isDigit = Character.isDigit(cChar); 
     if (isDigit != lastWasDigit) { 
      if (collector.length() > 0) { 
       output.add(collector.toString()); 
       collector = new StringBuilder(); 
      } 
      lastWasDigit = isDigit; 
     } 
     collector.append(cChar); 
    } 
    if (collector.length() > 0) 
     output.add(collector.toString()); 

    return output; 
} 

अब रेगेक्स संस्करण। यह मूल रूप से वही कोड है जिसे जुहा एस द्वारा पोस्ट किया गया था, लेकिन रेगेक्स वास्तव में काम करता है।

private static final Pattern DIGIT_OR_NONDIGIT_STRING = 
     Pattern.compile("(\\d+|[^\\d]+)"); 
static List<String> digitsAsElementsR(String str) { 
    // Match a consecutive series of digits or non-digits 
    final Matcher matcher = DIGIT_OR_NONDIGIT_STRING.matcher(str); 
    final List<String> output = new ArrayList<String>(); 
    while (matcher.find()) { 
     output.add(matcher.group()); 
    } 
    return output; 
} 

एक तरीका मैं अपने regexes पठनीय रखने के लिए एक तरीका है उनके नाम है।मुझे लगता है कि DIGIT_OR_NONDIGIT_STRING बहुत अच्छी तरह से बताता है कि मैं (प्रोग्रामर) सोचता हूं कि यह क्या करता है, और परीक्षण सुनिश्चित करना चाहिए कि यह वास्तव में करता है जो इसका मतलब है।

public static void main(String[] args) { 
    System.out.println(digitsAsElements("34A312O5MNI444123A")); 
    System.out.println(digitsAsElementsR("34A312O5MNI444123A")); 
} 

प्रिंट:

 
[34, A, 312, O, 5, MNI, 444123, A] 
[34, A, 312, O, 5, MNI, 444123, A]