2008-11-13 36 views
20

Tôi đang thử nghiệm cách các lớp FileStream và StreamReader làm việc với máy tính. Thông qua ứng dụng Console. Tôi đang cố gắng để đi vào một tập tin và đọc các dòng và in chúng trên bàn điều khiển.Sự cố FileStream StreamReader trong C#

Tôi đã có thể làm điều đó với một vòng lặp while, nhưng tôi muốn thử nó với vòng lặp foreach.

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.IO; 

namespace testing 
{ 
    public class Program 
    { 
     public static void Main(string[] args) 
     { 
      string file = @"C:\Temp\New Folder\New Text Document.txt"; 
      using(FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read)) 
      { 
       using(StreamReader sr = new StreamReader(fs)) 
       { 
        foreach(string line in file) 
        { 
         Console.WriteLine(line); 
        } 
       } 
      } 
     } 
    } 
} 

Các lỗi tôi tiếp tục nhận được cho việc này là: Không thể chuyển đổi loại 'char' thành 'chuỗi'

Vòng lặp while, mà không làm việc, trông như thế này:

while((line = sr.ReadLine()) != null) 
{ 
    Console.WriteLine(line); 
} 

tôi có lẽ nhìn thấy một cái gì đó thực sự cơ bản, nhưng tôi không thể nhìn thấy nó.

+0

Về đối tượng của foreach (trực tiếp liên quan đến nhận xét của bạn về "năng suất"), tôi khuyên bạn nên chương miễn phí 6 của C# in x - ở đây: http://www.manning.com/skeet/ –

+0

Vấn đề của bạn là mã của bạn đang lặp (foreach) thông qua từng phần tử trong "tệp" (là một chuỗi). Do đó mỗi phần tử là một "char". Do đó thông báo lỗi trình biên dịch mà bạn đang cố gắng chuyển đổi thành loại chuỗi. Bạn nên lặp lại (foreach) thông qua dữ liệu từ luồng. – RichS

Trả lời

22

Để đọc tất cả các dòng trong Văn bản Mới Document.txt:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.IO; 

namespace testing 
{ 
    public class Program 
    { 
     public static void Main(string[] args) 
     { 
      string file = @"C:\Temp\New Folder\New Text Document.txt"; 
      using(FileStream fs = new FileStream(file, FileMode.Open, FileAccess.Read)) 
      {      
       using(StreamReader sr = new StreamReader(fs)) 
       { 
        while(!sr.EndOfStream) 
        { 
         Console.WriteLine(sr.ReadLine()); 
        } 
       } 
      } 
     } 
    } 
} 
3

Vấn đề là ở:

foreach(string line in file) 
{ 
    Console.WriteLine(line); 
} 

của nó bởi vì "tập tin" là chuỗi, và chuỗi thực hiện IEnumerable. Nhưng điều tra viên này trả về "char" và "char" không thể được chuyển đổi thành chuỗi.

Bạn nên sử dụng vòng lặp while, như bạn sayd.

0

Bạn đang liệt kê một chuỗi và khi bạn làm điều đó, bạn sẽ lấy một thẻ vào lúc đó.

Bạn có chắc chắn đây là những gì bạn muốn không?

foreach(string line in file) 
+0

Không thực sự những gì tôi muốn, tôi chỉ kiểm tra khả năng. Tôi chưa bao giờ sử dụng vòng lặp foreach nhiều lắm. – Vordreller

1

Hình như bài tập về nhà với tôi;)

Bạn đang iterating trên tên tập tin (một chuỗi) chính nó mang đến cho bạn một ký tự cùng một lúc. Chỉ cần sử dụng phương pháp trong khi sử dụng chính xác sr.ReadLine().

+0

Đó là bài tập về nhà 'kinda'. Tôi đang làm việc trên một thứ khác có chứa những thứ tương tự. Vì vậy, tôi nhận được để biết các lớp FileStream và StreamReader/StreamWriter một chút ^^ – Vordreller

1

Thay vì sử dụng một StreamReader và sau đó cố gắng tìm đường vào bên trong biến String file, bạn chỉ có thể sử dụng File.ReadAllLines:

string[] lines = File.ReadAllLines(file); 
foreach(string line in lines) 
    Console.WriteLine(line); 
0

A (không nhớ hiệu quả) cách tiếp cận đơn giản của iterating mỗi dòng trong một tập tin là

foreach (string line in File.ReadAllLines(file)) 
{ 
    .. 
} 
+0

Xem bài đăng của tôi cho một phiên bản không đệm này –

35

Nếu bạn muốn đọc một tập tin line-by-line qua foreach (một cách tái sử dụng), hãy xem xét khối iterator sau:

public static IEnumerable<string> ReadLines(string path) 
    { 
     using (StreamReader reader = File.OpenText(path)) 
     { 
      string line; 
      while ((line = reader.ReadLine()) != null) 
      { 
       yield return line; 
      } 
     } 
    } 

Lưu ý rằng điều này được đánh giá rất lười biếng - không có bộ đệm nào bạn liên kết với File.ReadAllLines(). Cú pháp foreach sẽ đảm bảo rằng các iterator là Dispose() d một cách chính xác ngay cả đối với trường hợp ngoại lệ, đóng file:

foreach(string line in ReadLines(file)) 
{ 
    Console.WriteLine(line); 
} 

(bit này được thêm vào chỉ quan tâm ...)

Một ưu điểm khác của việc này loại trừu tượng là nó chơi đẹp với LINQ - tức làthật dễ dàng để thực hiện các phép biến đổi/bộ lọc v.v. với phương pháp này:

 DateTime minDate = new DateTime(2000,1,1); 
     var query = from line in ReadLines(file) 
        let tokens = line.Split('\t') 
        let person = new 
        { 
         Forname = tokens[0], 
         Surname = tokens[1], 
         DoB = DateTime.Parse(tokens[2]) 
        } 
        where person.DoB >= minDate 
        select person; 
     foreach (var person in query) 
     { 
      Console.WriteLine("{0}, {1}: born {2}", 
       person.Surname, person.Forname, person.DoB); 
     } 

Và một lần nữa, tất cả đều được đánh giá một cách lười biếng (không đệm).

+0

Vì net 4.0 này được xây dựng vào BCL như ['File.ReadLines'] (http://msdn.microsoft.com/en-us/library/dd383503 (v = vs.100) .aspx) – CodesInChaos

+0

sẽ không sử dụng 'while (! reader.EndOfStream) {return return reader.ReadLine()}' có hiệu suất tốt hơn một chút không? Rõ ràng không phải bằng một biên độ dài chút nào, nhưng biến 'dòng' ở trên được sử dụng không nơi nào khác ngoài' lợi nhuận', và cá nhân tôi thấy điều kiện dễ đọc hơn theo cách này. – cogumel0

0

Tôi đoán bạn muốn một cái gì đó như thế này:

using (FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read)) 
{ 
    using (StreamReader streamReader = new StreamReader(fileStream)) 
    { 
     string line = ""; 
     while (null != (line = streamReader.ReadLine())) 
     { 
      Console.WriteLine(line); 
     } 
    } 
} 
+1

Đó chính xác là những gì tôi in trong bài viết đầu tiên của tôi, chỉ là một pointername khác cho StreamReader và! = Null ở phía bên kia của vòng lặp while ... – Vordreller

+0

Cũng không hoàn toàn, như công trình của tôi, của bạn thì không. ;-) Vấn đề của bạn là bạn đang thực hiện "foreach" trên tên tệp chứ không phải trên luồng. – RichS

22

Tôi có một lớp LineReader trong dự án MiscUtil tôi. Nó hơi tổng quát hơn các giải pháp đưa ra ở đây, chủ yếu là về cách bạn có thể xây dựng nó:

  • Từ một hàm trả về một dòng suối, trong trường hợp này nó sẽ sử dụng UTF-8
  • Từ một hàm trả về một dòng suối, và một mã hóa
  • Từ một hàm trả về một người đọc văn bản
  • Từ chỉ là một tên tập tin, trong trường hợp này nó sẽ sử dụng UTF-8
  • Từ một tên tập tin và mã hóa

Lớp "sở hữu" bất kỳ tài nguyên nào nó sử dụng và đóng chúng một cách thích hợp. Tuy nhiên, nó thực hiện điều này mà không cần tự thực hiện IDisposable. Đây là lý do tại sao phải mất Func<Stream>Func<TextReader> thay vì luồng hoặc trình đọc trực tiếp - nó cần để có thể hoãn mở cho đến khi cần. Đó là bản thân trình vòng lặp (được tự động xử lý bởi một vòng lặp foreach) đóng tài nguyên.

