2009-03-11 77 views
7

Tôi muốn viết một phương thức mở rộng cho lớp .NET String. Tôi muốn nó là một biến thể đặc biệt trên phương thức Split - một biến mất một ký tự thoát để ngăn chặn tách chuỗi khi một ký tự thoát được sử dụng trước dấu phân cách.Phương pháp mở rộng C# - Phân tách chuỗi cũng chấp nhận ký tự thoát

Cách tốt nhất để viết nội dung này là gì? Tôi tò mò về cách thức không phải là regex tốt nhất để tiếp cận nó.
Something với một chữ ký như ...

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

UPDATE: Bởi vì nó đã đưa ra trong một trong những ý kiến, thoát ...

Trong C# khi thoát không ký tự đặc biệt bạn sẽ có được lỗi - CS1009: Chuỗi thoát không được nhận dạng.

Trong IE JScript, các ký tự thoát được loại bỏ. Trừ khi bạn thử \ u và sau đó bạn nhận được lỗi "Số thập lục phân dự kiến". Tôi đã thử nghiệm Firefox và nó có hành vi tương tự.

Tôi muốn phương pháp này được khá khoan dung và tuân theo mô hình JavaScript. Nếu bạn trốn thoát trên một phi phân cách nó chỉ nên "vui lòng" loại bỏ các ký tự thoát.

Trả lời

12

Làm thế nào về:

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

Điều đó dường như hoạt động (với một vài chuỗi kiểm tra nhanh), nhưng nó không loại bỏ ký tự thoát - điều đó sẽ tùy thuộc vào tình huống chính xác của bạn, tôi nghi ngờ.

+0

Dường như bạn đang giả định rằng bất cứ khi nào ký tự thoát xuất hiện, nó được theo sau bởi chuỗi dấu phân cách. Điều gì sẽ xảy ra nếu nó không? – tvanfosson

+0

Tôi chỉ đang nói về những gì trong câu hỏi - nếu ký tự thoát xuất hiện trước dấu phân cách, nó sẽ ngăn chặn dấu phân cách đó được sử dụng để tách. Tôi không cố gắng loại bỏ nhân vật trốn thoát hoặc xử lý nó theo bất kỳ cách nào khác. Naive, có lẽ, nhưng đó là tất cả thông tin chúng tôi có. –

+0

cool, lợi ích của ienumberable khi trả về một mảng chuỗi là gì? – rizzle

7

này sẽ cần phải được dọn dẹp một chút, nhưng điều này về cơ bản nó là ....

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

Chữ ký là không chính xác, bạn cần phải trả lại một mảng chuỗi

Extensions WARNIG KHÔNG BAO GIỜ SỬ DỤNG , vì vậy tha thứ cho tôi về một số lỗi;)

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

thú vị. Tôi sẽ sửa chữa nó trong câu hỏi ban đầu. – BuddyJoe

4

Quan sát đầu tiên của tôi là dấu phân tách phải là một ký tự không phải là chuỗi khi thoát chuỗi bằng một ký tự đơn có thể khó - bao nhiêu ký tự thoát khỏi chuỗi sau đây? Khác hơn thế, câu trả lời của @James Curran là khá nhiều cách tôi sẽ xử lý nó - mặc dù, như ông nói nó cần một số làm sạch. Ví dụ, việc khởi tạo j thành 0 trong bộ khởi tạo vòng lặp. Tìm hiểu cách xử lý các đầu vào rỗng, v.v.

Bạn có thể cũng muốn hỗ trợ StringSplitOptions và chỉ định xem chuỗi rỗng có được trả lại trong bộ sưu tập hay không.

+0

+1 Tất cả các điểm tốt – BuddyJoe

1

Cá nhân tôi muốn lừa dối và có một peek tại String.split sử dụng phản xạ ... InternalSplitOmitEmptyEntries trông hữu ích ;-)

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

Có lẽ không phải là cách tốt nhất để làm điều đó, nhưng đó là sự thay thế khác. Về cơ bản, ở khắp mọi nơi các chuỗi thoát + seperator được tìm thấy, thay thế nó bằng một GUID (bạn có thể sử dụng bất kỳ crap ngẫu nhiên khác ở đây, không quan trọng). Sau đó sử dụng chức năng chia nhỏ được tích hợp sẵn. Sau đó, thay thế guid trong mỗi phần tử của mảng bằng escape + seperator.

+0

Sau cuộc gọi chia đôi, bạn sẽ không thay thế g chỉ bằng dấu phân tách và không bao gồm thoát? Điều đó sẽ giúp bạn tiết kiệm những rắc rối của việc phải loại bỏ sự thoát khỏi chuỗi trả về. – rjrapson

+2

Đây là mẫu "giữ chỗ" cổ điển. Tôi thích việc sử dụng GUID như trình giữ chỗ. Tôi sẽ nói rằng điều này là đủ tốt cho mã "sở thích", nhưng không phải là mã "Chiến tranh nhiệt hạch hạt nhân toàn cầu". – BuddyJoe

+0

@rjrapson: Điểm tốt. Tôi đoán nó phụ thuộc vào những gì OP muốn. Tôi đoán bạn có thể mở rộng phương pháp này để có một bool hay không bao gồm các ký tự thoá[email protected]: Vấn đề duy nhất thực sự tôi thấy với cách tiếp cận này, là một Guid bao gồm một "-" mà có thể là tách. – BFree

4

Đây là giải pháp nếu bạn muốn xóa ký tự thoát.

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ạn có thể thử một cái gì đó như thế này. Mặc dù, tôi sẽ đề xuất triển khai với mã không an toàn cho các tác vụ quan trọng về hiệu suất.

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

hai trong số quá tải của bạn được tính nhưng không được sử dụng – innominate227

1

Tôi cũng gặp vấn đề này và không tìm thấy giải pháp. Vì vậy, tôi đã viết một phương pháp như vậy bản thân mình:

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

Nó đi kết hợp với thoát và unescape, mà thoát khỏi sự phân cách và thoát khỏi nhân vật và loại bỏ thoát khỏi nhân vật một lần nữa:

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

Ví dụ cho thoát/unescape

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

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

Split:

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

Vì vậy, để sử dụng nó, Escape trước khi Join và Unescape sau khi Split.

Các vấn đề liên quan