C # непрерывно читать файл

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

Ответы на вопрос(7)

Более естественный подход к использованию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 используется только для пробуждения основного цикла чтения.

 Mads Y28 апр. 2016 г., 16:29
Ты должен сделать:fsw.EnableRaisingEvents = true; передfsw.Changed событие запущено
 tsul14 июн. 2016 г., 19:25
@MadsY Исправлен ответ, как вы предложили.
 tsul01 мая 2016 г., 01:43
Да, вы правы, моя ошибка. Код выше работает только из-заWaitOne тайм-аут.

вам просто нужно помнить длину файла до.

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. Для меня достаточно таймера, и самое большее, он проверяет только длину файла каждую секунду, если файл не изменился.

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

    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}");            
    }
Решение Вопроса

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 метод так часто, как вы хотите. Вы можете немного оптимизировать ситуацию, открыв файл только один раз в начале программы. Это зависит от вас.

 Baruch29 окт. 2013 г., 14:25
Если с момента последней проверки было записано более или менее 1024 байта, то не пропустит ли это некоторые данные или не прочитает их дважды?
 Dmytro Leonenko25 сент. 2010 г., 12:09
Почему 1024? Почему бинарная? Пример?
 Jim Mischel25 сент. 2010 г., 22:55
@Dmytro: см. Мой обновленный пост.
 Jim Mischel29 окт. 2013 г., 14:46
@baruch: Да. Но тогда вот какtail команда тоже работает.
 Jim Mischel20 февр. 2014 г., 23:51
@ kah608: Спасибо за информацию. Для этого должно быть достаточно легко изменить приведенный выше код.
 DaveInCaz26 апр. 2017 г., 13:36
Другой ответstackoverflow.com/a/24993767/3195477 кажется проще ... есть ли преимущество у этого подхода?
 kah60820 февр. 2014 г., 23:00
Хвост не получает вслепую последние 1024 байта - он отслеживает последний размер и новый размер и получает только те байты, которые были добавлены недавно.git.savannah.gnu.org/cgit/coreutils.git/tree/src/tail.c
 Jim Mischel14 мая 2015 г., 14:51
Даунвотер: принято оставлять пояснительный комментарий. , ,
 omglolbah04 янв. 2014 г., 09:53
Стоит отметить, что если размер файла менее 1024 байт, код сгенерирует исключение IOException, поскольку делается попытка переместить указатель файла в точку до начала файла. Чтобы избежать проблемы: fs.Seek (Math.Max ​​(-1024, -fs.Length), SeekOrigin.End);
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);  
}

попробуйте бесплатную версиюГолый хвост

 Dmytro Leonenko25 сент. 2010 г., 12:10
Нет, я не ищу инструмент. Мне нужен способ реализовать это в C # для моей программы.

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

 Dmytro Leonenko25 сент. 2010 г., 12:08
ХОРОШО. Допустим, я получил событие, что файл изменился. Как я могу читать до конца с позиции, которую я закончил читать в прошлый раз? Любой пример оценили :)

Ваш ответ на вопрос