C# ciągły odczyt pliku

Chcę czytać plik w sposób ciągły jak GNU tail z param "- f". Potrzebuję go do odczytu na żywo. Jaki jest właściwy sposób, aby to zrobić?

 38
Author: Dmytro Leonenko, 2010-09-24

7 answers

Chcesz otworzyć FileStream w trybie binarnym. Okresowo poszukaj do końca pliku minus 1024 bajty (lub cokolwiek innego), a następnie odczytaj do końca i wyjdź. Tak działa tail -f.

Odpowiedzi na twoje pytania:

Binarny, ponieważ trudno jest losowo uzyskać dostęp do pliku, jeśli czytasz go jako tekst. Sam musisz wykonać konwersję binarną na tekst, ale nie jest to trudne. (Patrz niżej)

1024 bajtów, ponieważ jest to ładna, wygodna liczba i powinna obsługiwać 10 lub 15 wiersze tekstu. Zazwyczaj.

Oto przykład otwarcia pliku, odczytania ostatnich 1024 bajtów i przekonwertowania go na tekst:

static void ReadTail(string filename)
{
    using (FileStream fs = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
    {
        // Seek 1024 bytes from the end of the file
        fs.Seek(-1024, SeekOrigin.End);
        // read 1024 bytes
        byte[] bytes = new byte[1024];
        fs.Read(bytes, 0, 1024);
        // Convert bytes to string
        string s = Encoding.Default.GetString(bytes);
        // or string s = Encoding.UTF8.GetString(bytes);
        // and output to console
        Console.WriteLine(s);
    }
}

Zauważ, że musisz otworzyć za pomocą FileShare.ReadWrite, ponieważ próbujesz odczytać plik, który jest obecnie otwarty do napisania przez inny proces.

Zauważ również, że użyłem Encoding.Default, który w USA / angielskim i dla większości języków zachodnioeuropejskich będzie 8-bitowym kodowaniem znaków. Jeśli plik jest zapisany w innym kodowaniu (jak UTF-8 lub inne kodowanie Unicode), Możliwe, że bajty nie będą poprawnie konwertowane na znaki. Będziesz musiał sobie z tym poradzić, określając kodowanie, jeśli uważasz, że będzie to problem. Wyszukaj przepełnienie stosu w celu uzyskania informacji na temat kodowania tekstu pliku.

Jeśli chcesz to robić okresowo (na przykład co 15 sekund), możesz ustawić timer, który wywołuje metodę ReadTail tak często, jak chcesz. Możesz nieco zoptymalizować wszystko, otwierając plik tylko raz na początku programu. To zależy od ty.

 34
Author: Jim Mischel,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-02-26 11:52:24

Bardziej naturalne podejście do używania FileSystemWatcher:

    var wh = new AutoResetEvent(false);
    var fsw = new FileSystemWatcher(".");
    fsw.Filter = "file-to-read";
    fsw.EnableRaisingEvents = true;
    fsw.Changed += (s,e) => wh.Set();

    var fs = new FileStream("file-to-read", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
    using (var sr = new StreamReader(fs))
    {
        var s = "";
        while (true)
        {
            s = sr.ReadLine();
            if (s != null)
                Console.WriteLine(s);
            else
                wh.WaitOne(1000);
        }
    }

    wh.Close();

Tutaj główny cykl czytania zatrzymuje się, aby czekać na przychodzące dane, a FileSystemWatcher służy tylko do obudzenia głównego cyklu czytania.

 35
Author: tsul,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-06-14 17:24:34

Aby stale monitorować ogon pliku, musisz tylko pamiętać o długości pliku przed.

public static void MonitorTailOfFile(string filePath)
{
    var initialFileSize = new FileInfo(filePath).Length;
    var lastReadLength = initialFileSize - 1024;
    if (lastReadLength < 0) lastReadLength = 0;

    while (true)
    {
        try
        {
            var fileSize = new FileInfo(filePath).Length;
            if (fileSize > lastReadLength)
            {
                using (var fs = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
                {
                    fs.Seek(lastReadLength, SeekOrigin.Begin);
                    var buffer = new byte[1024];

                    while (true)
                    {
                        var bytesRead = fs.Read(buffer, 0, buffer.Length);
                        lastReadLength += bytesRead;

                        if (bytesRead == 0)
                            break;

                        var text = ASCIIEncoding.ASCII.GetString(buffer, 0, bytesRead);

                        Console.Write(text);
                    }
                }
            }
        }
        catch { }

        Thread.Sleep(1000);
    }
}

Musiałem użyć ASCIIEncoding, ponieważ ten kod nie jest wystarczająco inteligentny, aby zaspokoić zmienne długości znaków UTF8 na granicach bufora.

Uwaga: Możesz zmienić wątek.Część snu, aby być różne timingi, a także można połączyć go z filewatcher i blokowanie pattern-Monitor.Enter / Wait / Pulse. Dla mnie czasomierz wystarczy, a co najwyżej sprawdza tylko plik długość co sekundę, jeśli plik się nie zmienił.

 7
Author: Todd,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-02-27 23:03:33

This is my solution

    static IEnumerable<string> TailFrom(string file)
    {
        using (var reader = File.OpenText(file))
        {
            while (true) 
            {
                string line = reader.ReadLine();
                if (reader.BaseStream.Length < reader.BaseStream.Position) 
                    reader.BaseStream.Seek(0, SeekOrigin.Begin);

                if (line != null) yield return line;
                else Thread.Sleep(500);
            }
        }
    }

Więc w Twoim kodzie możesz zrobić

    foreach (string line in TailFrom(file)) 
    {
        Console.WriteLine($"line read= {line}");            
    }
 3
Author: Johann Medina,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-09 06:39:53

Możesz użyć klasy FileSystemWatcher , która może wysyłać powiadomienia o różnych zdarzeniach w systemie plików, takich jak zmiana pliku.

 2
Author: Darin Dimitrov,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-24 21:24:18
private void button1_Click(object sender, EventArgs e)
{
    if (folderBrowserDialog.ShowDialog() == DialogResult.OK)
    {
        path = folderBrowserDialog.SelectedPath;
        fileSystemWatcher.Path = path;

        string[] str = Directory.GetFiles(path);
        string line;
        fs = new FileStream(str[0], FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
        tr = new StreamReader(fs); 

        while ((line = tr.ReadLine()) != null)
        {

            listBox.Items.Add(line);
        }


    }
}

private void fileSystemWatcher_Changed(object sender, FileSystemEventArgs e)
{
    string line;
    line = tr.ReadLine();
    listBox.Items.Add(line);  
}
 0
Author: coolcake,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-08-11 07:36:11

Jeśli szukasz narzędzia do tego, to sprawdź darmową wersję Bare tail

 -7
Author: Snak3byte,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-09-24 21:26:47