2009-03-11 12 views
7

स्वीकार करता है मैं .NET स्ट्रिंग क्लास के लिए एक एक्सटेंशन विधि लिखना चाहता हूं। मैं इसे स्प्लिट विधि पर एक विशेष विविधता बनाना चाहता हूं - एक जो बचने से पहले बचने वाले चरित्र का उपयोग करते समय स्ट्रिंग को विभाजित करने से बचने के लिए एक बच निकलने वाला चरित्र लेता है।सी # विस्तार विधि - स्ट्रिंग स्प्लिट जो एस्केप कैरेक्टर

इसे लिखने का सबसे अच्छा तरीका क्या है? मैं इसके संपर्क में सबसे अच्छा गैर-रेगेक्स तरीका के बारे में उत्सुक हूं।
एक हस्ताक्षर की तरह साथ कुछ ...

public static string[] Split(this string input, string separator, char escapeCharacter) 
{ 
    // ... 
} 

अद्यतन: क्योंकि यह टिप्पणी, एस्केपिंग ...

सी # में जब गैर विशेष बचने पात्रों में से एक में आया आपको मिल त्रुटि - सीएस 1009: अपरिचित बचने का अनुक्रम।

आईई जेस्क्रिप्ट में भागने वाले पात्र फेंक दिए गए हैं। जब तक आप कोशिश नहीं करते हैं और फिर आपको "अपेक्षित हेक्साडेसिमल अंक" त्रुटि मिलती है। मैंने फ़ायरफ़ॉक्स का परीक्षण किया और यह वही व्यवहार है।

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

उत्तर

12

कैसे के बारे में:

public static IEnumerable<string> Split(this string input, 
             string separator, 
             char escapeCharacter) 
{ 
    int startOfSegment = 0; 
    int index = 0; 
    while (index < input.Length) 
    { 
     index = input.IndexOf(separator, index); 
     if (index > 0 && input[index-1] == escapeCharacter) 
     { 
      index += separator.Length; 
      continue; 
     } 
     if (index == -1) 
     { 
      break; 
     } 
     yield return input.Substring(startOfSegment, index-startOfSegment); 
     index += separator.Length; 
     startOfSegment = index; 
    } 
    yield return input.Substring(startOfSegment); 
} 

ऐसा लगता है कि (कुछ त्वरित परीक्षण तारों के साथ), लेकिन यह बचने वाले चरित्र को नहीं हटाता है - जो आपकी सटीक स्थिति पर निर्भर करेगा, मुझे संदेह है।

+0

ऐसा लगता है कि आप यह मान रहे हैं कि किसी भी समय बचने वाला चरित्र दिखाई देता है, इसके बाद विभाजक स्ट्रिंग होती है। क्या होगा अगर यह नहीं है? – tvanfosson

+0

मैं केवल प्रश्न में क्या चल रहा हूं - अगर विभाजक चरित्र विभाजक से पहले प्रकट होता है, तो उसे विभाजित करने के लिए उस विभाजक को उपयोग करने से रोकना चाहिए। मैं बचने वाले चरित्र को हटाने या इसे किसी अन्य तरीके से संसाधित करने की कोशिश नहीं करता हूं। बेवकूफ, शायद, लेकिन यह वह सारी जानकारी है जो हमें मिली है। –

+0

कूल, एक स्ट्रिंग सरणी लौटने पर ienumberable का लाभ क्या है? – rizzle

7

यह .... थोड़ा साफ करने की आवश्यकता होगी, लेकिन यह अनिवार्य रूप से यह है

List<string> output = new List<string>(); 
for(int i=0; i<input.length; ++i) 
{ 
    if (input[i] == separator && (i==0 || input[i-1] != escapeChar)) 
    { 
     output.Add(input.substring(j, i-j); 
     j=i; 
    } 
} 

return output.ToArray(); 
1

हस्ताक्षर गलत है, तो आप एक स्ट्रिंग सरणी

WARNIG इस्तेमाल नहीं किया एक्सटेंशन वापस जाने के लिए की जरूरत है है, तो मुझे कुछ त्रुटियों के बारे में माफ कर दें;)

public static List<String> Split(this string input, string separator, char escapeCharacter) 
{ 
    String word = ""; 
    List<String> result = new List<string>(); 
    for (int i = 0; i < input.Length; i++) 
    { 
//can also use switch 
     if (input[i] == escapeCharacter) 
     { 
      break; 
     } 
     else if (input[i] == separator) 
     { 
      result.Add(word); 
      word = ""; 
     } 
     else 
     { 
      word += input[i];  
     } 
    } 
    return result; 
} 
+0

अच्छी पकड़। मैं मूल प्रश्न में ठीक हो जाऊंगा। – BuddyJoe

4

