Como posso ler um arquivo de texto sem bloqueá-lo?


94

Eu tenho um serviço do Windows grava seu log em um arquivo de texto em um formato simples.

Agora, vou criar um pequeno aplicativo para ler o log do serviço e mostrar o log existente e o adicionado como visualização ao vivo.

O problema é que o serviço bloqueia o arquivo de texto para adicionar as novas linhas e, ao mesmo tempo, o aplicativo visualizador bloqueia o arquivo para leitura.

O Código de Serviço:

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

O código do visualizador:

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

Existe algum problema no meu código de leitura e escrita?

Como resolver o problema?


Respostas:


120

Você precisa se certificar de que tanto o serviço quanto o leitor abrem o arquivo de log não exclusivamente. Experimente isto:

Para o serviço - o escritor em seu exemplo - use uma FileStreaminstância criada da seguinte maneira:

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

Para o leitor, use o mesmo, mas altere o acesso ao arquivo:

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

Além disso, como FileStreamimplementos, IDisposablecertifique-se de que, em ambos os casos, você considere usar uma usinginstrução, por exemplo, para o escritor:

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

Boa sorte!


Também há a versão ReadLines () em stackoverflow.com/questions/5338450/…
Colin

Perfeito - pensei que File.Open () com FileAccess.Read fosse suficiente, mas não é. Este é, no entanto. :)
neminem

No gravador, FileShare.Read não seria suficiente, já que existe apenas um gravador?
crokusek

2
Além disso, é importante notar que FileStream implementaIDisposable
semanas de

28

Configure explicitamente o modo de compartilhamento enquanto lê o arquivo 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++;
        }
    }
}

2
não é necessário fechar explicitamente o fluxo, pois StreamReader.Dispose o fecha automaticamente.
nothrow de

2
Acredito que o compartilhamento de arquivos deve ser definido em AMBOS os fluxos de leitura e gravação, e não apenas de leitura.
LEO de

StreamReader.Dispose também descarta o FileStream, se não me engano. Aqui está descartado duas vezes.
Eregrith

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

-> isso não bloqueia o arquivo.


1
Parece funcionar ao ler dados. Ao gravar o arquivo bloqueado, surge um erro.
webzy de

8

O problema é que, quando você está gravando no log, está bloqueando exclusivamente o arquivo para que seu StreamReader não tenha permissão para abri-lo.

Você precisa tentar abrir o arquivo no modo somente leitura .

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

Como agora eu sei, File.ReadAllText()falha com o modo somente leitura.
bohdan_trotsenko,

@modosansreves sim, eu removi essa parte da resposta, pois os documentos afirmam que ele irá lançar um UnauthorizedAccessExceptionse o arquivo for somente leitura - deve ter esquecido isso no momento da resposta!
James,

5

Lembro-me de fazer a mesma coisa alguns anos atrás. Depois de algumas consultas no Google, encontrei o seguinte:

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

ou seja, use o atributo FileShare.ReadWrite em FileStream ().

(encontrado no blog de Balaji Ramesh )


1

Você já tentou copiar o arquivo e depois lê-lo?

Basta atualizar a cópia sempre que grandes mudanças forem feitas.


2
Já tentei isso e não é a melhor solução. Copiar o arquivo leva muito tempo, pois um arquivo de 4 MB leva cerca de 20 segundos.
Seichi

-1

Este método o ajudará a ler um arquivo de texto mais rapidamente e sem bloqueá-lo.

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 ajude você.


1
Até onde eu entendo, esse método lê um arquivo inteiro em uma string e engole exceções ao longo do caminho. Não consigo ver como isso pode ajudar no bloqueio de arquivos.
localidade padrão
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.