如何在不锁定文本的情况下读取文本文件?


94

我有一个Windows服务将其日志以简单格式写入文本文件中。

现在,我将创建一个小型应用程序以读取服务的日志,并将现有日志和添加的日志显示为实时视图。

问题在于该服务锁定了文本文件以添加新行,同时查看器应用程序锁定了该文件以进行读取。

服务代码:

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

查看者代码:

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

我的代码在读写方式上有什么问题吗?

该如何解决呢?


Answers:


120

您需要确保服务和阅读器都以非排他方式打开日志文件。试试这个:

对于服务(在您的示例中为作家),请使用FileStream如下创建的实例:

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

对于读者,请使用相同的文件,但更改文件访问权限:

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

另外,由于FileStream实现器IDisposable确保在两种情况下都考虑使用using语句,例如对于编写器:

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

祝好运!



完美-我以为File.Open()和FileAccess.Read就足够了,但事实并非如此。不过,这是。:)
neminem

对作家来说,FileShare.Read是否足够,因为只有一个作家?
crokusek

2
另外,值得注意的是,该FileStream工具的实现IDisposable
–weekdev

28

在读取文本文件时明确设置共享模式。

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
不需要显式关闭流,因为StreamReader.Dispose自动关闭它。
nothrow

2
我认为文件共享应该同时设置在读写流上,而不仅仅是读取。
LEO 2010年

如果我没有记错的话,StreamReader.Dispose 也会处理FileStream。在这里它被处置了两次。
伊格里斯(Eregrith)

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

->这不会锁定文件。


1
读取数据时似乎可以工作。写入锁定文件时出现错误。
webzy

8

问题是,当您写入日志时,您将排他地锁定文件,因此根本不允许您的StreamReader打开该文件。

您需要尝试以只读模式打开文件。

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

据我所知,File.ReadAllText()只读模式失败。
bohdan_trotsenko 2013年

@modosansreves是的,我删除了答案的这一部分,因为docs声明UnauthorizedAccessException如果文件是只读文件,它将抛出一个-回答时一定错过了!
詹姆斯,

5

我记得几年前做过同样的事情。经过一些谷歌查询,我发现了这一点:

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

即在FileStream()上使用FileShare.ReadWrite属性。

(在Balaji Ramesh的博客上找到)


1

您是否尝试过复制文件然后阅读?

只要进行了大的更改,只要更新副本即可。


2
我已经尝试过了,但这不是最好的解决方案。复制文件的时间过长,而4mb的文件则需要20秒钟。
2015年

-1

此方法将帮助您最快地读取文本文件而不锁定它。

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

希望这种方法对您有帮助。


1
据我了解,此方法将整个文件读取为字符串,并沿途吞入异常。我看不到该如何帮助文件锁定。
默认语言环境
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.