Як я можу прочитати текстовий файл, не заблокувавши його?


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

Чи є якісь проблеми у моєму коді у читанні та письмі?

Як вирішити проблему?


Відповіді:


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

Удачі!


Існує також версія ReadLines () за адресою stackoverflow.com/questions/5338450/…
Колін

Ідеально - я думав, що File.Open () за допомогою FileAccess.Read було досить, але це не так. Це, однак. :)
neminem

Щодо письменника, чи не буде FileShare.Read достатньо, оскільки існує лише один автор?
crokusek

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

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якщо файл лише для читання - мабуть, пропустив це під час відповіді!
James

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
Наскільки я розумію, цей метод зчитує цілий файл у рядок і поглинає винятки. Я не розумію, як це може допомогти із блокуванням файлів.
стандартна локалізація
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.