2015-01-27 20 views
6

Tất cả các phương thức string.Split dường như trả về một mảng các chuỗi (string[]).Có một `String.Split` lười biếng trong C#

Tôi tự hỏi nếu có một biến thể lười biếng trả về một IEnumerable<string> sao cho một chuỗi lớn (hoặc chiều dài vô hạn IEnumerable<char>), khi người ta chỉ quan tâm đến một chuỗi đầu tiên, một lần lưu nỗ lực tính toán cũng như bộ nhớ . Nó cũng có thể hữu ích nếu chuỗi được xây dựng bởi một thiết bị/chương trình (mạng, thiết bị đầu cuối, đường ống) và toàn bộ dây là không cần thiết ngay lập tức hoàn toàn có sẵn. Như vậy, người ta đã có thể xử lý các lần xuất hiện đầu tiên.

Có phương pháp như vậy trong khuôn khổ .NET không?

+0

C# không có thư viện chuẩn. Dường như bạn đang đề cập đến .NET Framework, mà không phải là cụ thể cho C#, VB.NET hoặc bất kỳ ngôn ngữ cụ thể nào khác. –

+0

@JohnSaunders: đã sửa đổi ... –

Trả lời

4

Không có nội dung nào được tích hợp sẵn. Regex.Matches là lười biếng nếu tôi giải thích mã bị giải mã một cách chính xác. Có lẽ bạn có thể tận dụng điều đó.

Hoặc, bạn chỉ cần viết chức năng chia của riêng bạn.

Thực ra, bạn có thể hình ảnh hầu hết các chức năng string được khái quát hóa thành chuỗi tùy ý. Thông thường, ngay cả các chuỗi T, không chỉ là char. BCL không nhấn mạnh rằng ở khái quát hóa tất cả. Ví dụ: không có Enumerable.Subsequence.

+3

Shame 'Regex.Split' rõ ràng không phải là lười biếng. – Rawling

+0

Tôi muốn .NET đã bao gồm một mảng "không thay đổi kiểu T"; 'Chuỗi' có thể chỉ đơn giản là viết tắt của" mảng bất biến của char ". Tôi biết có rất nhiều lần tôi đã sử dụng "mảng bất biến của Byte" hoặc "mảng bất biến của Int32" nếu chúng tồn tại, và hy vọng tổng quát sẽ hữu ích trong nhiều trường hợp khác nữa. – supercat

+0

@supercat: đúng, đó là cách Haskell xử lý chuỗi. Nó cho phép khái quát hóa rất nhiều phương thức chuỗi để liệt kê ... –

2

Không có gì built-in, nhưng cảm thấy tự do để rip phương pháp của tôi Tokenize:

/// <summary> 
/// Splits a string into tokens. 
/// </summary> 
/// <param name="s">The string to split.</param> 
/// <param name="isSeparator"> 
/// A function testing if a code point at a position 
/// in the input string is a separator. 
/// </param> 
/// <returns>A sequence of tokens.</returns> 
IEnumerable<string> Tokenize(string s, Func<string, int, bool> isSeparator = null) 
{ 
    if (isSeparator == null) isSeparator = (str, i) => !char.IsLetterOrDigit(str, i); 

    int startPos = -1; 

    for (int i = 0; i < s.Length; i += char.IsSurrogatePair(s, i) ? 2 : 1) 
    { 
     if (!isSeparator(s, i)) 
     { 
      if (startPos == -1) startPos = i; 
     } 
     else if (startPos != -1) 
     { 
      yield return s.Substring(startPos, i - startPos); 
      startPos = -1; 
     } 
    } 

    if (startPos != -1) 
    { 
     yield return s.Substring(startPos); 
    } 
} 
1

Không có built-in phương pháp để thực hiện điều này như xa như tôi biết. Nhưng nó không có nghĩa là bạn không thể viết một. Dưới đây là mẫu để cung cấp cho bạn ý tưởng:

public static IEnumerable<string> SplitLazy(this string str, params char[] separators) 
{ 
    List<char> temp = new List<char>(); 
    foreach (var c in str) 
    { 
     if (separators.Contains(c) && temp.Any()) 
     { 
      yield return new string(temp.ToArray()); 
      temp.Clear(); 
     } 
     else 
     { 
      temp.Add(c); 
     } 
    } 
    if(temp.Any()) { yield return new string(temp.ToArray()); } 
} 

Nguồn gốc không xử lý tất cả các trường hợp và có thể cải thiện.

+2

Điều này chỉ chia tách các ký tự chứ không phải các chuỗi. – Servy

4

Bạn có thể dễ dàng viết một:

public static class StringExtensions 
{ 
    public static IEnumerable<string> Split(this string toSplit, params char[] splits) 
    { 
     if (string.IsNullOrEmpty(toSplit)) 
      yield break; 

     StringBuilder sb = new StringBuilder(); 

     foreach (var c in toSplit) 
     { 
      if (splits.Contains(c)) 
      { 
       yield return sb.ToString(); 
       sb.Clear(); 
      } 
      else 
      { 
       sb.Append(c); 
      } 
     } 

     if (sb.Length > 0) 
      yield return sb.ToString(); 
    } 
} 

Rõ ràng, tôi đã không kiểm tra nó cho chẵn lẻ với String.split, nhưng tôi tin rằng nó sẽ làm việc chỉ là về giống.

Khi Servy ghi chú, điều này không chia thành chuỗi. Điều đó không đơn giản, và không hiệu quả, nhưng về cơ bản nó cũng giống như vậy.

