Làm cách nào tôi có thể đọc một tệp văn bản mà không cần khóa nó?


94

Tôi có một dịch vụ windows ghi nhật ký của nó vào một tệp văn bản ở định dạng đơn giản.

Bây giờ, tôi sẽ tạo một ứng dụng nhỏ để đọc nhật ký của dịch vụ và hiển thị cả nhật ký hiện có và nhật ký đã thêm dưới dạng xem trực tiếp.

Vấn đề là dịch vụ khóa tệp văn bản để thêm dòng mới và đồng thời ứng dụng trình xem khóa tệp để đọc.

Mã dịch vụ:

void WriteInLog(string logFilePath, data)
{
    File.AppendAllText(logFilePath, 
                       string.Format("{0} : {1}\r\n", DateTime.Now, data));
}

Mã người xem:

int index = 0;
private void Form1_Load(object sender, EventArgs e)
        {
            try
            {
                using (StreamReader sr = new StreamReader(logFilePath))
                {
                    while (sr.Peek() >= 0)  // reading the old data
                    {
                        AddLineToGrid(sr.ReadLine());
                        index++;
                    }
                    sr.Close();
                }

                timer1.Start();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }


private void timer1_Tick(object sender, EventArgs e)
        {
            using (StreamReader sr = new StreamReader(logFilePath))
            {
                // skipping the old data, it has read in the Form1_Load event handler
                for (int i = 0; i < index ; i++) 
                    sr.ReadLine();

                while (sr.Peek() >= 0) // reading the live data if exists
                {
                    string str = sr.ReadLine();
                    if (str != null)
                    {
                        AddLineToGrid(str);
                        index++;
                    }
                }
                sr.Close();
            }
        }

Có vấn đề gì trong mã của tôi trong cách đọc và viết không?

Làm thế nào để giải quyết vấn đề?


Câu trả lời:


120

Bạn cần đảm bảo rằng cả dịch vụ và trình đọc đều mở tệp nhật ký không độc quyền. Thử cái này:

Đối với dịch vụ - người viết trong ví dụ của bạn - hãy sử dụng một FileStreamphiên bản được tạo như sau:

var outStream = new FileStream(logfileName, FileMode.Open, 
                               FileAccess.Write, FileShare.ReadWrite);

Đối với người đọc sử dụng tương tự nhưng thay đổi quyền truy cập tệp:

var inStream = new FileStream(logfileName, FileMode.Open, 
                              FileAccess.Read, FileShare.ReadWrite);

Ngoài ra, vì các FileStreamchương trình triển khai, IDisposablehãy đảm bảo rằng trong cả hai trường hợp, bạn cân nhắc sử dụng một usingcâu lệnh, ví dụ cho người viết:

using(var outStream = ...)
{
   // using outStream here
   ...
}

Chúc may mắn!


Ngoài ra còn có phiên bản ReadLines () tại stackoverflow.com/questions/5338450/…
Colin

Hoàn hảo - Tôi đã nghĩ File.Open () với FileAccess.Read là đủ, nhưng không phải vậy. Đây là, mặc dù. :)
neminem

Về người viết, chẳng phải FileShare.Read là đủ vì chỉ có một người viết?
crokusek

2
Ngoài ra, đáng chú ý là FileStreamthực hiệnIDisposable
tuần

28

Rõ ràng thiết lập chế độ chia sẻ trong khi đọc tệp văn bản.

using (FileStream fs = new FileStream(logFilePath, 
                                      FileMode.Open, 
                                      FileAccess.Read,    
                                      FileShare.ReadWrite))
{
    using (StreamReader sr = new StreamReader(fs))
    {
        while (sr.Peek() >= 0) // reading the old data
        {
           AddLineToGrid(sr.ReadLine());
           index++;
        }
    }
}

2
không cần thiết phải đóng luồng một cách rõ ràng, vì StreamReader.Dispose tự động đóng luồng.
nothrow

2
Tôi tin rằng chia sẻ tệp nên được đặt trên CẢ luồng đọc và ghi, và không chỉ đọc.
LEO

StreamReader.Dispose cũng loại bỏ FileStream nếu tôi không nhầm. Ở đây nó được xử lý hai lần.
Eregrith

12
new StreamReader(File.Open(logFilePath, 
                           FileMode.Open, 
                           FileAccess.Read, 
                           FileShare.ReadWrite))

-> điều này không khóa tệp.


1
Có vẻ hoạt động khi đọc dữ liệu. Khi ghi lỗi tập tin bị khóa.
webzy

8

Vấn đề là khi bạn đang ghi vào nhật ký, bạn đang khóa tệp độc quyền, vì vậy StreamReader của bạn sẽ không được phép mở nó.

Bạn cần thử mở tệp ở chế độ chỉ đọc .

using (FileStream fs = new FileStream("myLogFile.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
{
    using (StreamReader sr = new StreamReader(fs))
    {
        while (!fs.EndOfStream)
        {
            string line = fs.ReadLine();
            // Your code here
        }
    }
}

Như bây giờ tôi biết, File.ReadAllText()không thành công với chế độ chỉ đọc.
bohdan_trotsenko

@modosansreves vâng Tôi đã xóa phần đó của câu trả lời vì tài liệu nói rằng nó sẽ ném ra UnauthorizedAccessExceptionnếu tệp chỉ được đọc - chắc hẳn đã bỏ lỡ điều đó tại thời điểm trả lời!
James

5

Tôi nhớ mình đã làm điều tương tự cách đây vài năm. Sau một số truy vấn của google, tôi đã tìm thấy điều này:

    FileStream fs = new FileStream(@”c:\test.txt”, 
                                   FileMode.Open, 
                                   FileAccess.Read,        
                                   FileShare.ReadWrite);

tức là sử dụng thuộc tính FileShare.ReadWrite trên FileStream ().

(tìm thấy trên blog của Balaji Ramesh )


1

Bạn đã thử sao chép tệp rồi đọc nó chưa?

Chỉ cần cập nhật bản sao bất cứ khi nào thay đổi lớn được thực hiện.


2
Tôi đã thử điều này và nó không phải là giải pháp tốt nhất. Việc sao chép tệp mất quá nhiều thời gian, đối với tệp 4mb thì mất khoảng 20 giây.
Seichi

-1

Phương pháp này sẽ giúp bạn đọc nhanh nhất một tệp văn bản mà không bị khóa.

private string ReadFileAndFetchStringInSingleLine(string file)
    {
        StringBuilder sb;
        try
        {
            sb = new StringBuilder();
            using (FileStream fs = File.Open(file, FileMode.Open))
            {
                using (BufferedStream bs = new BufferedStream(fs))
                {
                    using (StreamReader sr = new StreamReader(bs))
                    {
                        string str;
                        while ((str = sr.ReadLine()) != null)
                        {
                            sb.Append(str);
                        }
                    }
                }
            }
            return sb.ToString();
        }
        catch (Exception ex)
        {
            return "";
        }
    }

Hy vọng phương pháp này sẽ giúp ích cho bạn.


1
Theo như tôi hiểu, phương pháp này đọc toàn bộ tệp thành một chuỗi và nuốt các ngoại lệ trên đường đi. Tôi không thể hiểu cách đó sẽ giúp khóa tệp như thế nào.
ngôn ngữ mặc định
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.