Wie kann ich eine Textdatei lesen, ohne sie zu sperren?

93

Ich habe einen Windows-Dienst, der sein Protokoll in eine Textdatei in einem einfachen Format schreibt.

Jetzt werde ich eine kleine Anwendung erstellen, um das Protokoll des Dienstes zu lesen und sowohl das vorhandene als auch das hinzugefügte Protokoll als Live-Ansicht anzuzeigen.

Das Problem ist, dass der Dienst die Textdatei zum Hinzufügen der neuen Zeilen sperrt und gleichzeitig die Viewer-Anwendung die Datei zum Lesen sperrt.

Der Service Code:

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

Der Viewer-Code:

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

Gibt es ein Problem in meinem Code beim Lesen und Schreiben?

Wie löse ich das Problem?

Homam
quelle

Antworten:

118

Sie müssen sicherstellen, dass sowohl der Dienst als auch der Leser die Protokolldatei nicht ausschließlich öffnen. Versuche dies:

Verwenden Sie für den Dienst - den Writer in Ihrem Beispiel - eine FileStreamInstanz, die wie folgt erstellt wurde:

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

Verwenden Sie für den Leser dasselbe, ändern Sie jedoch den Dateizugriff:

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

Da FileStreamImplementierungen IDisposablesicherstellen, dass Sie in beiden Fällen eine usingAnweisung verwenden, z. B. für den Writer:

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

Viel Glück!

Manfred
quelle
Es gibt auch die ReadLines () Version unter stackoverflow.com/questions/5338450/…
Colin
Perfekt - Ich hatte gedacht, File.Open () mit FileAccess.Read sei genug, ist es aber nicht. Dies ist jedoch. :)
Neminem
Wäre FileShare.Read auf dem Writer nicht genug, da es nur einen Writer gibt?
Crokusek
2
FileStreamIDisposable
Erwähnenswert ist
27

Richten Sie den Freigabemodus explizit ein, während Sie die Textdatei lesen.

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++;
        }
    }
}
Köpfe5150
quelle
2
Es ist nicht erforderlich, den Stream explizit zu schließen, da StreamReader.Dispose ihn automatisch schließt.
Nothrow
2
Ich glaube, die Dateifreigabe sollte sowohl für Lese- als auch für Schreib-Streams und nicht nur für Lese-Streams festgelegt werden.
LEO
StreamReader.Dispose auch verfügt über die Filestream , wenn ich mich nicht irre. Hier ist es zweimal entsorgt.
Eregrith
12
new StreamReader(File.Open(logFilePath, 
                           FileMode.Open, 
                           FileAccess.Read, 
                           FileShare.ReadWrite))

-> Dadurch wird die Datei nicht gesperrt.

nothrow
quelle
1
Scheint beim Lesen von Daten zu funktionieren. Beim Schreiben der gesperrten Datei tritt ein Fehler auf.
Webzy
8

Das Problem ist, dass Sie beim Schreiben in das Protokoll die Datei ausschließlich sperren, damit Ihr StreamReader sie überhaupt nicht öffnen kann.

Sie müssen versuchen, die Datei im schreibgeschützten Modus zu öffnen .

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
        }
    }
}
James
quelle
Wie ich jetzt weiß, File.ReadAllText()schlägt der schreibgeschützte Modus fehl.
Bohdan_Trotsenko
@modosansreves Ja, ich habe diesen Teil der Antwort entfernt, da in den Dokumenten angegeben ist, dass eine Antwort ausgegeben wird, UnauthorizedAccessExceptionwenn die Datei schreibgeschützt ist. Dies muss zum Zeitpunkt der Beantwortung übersehen worden sein.
James
5

Ich erinnere mich, dass ich vor ein paar Jahren dasselbe getan habe. Nach einigen Google-Anfragen fand ich Folgendes:

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

Verwenden Sie also das FileShare.ReadWrite-Attribut in FileStream ().

(gefunden auf Balaji Rameshs Blog )

Dotmartin
quelle
1

Haben Sie versucht, die Datei zu kopieren und dann zu lesen?

Aktualisieren Sie einfach die Kopie, wenn große Änderungen vorgenommen werden.

Tom Gullen
quelle
2
Ich habe es versucht und es ist nicht die beste Lösung. Das Kopieren der Datei dauert zu lange, da eine 4-MB-Datei etwa 20 Sekunden dauert.
Seichi
-1

Mit dieser Methode können Sie eine Textdatei am schnellsten lesen, ohne sie zu sperren.

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

Hoffe, diese Methode wird Ihnen helfen.

Amit Kumawat
quelle
1
Soweit ich weiß, liest diese Methode eine ganze Datei in einen String und verschluckt dabei Ausnahmen. Ich kann nicht sehen, wie das beim Sperren von Dateien helfen soll.
Standardgebietsschema