public static IEnumerable<string> Split(this string toSplit, string[] separators) 
{ 
    if (string.IsNullOrEmpty(toSplit)) 
     yield break; 

    StringBuilder sb = new StringBuilder(); 
    foreach (var c in toSplit) 
    { 
     var s = sb.ToString(); 
     var sep = separators.FirstOrDefault(i => s.Contains(i)); 
     if (sep != null) 
     { 
      yield return s.Replace(sep, string.Empty); 
      sb.Clear(); 
     } 
     else 
     { 
      sb.Append(c); 
     } 
    } 

    if (sb.Length > 0) 
     yield return sb.ToString(); 
} 
+0

Điều này chỉ chia tách các ký tự chứ không phải các chuỗi. – Servy

1

Tôi đã viết biến thể này cũng hỗ trợ SplitOptions và đếm. Nó hoạt động giống như string.Split trong tất cả các trường hợp thử nghiệm tôi đã thử. Toán tử nameof là C# 6 sepcific và có thể được thay thế bằng "count".

public static class StringExtensions 
{ 
    /// <summary> 
    /// Splits a string into substrings that are based on the characters in an array. 
    /// </summary> 
    /// <param name="value">The string to split.</param> 
    /// <param name="options"><see cref="StringSplitOptions.RemoveEmptyEntries"/> to omit empty array elements from the array returned; or <see cref="StringSplitOptions.None"/> to include empty array elements in the array returned.</param> 
    /// <param name="count">The maximum number of substrings to return.</param> 
    /// <param name="separator">A character array that delimits the substrings in this string, an empty array that contains no delimiters, or null. </param> 
    /// <returns></returns> 
    /// <remarks> 
    /// Delimiter characters are not included in the elements of the returned array. 
    /// If this instance does not contain any of the characters in separator the returned sequence consists of a single element that contains this instance. 
    /// If the separator parameter is null or contains no characters, white-space characters are assumed to be the delimiters. White-space characters are defined by the Unicode standard and return true if they are passed to the <see cref="Char.IsWhiteSpace"/> method. 
    /// </remarks> 
    public static IEnumerable<string> SplitLazy(this string value, int count = int.MaxValue, StringSplitOptions options = StringSplitOptions.None, params char[] separator) 
    { 
     if (count <= 0) 
     { 
      if (count < 0) throw new ArgumentOutOfRangeException(nameof(count), "Count cannot be less than zero."); 
      yield break; 
     } 

     Func<char, bool> predicate = char.IsWhiteSpace; 
     if (separator != null && separator.Length != 0) 
      predicate = (c) => separator.Contains(c); 

     if (string.IsNullOrEmpty(value) || count == 1 || !value.Any(predicate)) 
     { 
      yield return value; 
      yield break; 
     } 

     bool removeEmptyEntries = (options & StringSplitOptions.RemoveEmptyEntries) != 0; 
     int ct = 0; 
     var sb = new StringBuilder(); 
     for (int i = 0; i < value.Length; ++i) 
     { 
      char c = value[i]; 
      if (!predicate(c)) 
      { 
       sb.Append(c); 
      } 
      else 
      { 
       if (sb.Length != 0) 
       { 
        yield return sb.ToString(); 
        sb.Clear(); 
       } 
       else 
       { 
        if (removeEmptyEntries) 
         continue; 
        yield return string.Empty; 
       } 

       if (++ct >= count - 1) 
       { 
        if (removeEmptyEntries) 
         while (++i < value.Length && predicate(value[i])); 
        else 
         ++i; 
        if (i < value.Length - 1) 
        { 
         sb.Append(value, i, value.Length - i); 
         yield return sb.ToString(); 
        } 
        yield break; 
       } 
      } 
     } 

     if (sb.Length > 0) 
      yield return sb.ToString(); 
     else if (!removeEmptyEntries && predicate(value[value.Length - 1])) 
      yield return string.Empty; 
    } 

    public static IEnumerable<string> SplitLazy(this string value, params char[] separator) 
    { 
     return value.SplitLazy(int.MaxValue, StringSplitOptions.None, separator); 
    } 

    public static IEnumerable<string> SplitLazy(this string value, StringSplitOptions options, params char[] separator) 
    { 
     return value.SplitLazy(int.MaxValue, options, separator); 
    } 

    public static IEnumerable<string> SplitLazy(this string value, int count, params char[] separator) 
    { 
     return value.SplitLazy(count, StringSplitOptions.None, separator); 
    } 
} 
0

Tôi muốn có chức năng của Regex.Split, nhưng ở dạng được đánh giá rất lười. Mã dưới đây chỉ chạy qua tất cả Matches trong chuỗi đầu vào, và tạo ra kết quả tương tự như Regex.Split:

public static IEnumerable<string> Split(string input, string pattern, RegexOptions options = RegexOptions.None) 
{ 
    // Always compile - we expect many executions 
    var regex = new Regex(pattern, options | RegexOptions.Compiled); 

    int currentSplitStart = 0; 
    var match = regex.Match(input); 

    while (match.Success) 
    { 
     yield return input.Substring(currentSplitStart, match.Index - currentSplitStart); 

     currentSplitStart = match.Index + match.Length; 
     match = match.NextMatch(); 
    } 

    yield return input.Substring(currentSplitStart); 
} 

Lưu ý rằng việc sử dụng điều này với các tham số mô hình @"\s" sẽ cung cấp cho bạn kết quả tương tự như string.Split().

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