2012-03-26 21 views
16

Cách tốt nhất để có chức năng của phương pháp StreamReader.ReadLine() là gì, nhưng với các dấu phân cách tùy chỉnh (Chuỗi)?C# StreamReader, "ReadLine" Đối với các dấu phân cách tùy chỉnh

Tôi muốn làm điều gì đó như:

String text; 
while((text = myStreamReader.ReadUntil("my_delim")) != null) 
{ 
    Console.WriteLine(text); 
} 

Tôi đã cố gắng để làm của riêng của tôi sử dụng Peek()StringBuilder, nhưng nó quá kém hiệu quả. Tôi đang tìm kiếm các đề xuất hoặc có thể là một giải pháp nguồn mở.

Cảm ơn.

Sửa

Lẽ ra tôi nên làm rõ việc này trước đó ... Tôi đã thấy this answer, tuy nhiên, tôi không muốn đọc toàn bộ tập tin vào bộ nhớ.

+0

Tại sao không sử dụng ReadLine() và sau đó tìm kiếm delimiter trong chuỗi? –

+0

Bằng cách sử dụng 'Peek()' và 'StringBuilder', về cơ bản bạn đang nhân bản những gì' ReadLine() 'làm bên trong' StreamReader' ... do đó, nó có vẻ lạ đối với tôi đó là quá chậm; bạn có thể đăng những gì bạn đã thử không? – digEmAll

+0

Không hiệu quả? Làm thế nào không hiệu quả? Hiệu suất có thiếu đáng kể không? –

Trả lời

2

tôi figured tôi sẽ gửi giải pháp của riêng tôi. Dường như nó hoạt động khá tốt và mã tương đối đơn giản. Hãy bình luận.

public static String ReadUntil(this StreamReader sr, String delim) 
{ 
    StringBuilder sb = new StringBuilder(); 
    bool found = false; 

    while (!found && !sr.EndOfStream) 
    { 
     for (int i = 0; i < delim.Length; i++) 
     { 
      Char c = (char)sr.Read(); 
      sb.Append(c); 

      if (c != delim[i]) 
       break; 

      if (i == delim.Length - 1) 
      { 
       sb.Remove(sb.Length - delim.Length, delim.Length); 
       found = true; 
      } 
     } 
    } 

    return sb.ToString(); 
} 
+1

Nó sẽ là hơi rõ ràng hơn (với tôi) nếu bạn đặt một "break" ngay sau khi "tìm thấy = true" là tốt. Yêu cầu xử lý ít tinh thần hơn một chút. –

+3

Giải pháp này chỉ hoạt động trong một số trường hợp. Ví dụ, nếu dấu phân tách là "xy", thì thuật toán này sẽ bỏ dấu phân tách trong "axxyb" và nó sẽ đọc cho đến cuối dòng. –

1

Mã này sẽ hoạt động đối với bất kỳ dấu tách chuỗi nào.

public static IEnumerable<string> ReadChunks(this TextReader reader, string chunkSep) 
{ 
    var sb = new StringBuilder(); 

    var sepbuffer = new Queue<char>(chunkSep.Length); 
    var sepArray = chunkSep.ToCharArray(); 

    while (reader.Peek() >= 0) 
    { 
     var nextChar = (char)reader.Read(); 
     if (nextChar == chunkSep[sepbuffer.Count]) 
     { 
      sepbuffer.Enqueue(nextChar); 
      if (sepbuffer.Count == chunkSep.Length) 
      { 
       yield return sb.ToString(); 
       sb.Length = 0; 
       sepbuffer.Clear(); 
      } 
     } 
     else 
     { 
      sepbuffer.Enqueue(nextChar); 
      while (sepbuffer.Count > 0) 
      { 
       sb.Append(sepbuffer.Dequeue()); 
       if (sepbuffer.SequenceEqual(chunkSep.Take(sepbuffer.Count))) 
        break; 
      } 
     } 
    } 
    yield return sb.ToString() + new string(sepbuffer.ToArray()); 
} 

Disclaimer:

tôi đã thực hiện một thử nghiệm nhỏ về vấn đề này và thực sự là chậm hơn so với ReadLine phương pháp, nhưng tôi nghi ngờ đó là do sự enqueue/dequeue/sequenceEqual gọi rằng trong phương pháp ReadLine thể tránh (vì dấu phân cách luôn là \r\n).

Một lần nữa, tôi đã thực hiện một số bài kiểm tra và nó sẽ hoạt động, nhưng đừng coi nó là hoàn hảo, và cảm thấy tự do để sửa nó. ;)

1

Dưới đây là trình phân tích cú pháp đơn giản mà tôi đã sử dụng khi cần thiết (thường là khi phát trực tiếp không phải là chỉ đọc và .Split thực hiện công việc), không quá tối ưu nhưng phải hoạt động tốt:
phương pháp - và nhiều hơn nữa ghi chú dưới đây)

public static IEnumerable<string> Split(this Stream stream, string delimiter, StringSplitOptions options) 
    { 
     var buffer = new char[_bufffer_len]; 
     StringBuilder output = new StringBuilder(); 
     int read; 
     using (var reader = new StreamReader(stream)) 
     { 
      do 
      { 
       read = reader.ReadBlock(buffer, 0, buffer.Length); 
       output.Append(buffer, 0, read); 

       var text = output.ToString(); 
       int id = 0, total = 0; 
       while ((id = text.IndexOf(delimiter, id)) >= 0) 
       { 
        var line = text.Substring(total, id - total); 
        id += delimiter.Length; 
        if (options != StringSplitOptions.RemoveEmptyEntries || line != string.Empty) 
         yield return line; 
        total = id; 
       } 
       output.Remove(0, total); 
      } 
      while (read == buffer.Length); 
     } 

     if (options != StringSplitOptions.RemoveEmptyEntries || output.Length > 0) 
      yield return output.ToString(); 
    } 

... và bạn chỉ có thể chuyển sang char delimiters nếu cần thiết chỉ cần thay thế

while ((id = text.IndexOf(delimiter, id)) >= 0) 

... với

while ((id = text.IndexOfAny(delimiters, id)) >= 0) 

(và id++ thay vì id+= và chữ ký this Stream stream, StringSplitOptions options, params char[] delimiters)

... cũng loại bỏ trống, vv
hy vọng nó sẽ giúp

0
public static String ReadUntil(this StreamReader streamReader, String delimiter) 
    { 
     StringBuilder stringBuilder = new StringBuilder(); 

     while (!streamReader.EndOfStream) 
     { 
      stringBuilder.Append(value: (Char) streamReader.Read()); 

      if (stringBuilder.ToString().EndsWith(value: delimiter)) 
      { 
       stringBuilder.Remove(stringBuilder.Length - delimiter.Length, delimiter.Length); 
       break; 
      } 
     } 

     return stringBuilder.ToString(); 
    } 
Các vấn đề liên quan