मेरा पहला अवलोकन यह है कि विभाजक को एक वर्ण नहीं होना चाहिए क्योंकि एक वर्ण का उपयोग कर स्ट्रिंग से बचने से स्ट्रिंग नहीं हो सकती है - निम्न में से कितनी स्ट्रिंग बचने वाले चरित्र को कवर करती है? इसके अलावा, @ जेम्स कर्रेन का जवाब काफी है कि मैं इसे कैसे संभालेगा - हालांकि, जैसा कि वह कहता है कि इसे कुछ साफ करने की जरूरत है। उदाहरण के लिए, लूप प्रारंभकर्ता में जे से 0 शुरू करना। नल इनपुट को संभालने के तरीके को समझना आदि।

शायद आप स्ट्रिंगस्प्लिटऑप्शन का भी समर्थन करना चाहते हैं और यह निर्दिष्ट करते हैं कि संग्रह में खाली स्ट्रिंग को वापस किया जाना चाहिए या नहीं।

+0

+1 सभी अच्छे अंक – BuddyJoe

1

व्यक्तिगत तौर पर मैं धोखा था और परावर्तक का उपयोग कर string.Split पर एक नज़र है ... InternalSplitOmitEmptyEntries उपयोगी लग रहा है ;-)

3
public static string[] Split(this string input, string separator, char escapeCharacter) 
{ 
    Guid g = Guid.NewGuid(); 
    input = input.Replace(escapeCharacter.ToString() + separator, g.ToString()); 
    string[] result = input.Split(new string []{separator}, StringSplitOptions.None); 
    for (int i = 0; i < result.Length; i++) 
    { 
     result[i] = result[i].Replace(g.ToString(), escapeCharacter.ToString() + separator); 
    } 

    return result; 
} 
नहीं

शायद यह ऐसा करने का सबसे अच्छा तरीका है, लेकिन यह एक और विकल्प है। असल में, हर जगह से बचने का अनुक्रम + सेपरेटर पाया जाता है, इसे एक GUID के साथ प्रतिस्थापित करें (आप यहां किसी भी अन्य यादृच्छिक बकवास का उपयोग कर सकते हैं, कोई फर्क नहीं पड़ता)। फिर निर्मित स्प्लिट फ़ंक्शन का उपयोग करें। फिर एस्केप + सेपरेटर के साथ सरणी के प्रत्येक तत्व में guid को प्रतिस्थापित करें।

+0

विभाजन कॉल के बाद, क्या आप केवल विभाजक के साथ जी को प्रतिस्थापित नहीं करेंगे और भागने को शामिल नहीं करेंगे? इससे आपको लौटे स्ट्रिंग से बचने की परेशानी होगी। – rjrapson

+2

यह क्लासिक "प्लेसहोल्डर" पैटर्न है। मुझे प्लेसहोल्डर के रूप में GUID का उपयोग पसंद है। मैं कहूंगा कि यह "शौक" कोड के लिए पर्याप्त है, लेकिन "वैश्विक थर्मोन्यूक्लियर युद्ध" कोड नहीं है। – BuddyJoe

+0

@rjrapson: अच्छा बिंदु। मुझे लगता है कि यह ओपी चाहता था पर निर्भर करता है। मुझे लगता है कि आप इस विधि को एक बूल लेने के लिए बढ़ा सकते हैं कि बचने वाले चरित्र को शामिल करना है या नहीं।@ ब्रूनो: इस दृष्टिकोण के साथ मैं एकमात्र असली मुद्दा यह देखता हूं कि एक ग्रिड में एक "-" है जो विभाजक हो सकता है। – BFree

4

यदि आप बच निकलने वाले चरित्र को हटाना चाहते हैं तो यहां समाधान है।

public static IEnumerable<string> Split(this string input, 
             string separator, 
             char escapeCharacter) { 
    string[] splitted = input.Split(new[] { separator }); 
    StringBuilder sb = null; 

    foreach (string subString in splitted) { 
     if (subString.EndsWith(escapeCharacter.ToString())) { 
      if (sb == null) 
       sb = new StringBuilder(); 
      sb.Append(subString, 0, subString.Length - 1); 
     } else { 
      if (sb == null) 
       yield return subString; 
      else { 
       sb.Append(subString); 
       yield return sb.ToString(); 
       sb = null; 
      } 
     } 
    } 
    if (sb != null) 
     yield return sb.ToString(); 
} 
0
public string RemoveMultipleDelimiters(string sSingleLine) 
{ 
    string sMultipleDelimitersLine = ""; 
    string sMultipleDelimitersLine1 = ""; 
    int iDelimeterPosition = -1; 
    iDelimeterPosition = sSingleLine.IndexOf('>'); 
    iDelimeterPosition = sSingleLine.IndexOf('>', iDelimeterPosition + 1); 
    if (iDelimeterPosition > -1) 
    { 
     sMultipleDelimitersLine = sSingleLine.Substring(0, iDelimeterPosition - 1); 
     sMultipleDelimitersLine1 = sSingleLine.Substring(sSingleLine.IndexOf('>', iDelimeterPosition) - 1); 
     sMultipleDelimitersLine1 = sMultipleDelimitersLine1.Replace('>', '*'); 
     sSingleLine = sMultipleDelimitersLine + sMultipleDelimitersLine1; 
    } 
    return sSingleLine; 
} 
3