Như Marc đã chỉ ra, điều này hoạt động thực sự tốt trong LINQ. Một ví dụ tôi muốn đưa ra là:

var errors = from file in Directory.GetFiles(logDirectory, "*.log") 
      from line in new LineReader(file) 
      select new LogEntry(line) into entry 
      where entry.Severity == Severity.Error 
      select entry; 

Điều này sẽ truyền tất cả các lỗi từ toàn bộ các tệp nhật ký, mở và đóng khi nó đi. Kết hợp với Push LINQ, bạn có thể thực hiện tất cả các loại nội dung thú vị :)

Đây không phải là một lớp học đặc biệt "khéo léo", nhưng nó thực sự tiện dụng. Đây là nguồn đầy đủ, để thuận tiện nếu bạn không muốn tải xuống MiscUtil. Giấy phép cho mã nguồn là here.

using System; 
using System.Collections; 
using System.Collections.Generic; 
using System.IO; 
using System.Text; 

namespace MiscUtil.IO 
{ 
    /// <summary> 
    /// Reads a data source line by line. The source can be a file, a stream, 
    /// or a text reader. In any case, the source is only opened when the 
    /// enumerator is fetched, and is closed when the iterator is disposed. 
    /// </summary> 
    public sealed class LineReader : IEnumerable<string> 
    { 
     /// <summary> 
     /// Means of creating a TextReader to read from. 
     /// </summary> 
     readonly Func<TextReader> dataSource; 

     /// <summary> 
     /// Creates a LineReader from a stream source. The delegate is only 
     /// called when the enumerator is fetched. UTF-8 is used to decode 
     /// the stream into text. 
     /// </summary> 
     /// <param name="streamSource">Data source</param> 
     public LineReader(Func<Stream> streamSource) 
      : this(streamSource, Encoding.UTF8) 
     { 
     } 

     /// <summary> 
     /// Creates a LineReader from a stream source. The delegate is only 
     /// called when the enumerator is fetched. 
     /// </summary> 
     /// <param name="streamSource">Data source</param> 
     /// <param name="encoding">Encoding to use to decode the stream 
     /// into text</param> 
     public LineReader(Func<Stream> streamSource, Encoding encoding) 
      : this(() => new StreamReader(streamSource(), encoding)) 
     { 
     } 

     /// <summary> 
     /// Creates a LineReader from a filename. The file is only opened 
     /// (or even checked for existence) when the enumerator is fetched. 
     /// UTF8 is used to decode the file into text. 
     /// </summary> 
     /// <param name="filename">File to read from</param> 
     public LineReader(string filename) 
      : this(filename, Encoding.UTF8) 
     { 
     } 

     /// <summary> 
     /// Creates a LineReader from a filename. The file is only opened 
     /// (or even checked for existence) when the enumerator is fetched. 
     /// </summary> 
     /// <param name="filename">File to read from</param> 
     /// <param name="encoding">Encoding to use to decode the file 
     /// into text</param> 
     public LineReader(string filename, Encoding encoding) 
      : this(() => new StreamReader(filename, encoding)) 
     { 
     } 

     /// <summary> 
     /// Creates a LineReader from a TextReader source. The delegate 
     /// is only called when the enumerator is fetched 
     /// </summary> 
     /// <param name="dataSource">Data source</param> 
     public LineReader(Func<TextReader> dataSource) 
     { 
      this.dataSource = dataSource; 
     } 

     /// <summary> 
     /// Enumerates the data source line by line. 
     /// </summary> 
     public IEnumerator<string> GetEnumerator() 
     { 
      using (TextReader reader = dataSource()) 
      { 
       string line; 
       while ((line = reader.ReadLine()) != null) 
       { 
        yield return line; 
       } 
      } 
     } 

     /// <summary> 
     /// Enumerates the data source line by line. 
     /// </summary> 
     IEnumerator IEnumerable.GetEnumerator() 
     { 
      return GetEnumerator(); 
     } 
    } 
} 
2

Hơi tao nhã hơn là sau ...

using (var fileStream = new FileStream(file, FileMode.Open, FileAccess.Read)) 
{ 
    using (var streamReader = new StreamReader(fileStream)) 
    { 
     while (!streamReader.EndOfStream) 
     { 
      yield return reader.ReadLine(); 
     } 
    } 
} 
Các vấn đề liên quan