¿Cómo puedo leer un archivo de texto sin bloquearlo?

94

Tengo un servicio de Windows que escribe su registro en un archivo de texto en un formato simple.

Ahora, voy a crear una pequeña aplicación para leer el registro del servicio y muestra tanto el registro existente como el agregado como vista en vivo.

El problema es que el servicio bloquea el archivo de texto para agregar las nuevas líneas y, al mismo tiempo, la aplicación del visor bloquea el archivo para su lectura.

El código de servicio:

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

El código del espectador:

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

¿Hay algún problema en mi código para leer y escribir?

¿Como resolver el problema?

Homam
fuente

Respuestas:

120

Debe asegurarse de que tanto el servicio como el lector abran el archivo de registro de forma no exclusiva. Prueba esto:

Para el servicio, el escritor en su ejemplo, use una FileStreaminstancia creada de la siguiente manera:

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

Para el lector use lo mismo pero cambie el acceso al archivo:

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

Además, dado que FileStreamimplementa, IDisposableasegúrese de que en ambos casos considere usar una usingdeclaración, por ejemplo para el escritor:

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

¡Buena suerte!

Manfred
fuente
También está la versión ReadLines () en stackoverflow.com/questions/5338450/…
Colin
Perfecto: había pensado que File.Open () con FileAccess.Read era suficiente, pero no lo es. Sin embargo, esto es. :)
neminem
En cuanto al escritor, ¿no sería suficiente FileShare.Read ya que solo hay un escritor?
crokusek
2
Además, vale la pena señalar que FileStreamimplementaIDisposable
semanasdev
28

Configure explícitamente el modo para compartir mientras lee el archivo de texto.

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++;
        }
    }
}
jefes5150
fuente
2
No es necesario cerrar explícitamente el flujo, ya que StreamReader.Dispose lo cierra automáticamente.
nothrow
2
Creo que el recurso compartido de archivos debe establecerse en AMBOS flujos de lectura y escritura, y no solo en lectura.
LEO
StreamReader.Dispose también se deshace de FileStream si no me equivoco. Aquí se desecha dos veces.
Eregrith
12
new StreamReader(File.Open(logFilePath, 
                           FileMode.Open, 
                           FileAccess.Read, 
                           FileShare.ReadWrite))

-> esto no bloquea el archivo.

nothrow
fuente
1
Parece funcionar al leer datos. Al escribir aparece el error de archivo bloqueado.
webzy
8

El problema es que cuando está escribiendo en el registro, está bloqueando exclusivamente el archivo, por lo que su StreamReader no podrá abrirlo en absoluto.

Debe intentar abrir el archivo en modo de solo lectura .

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
fuente
Como ahora sé, File.ReadAllText()falla con el modo de solo lectura.
bohdan_trotsenko
@modosansreves sí, eliminé esa parte de la respuesta ya que los documentos indican que arrojará un UnauthorizedAccessExceptionsi el archivo es de solo lectura, ¡debe haberlo perdido en el momento de responder!
James
5

Recuerdo haber hecho lo mismo hace un par de años. Después de algunas consultas de Google, encontré esto:

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

es decir, utilice el atributo FileShare.ReadWrite en FileStream ().

(encontrado en el blog de Balaji Ramesh )

dotmartin
fuente
1

¿Ha intentado copiar el archivo y luego leerlo?

Simplemente actualice la copia siempre que se realicen cambios importantes.

Tom Gullen
fuente
2
Probé esto y no es la mejor solución. Copiar el archivo tarda demasiado, ya que un archivo de 4 MB tarda unos 20 segundos.
Seichi
-1

Este método le ayudará a leer más rápidamente un archivo de texto y sin bloquearlo.

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

Espero que este método te ayude.

Amit Kumawat
fuente
1
Por lo que tengo entendido, este método lee un archivo completo en una cadena y se traga las excepciones en el camino. No veo cómo se supone que eso ayude con el bloqueo de archivos.
configuración regional predeterminada