Как я могу прочитать текстовый файл, не блокируя его?

95

У меня служба 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();
            }
        }

Есть ли в моем коде проблемы с чтением и записью?

Как решить проблему?

Хомам
источник

Ответы:

122

Вам необходимо убедиться, что и служба, и читатель открывают файл журнала неэксклюзивно. Попробуй это:

Для службы - писателя в вашем примере - используйте 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
   ...
}

Удачи!

Манфред
источник
Также есть версия ReadLines () на stackoverflow.com/questions/5338450/…
Колин
Идеально - я думал, что File.Open () с FileAccess.Read было достаточно, но это не так. Хотя это так. :)
neminem
Что касается писателя, разве FileShare.Read не будет достаточно, поскольку есть только один писатель?
crokusek 06
2
Также стоит отметить, что FileStreamреализуетIDisposable
weeksdev
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++;
        }
    }
}
головы5150
источник
2
нет необходимости явно закрывать поток, поскольку StreamReader.Dispose закрывает его автоматически.
nothrow
2
Я считаю, что файловый ресурс должен быть установлен на ОБЕИХ потоках чтения и записи, а не только на чтение.
LEO
StreamReader.Dispose также избавляется от FileStream, если я не ошибаюсь. Здесь размещается дважды.
Эрегрит
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
@modosansreves да, я удалил эту часть ответа, поскольку в документации указано, что он выдаст, UnauthorizedAccessExceptionесли файл доступен только для чтения - должно быть, пропустил это во время ответа!
Джеймс
5

Я помню, как делал то же самое пару лет назад. После нескольких запросов в Google я обнаружил это:

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

т.е. используйте атрибут FileShare.ReadWrite в FileStream ().

(найдено в блоге Баладжи Рамеша )

дотмартин
источник
1

Вы пробовали скопировать файл, а затем прочитать его?

Просто обновляйте копию всякий раз, когда вносятся большие изменения.

Том Галлен
источник
2
Я пробовал это, и это не лучшее решение. Копирование файла занимает слишком много времени, для файла размером 4 МБ требуется около 20 секунд.
Сэйчи
-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
Насколько я понимаю, этот метод считывает весь файл в строку и при этом проглатывает исключения. Я не понимаю, как это должно помочь с блокировкой файлов.
локаль по умолчанию