Jak mogę czytać plik tekstowy bez blokowania go?


95

Mam usługę Windows zapisującą swój dziennik w pliku tekstowym w prostym formacie.

Teraz utworzę małą aplikację, która odczyta dziennik usługi i pokaże zarówno istniejący, jak i dodany dziennik jako podgląd na żywo.

Problem polega na tym, że usługa blokuje plik tekstowy do dodawania nowych wierszy, a jednocześnie aplikacja przeglądarki blokuje plik do odczytu.

Kod serwisowy:

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

Kod przeglądarki:

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

Czy jest jakiś problem w moim kodzie podczas czytania i pisania?

Jak rozwiązać problem?


Odpowiedzi:


122

Musisz upewnić się, że zarówno usługa, jak i czytnik otwierają plik dziennika niewyłącznie. Spróbuj tego:

Dla usługi - writer w twoim przykładzie - użyj FileStreaminstancji utworzonej w następujący sposób:

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

Dla czytelnika użyj tego samego, ale zmień dostęp do pliku:

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

Ponadto, ponieważ FileStreamimplementuje, IDisposableupewnij się, że w obu przypadkach rozważasz użycie usinginstrukcji, na przykład dla pisarza:

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

Powodzenia!


Dostępna jest również wersja ReadLines () pod adresem stackoverflow.com/questions/5338450/ ...
Colin

Idealnie - myślałem, że File.Open () z FileAccess.Read wystarczy, ale tak nie jest. Tak jest jednak. :)
neminem

Jeśli chodzi o pisarza, czy FileShare.Read nie wystarczy, skoro jest tylko jeden pisarz?
crokusek

2
Warto również zauważyć, że FileStreamimplementujeIDisposable
weekdev

28

Jawne skonfigurowanie trybu udostępniania podczas czytania pliku tekstowego.

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
nie jest konieczne jawne zamykanie strumienia, ponieważ StreamReader.Dispose zamyka go automatycznie.
nothrow

2
Uważam, że udział plików powinien być ustawiony na OBU strumieni do odczytu i zapisu, a nie tylko do odczytu.
LEO

StreamReader.Dispose usuwa również FileStream, jeśli się nie mylę. Tutaj jest usuwany dwukrotnie.
Eregrith

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

-> to nie blokuje pliku.


1
Wydaje się działać podczas odczytu danych. Podczas zapisywania pojawia się błąd zablokowanego pliku.
webzy

8

Problem polega na tym, że kiedy piszesz do dziennika, blokujesz plik wyłącznie, aby StreamReader nie mógł go w ogóle otworzyć.

Musisz spróbować otworzyć plik w trybie tylko do odczytu .

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

Jak teraz wiem, File.ReadAllText()nie działa w trybie tylko do odczytu.
bohdan_trotsenko

@modosansreves yeah Usunąłem tę część odpowiedzi, ponieważ dokumentacja stwierdza, że ​​wyrzuci, UnauthorizedAccessExceptionjeśli plik jest tylko do odczytu - musiałem to przegapić w momencie odpowiadania!
James

5

Pamiętam, jak robiłem to samo kilka lat temu. Po kilku zapytaniach w Google znalazłem to:

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

tj. użyj atrybutu FileShare.ReadWrite w FileStream ().

(znalezione na blogu Balaji Ramesh )


1

Czy próbowałeś skopiować plik, a następnie go przeczytać?

Wystarczy zaktualizować kopię, gdy zostaną wprowadzone duże zmiany.


2
Próbowałem tego i nie jest to najlepsze rozwiązanie. Kopiowanie pliku trwa zbyt długo, ponieważ plik o rozmiarze 4 MB zajmuje około 20 sekund.
Seichi

-1

Ta metoda pomoże Ci szybciej odczytać plik tekstowy i bez blokowania go.

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

Mam nadzieję, że ta metoda ci pomoże.


1
O ile rozumiem, ta metoda wczytuje cały plik do łańcucha i połyka wyjątki po drodze. Nie rozumiem, jak to ma pomóc w blokowaniu plików.
domyślny język
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.