C # непрерывно читать файл - PullRequest
29 голосов
/ 25 сентября 2010

Я хочу читать файл непрерывно, как хвост GNU с параметром "-f". Мне нужно это для чтения в реальном времени файла журнала. Как правильно это сделать?

Ответы [ 7 ]

26 голосов
/ 25 сентября 2010

Вы хотите открыть FileStream в двоичном режиме.Периодически ищите до конца файла минус 1024 байта (или что-то еще), затем читайте до конца и выводите.Вот как работает tail -f.

Ответы на ваши вопросы:

Двоичный файл, потому что трудно получить произвольный доступ к файлу, если вы читаете его как текст.Вы должны сделать двоичное преобразование в текст самостоятельно, но это не сложно.(См. Ниже)

1024 байта, потому что это удобное удобное число, которое должно обрабатывать 10 или 15 строк текста.Обычно.

Вот пример открытия файла, чтения последних 1024 байт и преобразования его в текст:

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

Обратите внимание, что вы должны открыть с помощью FileShare.ReadWrite, так как вы 'мы пытаемся прочитать файл, который в данный момент открыт для записи другим процессом.

Также обратите внимание, что я использовал Encoding.Default, что в США / английском и для большинства западноевропейских языков будет 8-битной кодировкой символов.Если файл записан в какой-либо другой кодировке (например, UTF-8 или другой кодировке Unicode), возможно, что байты не будут правильно преобразованы в символы.Вам придется справиться с этим, определив кодировку, если вы думаете, что это будет проблемой.Переполнение стека поиска для получения информации об определении кодировки текста файла.

Если вы хотите делать это периодически (например, каждые 15 секунд), вы можете настроить таймер, который вызывает метод ReadTail так часто, как:ты хочешь.Вы можете немного оптимизировать ситуацию, открыв файл только один раз в начале программы.Это зависит от вас.

25 голосов
/ 28 июля 2014

Более естественный подход к использованию 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();

Здесь основной цикл чтения останавливается для ожидания входящих данных, а FileSystemWatcher используется только для пробуждения основного цикла чтения.

3 голосов
/ 28 февраля 2017

Чтобы непрерывно отслеживать хвост файла, вам просто нужно запомнить длину файла до этого.

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

Мне пришлось использовать ASCIIEncoding, потому что этот код недостаточно умен, чтобы обслуживать переменные длины символов UTF8 на границах буфера.

Примечание: Вы можете изменить часть Thread.Sleep на разные тайминги, а также связать ее с файловым наблюдателем и шаблоном блокировки - Monitor.Enter / Wait / Pulse. Для меня достаточно таймера, и самое большее, он проверяет только длину файла каждую секунду, если файл не изменился.

2 голосов
/ 09 ноября 2017

Это моё решение

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

так, в своем коде вы можете сделать

    foreach (string line in TailFrom(file)) 
    {
        Console.WriteLine($"line read= {line}");            
    }
2 голосов
/ 25 сентября 2010

Вы можете использовать класс FileSystemWatcher , который может отправлять уведомления о различных событиях, происходящих в файловой системе, таких как изменение файла.

0 голосов
/ 21 апреля 2011
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 голосов
/ 25 сентября 2010

Если вы просто ищете инструмент для этого, попробуйте бесплатную версию Bare tail

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...