2009-03-11 18 views
7

.NET Dize sınıfı için bir uzantı yöntemi yazmak istiyorum. Split yönteminde özel bir varation olmasını isterim - ayırıcıdan önce bir kaçış karakteri kullanıldığında dizgenin bölünmesini önlemek için bir kaçış karakteri alır.C# Uzantı Yöntemi - Dize Bölmesi de bir Kaçış Karakteri kabul eder

Bunu yazmanın en iyi yolu nedir? Ona yaklaşmanın en iyi non-regex yolu hakkında merak ediyorum. Bir imza gibi olan
şey ...

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

GÜNCELLEME: bu birinde

C#

olmayan özel kaçan karakterleri yorum, önceleniminin ... geldi Çünkü Alacağınız hata - CS1009: Tanınmayan çıkış dizisi.

IE JScript'te, çıkış karakterleri atılıyor. Eğer denemediyseniz ve sonra "Beklenen bir onaltılı rakam" hatası alırsınız. Firefox'u test ettim ve aynı davranışı var.

Bu yöntemin güzel bir bağışlayıcı olmasını ve JavaScript modelini izlemesini istiyorum. Bir ayırıcıdan kaçarsanız, kaçış karakterini "nazikçe" kaldırmalıdır.

cevap

12

ne dersiniz:

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

Bu işe yarayacak gibi görünüyor (birkaç hızlı test dizgisiyle), ama kaçış karakterini kaldırmıyor - ki bu sizin durumunuza bağlı olacaktır, şüpheliyim.

+0

Görünüşe göre kaçış karakterinin görüntülendiği her zaman ayırıcı dizgenin izlediği varsayılıyor. Ya değilse? – tvanfosson

+0

Sadece soruda olana gidiyorum - ayırma karakterinin ayırıcıdan önce ortaya çıkması durumunda, ayırıcının ayırma için kullanılmasını önlemelidir. Kaçış karakterini kaldırmaya veya başka bir şekilde işlemeye çalışmıyorum. Naif, belki, ama sahip olduğumuz tüm bilgiler bu. –

+0

cool, bir dizi dizisi döndürmek için ienumberable yararı nedir? – rizzle

7

imza yanlıştır ....

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

Bu Biraz temizlik gerekecektir, ama bu aslında öyle, bir dize dizisi

warnig KULLANILAN ASLA uzantılarını dönmek gerekir böylece bazı hatalar hakkında beni affet;)

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

güzel yakalama. Bunu orijinal soruda düzelteceğim. – BuddyJoe

4

İlk gözlemim, ayırıcının tek bir karakter kullanarak bir dizeden kaçtığından beri bir karakter dizisi olmamasının zor olabileceğidir - aşağıdaki dize kaç saniye kaçış karakterini kapsıyor? Bunun dışında, @James Curran'ın cevabı, bunu nasıl halledebileceğime dair oldukça fazla. Örneğin, döngü başlatıcıda j'yi 0'a sıfırlama. Null girişlerin nasıl ele alınacağını belirleme.

Ayrıca StringSplitOptions'ı da desteklemek ve boş dizenin koleksiyona geri gönderilip gönderilmeyeceğini belirtmek isteyebilirsiniz.

+0

+1 Tüm iyi puanlar – BuddyJoe

1

Şahsen hile ve reflektör kullanarak string.split bir göz var ... InternalSplitOmitEmptyEntries Muhtemelen

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; 
} 

değil bunu yapmanın en iyi yolu ;-) kullanışlı görünüyor, ama başka bir alternatif olduğunu ederim. Temel olarak, her yerde kaçış + ayırıcı dizisi bulunur, onu bir GUID ile değiştirin (burada başka rastgele bir sargı kullanabilirsiniz, önemli değil). Sonra yerleşik bölünmüş işlevi kullanın. Ardından, dizinin her öğesindeki guid öğesini escape + seperator ile değiştirin.

+0

Bölünmüş aramayı tamamladıktan sonra, g'yi sadece ayırıcı ile değiştirmez ve kaçmayı dahil etmez misiniz? Bu, geri dönen dizeden kaçmayı kaldırmanızın zahmetinden kurtulacaktır. – rjrapson

+2

Klasik "yer tutucu" desen budur. GUID'in yer tutucu olarak kullanılmasını seviyorum. Bunun "hobi" kodu için yeterli olduğunu, ancak "Global Thermonuclear War" kodunun olmadığını söyleyebilirim. – BuddyJoe

+0

@rjrapson: İyi nokta. Bence OP'nin istediği şeye bağlı. Sanırım kaçma karakterini dahil edip etmeyeceğimizi bir bool almak için bu yöntemi [email protected]: Bu yaklaşımda gördüğüm tek gerçek mesele, bir Kılavuzun, ayırıcı olabilecek bir "-" içermesidir. – BFree

4

Çıkış karakterini kaldırmak istiyorsanız, çözüm budur.

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

Böyle bir şey deneyebilirsiniz. Bununla birlikte, performans açısından kritik görevler için güvenli olmayan kodla uygulamanızı öneririm.

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

aşırı yüklerinizden iki tanesi sayılıyor ancak kullanılmıyor – innominate227

1

Bu problemi de yaşadım ve bir çözüm bulamadım. Bu yüzden kendimi böyle bir yöntem yazdı:

Bu ayırıcı kaçar ve kaçış karakteri ve yeniden karakterlerden kurtulmak kaldırır Escape ve Unescape ile birlikte gider
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(); 
    } 

: kaçış/çıkış yapılmış

için

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

Örnekler

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

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

Bölünmüş:

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

Bunu kullanmak için, Bölmeden Önce Kaçış ve Ayrıldıktan Sonra Unescape.