आप कुछ इस तरह की कोशिश कर सकते हैं। हालांकि, मैं प्रदर्शन महत्वपूर्ण कार्यों के लिए असुरक्षित कोड के साथ कार्यान्वित करने का सुझाव दूंगा।

public static class StringExtensions 
{ 
    public static string[] Split(this string text, char escapeChar, params char[] seperator) 
    { 
     return Split(text, escapeChar, seperator, int.MaxValue, StringSplitOptions.None); 
    } 

    public static string[] Split(this string text, char escapeChar, char[] seperator, int count) 
    { 
     return Split(text, escapeChar, seperator, count, StringSplitOptions.None); 
    } 

    public static string[] Split(this string text, char escapeChar, char[] seperator, StringSplitOptions options) 
    { 
     return Split(text, escapeChar, seperator, int.MaxValue, options); 
    } 

    public static string[] Split(this string text, char escapeChar, char[] seperator, int count, StringSplitOptions options) 
    { 
     if (text == null) 
     { 
      throw new ArgumentNullException("text"); 
     } 

     if (text.Length == 0) 
     { 
      return new string[0]; 
     } 

     var segments = new List<string>(); 

     bool previousCharIsEscape = false; 
     var segment = new StringBuilder(); 

     for (int i = 0; i < text.Length; i++) 
     { 
      if (previousCharIsEscape) 
      { 
       previousCharIsEscape = false; 

       if (seperator.Contains(text[i])) 
       { 
        // Drop the escape character when it escapes a seperator character. 
        segment.Append(text[i]); 
        continue; 
       } 

       // Retain the escape character when it escapes any other character. 
       segment.Append(escapeChar); 
       segment.Append(text[i]); 
       continue; 
      } 

      if (text[i] == escapeChar) 
      { 
       previousCharIsEscape = true; 
       continue; 
      } 

      if (seperator.Contains(text[i])) 
      { 
       if (options != StringSplitOptions.RemoveEmptyEntries || segment.Length != 0) 
       { 
        // Only add empty segments when options allow. 
        segments.Add(segment.ToString()); 
       } 

       segment = new StringBuilder(); 
       continue; 
      } 

      segment.Append(text[i]); 
     } 

     if (options != StringSplitOptions.RemoveEmptyEntries || segment.Length != 0) 
     { 
      // Only add empty segments when options allow. 
      segments.Add(segment.ToString()); 
     } 

     return segments.ToArray(); 
    } 
} 
+0

आपके दो ओवरलोड्स गिनती करते हैं लेकिन इसका उपयोग नहीं किया जाता है – innominate227

1

मुझे यह समस्या भी थी और समाधान नहीं मिला। इसलिए मैं इस तरह के एक विधि लिखा था अपने आप को:

public static IEnumerable<string> Split(
     this string text, 
     char separator, 
     char escapeCharacter) 
    { 
     var builder = new StringBuilder(text.Length); 

     bool escaped = false; 
     foreach (var ch in text) 
     { 
      if (separator == ch && !escaped) 
      { 
       yield return builder.ToString(); 
       builder.Clear(); 
      } 
      else 
      { 
       // separator is removed, escape characters are kept 
       builder.Append(ch); 
      } 
      // set escaped for next cycle, 
      // or reset unless escape character is escaped. 
      escaped = escapeCharacter == ch && !escaped; 
     } 
     yield return builder.ToString(); 
    } 

यह भागने और unescape, जो विभाजक बच निकलता है और चरित्र से बचने और पात्रों फिर से बचने को हटा के साथ संयोजन में चला जाता है: भागने/unescape

के लिए

public static string Escape(this string text, string controlChars, char escapeCharacter) 
    { 
     var builder = new StringBuilder(text.Length + 3); 
     foreach (var ch in text) 
     { 
      if (controlChars.Contains(ch)) 
      { 
       builder.Append(escapeCharacter); 
      } 
      builder.Append(ch); 
     } 
     return builder.ToString(); 
    } 

    public static string Unescape(string text, char escapeCharacter) 
    { 
     var builder = new StringBuilder(text.Length); 
     bool escaped = false; 
     foreach (var ch in text) 
     { 
      escaped = escapeCharacter == ch && !escaped; 
      if (!escaped) 
      { 
       builder.Append(ch); 
      } 
     } 
     return builder.ToString(); 
    } 

उदाहरण

separator = ',' 
escapeCharacter = '\\' 
//controlCharacters is always separator + escapeCharacter 

@"AB,CD\EF\," <=> @"AB\,CD\\EF\\\," 

अलग करना:

@"AB,CD\,EF\\,GH\\\,IJ" => [@"AB", @"CD\,EF\\", @"GH\\\,IJ"] 

तो इसका उपयोग करने के लिए, जुड़ने से पहले भागें, और स्प्लिट के बाद यूनेस्केप।

संबंधित मुद्दे