Come posso leggere un file di testo senza bloccarlo?


94

Ho un servizio Windows che scrive il suo registro in un file di testo in un formato semplice.

Ora creerò una piccola applicazione per leggere il registro del servizio e mostrare sia il registro esistente che quello aggiunto come visualizzazione live.

Il problema è che il servizio blocca il file di testo per l'aggiunta delle nuove righe e allo stesso tempo l'applicazione visualizzatore blocca il file per la lettura.

Il codice del servizio:

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

Il codice del visualizzatore:

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

C'è qualche problema nel mio codice in lettura e scrittura?

Come risolvere il problema?


Risposte:


120

È necessario assicurarsi che sia il servizio che il lettore aprano il file di registro in modo non esclusivo. Prova questo:

Per il servizio, il writer nel tuo esempio, utilizza FileStreamun'istanza creata come segue:

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

Per il lettore usa lo stesso ma cambia l'accesso ai file:

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

Inoltre, poiché FileStreamimplements IDisposableassicurati che in entrambi i casi consideri l'utilizzo di usingun'istruzione, ad esempio per l'autore:

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

In bocca al lupo!


C'è anche la versione ReadLines () su stackoverflow.com/questions/5338450/…
Colin

Perfetto: pensavo che File.Open () con FileAccess.Read fosse sufficiente, ma non lo è. Questo è, però. :)
neminem

Sul writer, FileShare.Read non sarebbe sufficiente visto che c'è un solo scrittore?
crokusek

2
Inoltre, vale la pena notare che FileStreamimplementaIDisposable
weeksdev

28

Impostazione esplicita della modalità di condivisione durante la lettura del file di testo.

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
non è necessario chiudere esplicitamente il flusso, poiché StreamReader.Dispose lo chiude automaticamente.
nothrow

2
Credo che la condivisione di file dovrebbe essere impostata su ENTRAMBI i flussi di lettura e scrittura e non solo di lettura.
LEO

StreamReader.Dispose dispone anche di FileStream se non mi sbaglio. Qui è smaltito due volte.
Eregrith

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

-> questo non blocca il file.


1
Sembra funzionare durante la lettura dei dati. Durante la scrittura viene visualizzato l'errore del file bloccato.
webzy

8

Il problema è che quando scrivi nel log stai bloccando esclusivamente il file in modo che il tuo StreamReader non possa aprirlo affatto.

Devi provare ad aprire il file in modalità di sola lettura .

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

Come ora so, File.ReadAllText()fallisce con la modalità di sola lettura.
bohdan_trotsenko

@modosansreves sì, ho rimosso quella parte della risposta poiché i documenti affermano che lancerà un messaggio UnauthorizedAccessExceptionse il file è di sola lettura - deve averlo perso al momento della risposta!
James

5

Ricordo di aver fatto la stessa cosa un paio di anni fa. Dopo alcune domande su Google ho trovato questo:

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

cioè usa l'attributo FileShare.ReadWrite su FileStream ().

(trovato sul blog di Balaji Ramesh )


1

Hai provato a copiare il file e poi a leggerlo?

Aggiorna la copia ogni volta che vengono apportate modifiche importanti.


2
L'ho provato e non è la soluzione migliore. La copia del file richiede troppo tempo, perché un file da 4 MB richiede circa 20 secondi.
Seichi

-1

Questo metodo ti aiuterà a leggere più velocemente un file di testo e senza bloccarlo.

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

Spero che questo metodo ti possa aiutare.


1
Per quanto ho capito, questo metodo legge un intero file in una stringa e ingoia le eccezioni lungo il percorso. Non riesco a vedere come dovrebbe aiutare con il blocco dei file.
locale predefinito
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.