Как воспроизвести функциональность хвоста -f в C # [дубликат]

Если у вас проблемы с производительностью, вы можете вычислить собственные значения в Fortran (например, используя LAPACK, например, Владимир F, уже указанный в комментариях). Однако вместо того, чтобы запускать программу Fortran и пытаться передать матрицы Matlab, вы можете вызвать свою программу Fortran / ваши функции / подпрограммы / матрицы Fortran / указатели / указатели из Matlab. Вам придется переписать части вашей программы, но затем вы можете использовать API библиотеки матриц Fortran . (В основном использование mex в качестве метки высокой производительности, упомянутого в комментариях)

26
задан Dmytro Leonenko 24 September 2010 в 22:21
поделиться

7 ответов

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

23
ответ дан Chris F Carroll 21 August 2018 в 04:58
поделиться
  • 1
    Почему 1024? Почему двоичный? Пример? – Dmytro Leonenko 25 September 2010 в 11:09
  • 2
    @Dmytro: Смотрите мой обновленный пост. – Jim Mischel 25 September 2010 в 21:55
  • 3
    Если с момента последней проверки было записано более или менее 1024 байта, разве это не пропустит некоторые данные или не прочитает некоторые данные дважды? – baruch 29 October 2013 в 15:25
  • 4
    Стоит отметить, что если файл меньше 1024 байта, код будет генерировать исключение IOException, потому что делается попытка переместить указатель файла в точку до начала файла. Чтобы избежать проблемы: fs.Seek (Math.Max ​​(-1024, -fs.Length), SeekOrigin.End); – omglolbah 4 January 2014 в 10:53
  • 5
    Хвост не слепо получает последние 1024 байта - он отслеживает последний размер и новый размер и получает только байты, которые были недавно добавлены. git.savannah.gnu.org/cgit/coreutils.git/tree/src/tail.c – kah608 21 February 2014 в 00:00

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

2
ответ дан Darin Dimitrov 21 August 2018 в 04:58
поделиться
  • 1
    ОК. Предположим, я получил событие, которое изменил файл. Как я могу прочитать до конца с позиции, которую я закончил, прочитав в прошлый раз? Пример Эни оценил :) – Dmytro Leonenko 25 September 2010 в 11:08

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

    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}");            
    }
1
ответ дан Johann Medina 21 August 2018 в 04:58
поделиться

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

-4
ответ дан Snak3byte 21 August 2018 в 04:58
поделиться
  • 1
    Нет, я не ищу инструмент. Мне нужен способ реализовать его в C # для моей программы. – Dmytro Leonenko 25 September 2010 в 11:10

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

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

3
ответ дан Todd 21 August 2018 в 04:58
поделиться

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

20
ответ дан tsul 21 August 2018 в 04:58
поделиться
  • 1
    Вы должны сделать: fsw.EnableRaisingEvents = true; до срабатывания события fsw.Changed – Mads Y 28 April 2016 в 14:29
  • 2
    Да, ты прав, моя ошибка. Приведенный выше код работает только из-за таймаута WaitOne. – tsul 30 April 2016 в 23:43
  • 3
    @MadsY Исправлен ответ, как вы предложили. – tsul 14 June 2016 в 17:25
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
ответ дан Zyberzero 21 August 2018 в 04:58
поделиться
Другие вопросы по тегам:

Похожие вопросы: