Читать текстовый файл построчно, используя таймер

StreamReader sr = new StreamReader("C:/CR EZ Test/Log.txt");    //use with IF
private void timer2_Tick(object sender, EventArgs e)
{
    if ((line = sr.ReadLine()) != null)
    {   
        //FileStream fs = File.Open("C:/CR EZ Test/Log.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
        //StreamReader sr = new StreamReader(fs); //use with While can't use with }else{
        //while ((line = sr.ReadLine()) != null) 
        //{
        string[] dataLog = line.Split(new[] { ',' }, StringSplitOptions.None);
        mpa = (dataLog[1]);
        ml  = (dataLog[2]);
        lph = (dataLog[3]);
        elapsedTime = float.Parse(dataLog[4]) / 1000;

        if (testStatus > 0) time = elapsedTime.ToString("0.0");
        tb2.Value = int.Parse(dataLog[6]);

        if (chart1.Series[0].Points.Count > tb1.Value && tb1.Value > 0)
        {
            chart1.Series[0].Points.RemoveAt(0);
            chart1.Series[1].Points.RemoveAt(0);
        }
        chart1.Series[0].Points.AddXY(dataLog[5], int.Parse(dataLog[1]));
        chart1.Series[1].Points.AddXY(dataLog[5], int.Parse(dataLog[6]));
        //}
    }
    else
    {
        sr.DiscardBufferedData();
        sr.BaseStream.Seek(0, SeekOrigin.Begin);
        sr.BaseStream.Position = 0;
        //sr.Close();
        //alertTB.Text = "";
        timer2.Enabled = false;
    }
    alertTB.ForeColor = Color.Red;
    alertTB.Text = "Data Log Viewing In Progress";
}

Проблема в том, что я читаю текстовый файл, полный переменных, обратно через графический интерфейс, как при воспроизведении видео. Как показывает код, он работает, и я могу контролировать тик таймера, чтобы изменить скорость воспроизведения. Проблема в том, что файл используется, поэтому я не могу писать или удалять текст, пока файл используется, не закрывая его в первую очередь. Я хотел бы либо найти обходной путь для Streamreader, либо использовать код Filestream to Streamreader, который позволит мне редактировать файл во время его использования. Проблема в том, что я не могу понять, как заставить его работать с таймером, он просто очень быстро читает весь файл. Любая помощь или идеи с благодарностью.

Проблема здесь в том, как получить закомментированный код для:

  1. прочитать строку текстового файла,
  2. есть таймер, чтобы отметить
  3. затем прочитайте следующую строку текстового файла и так далее. Очевидно, обработка данных по мере их поступления.

2 ответа

Решение

Проверенное рабочее решение

 string line;
        if (!File.Exists(logFile))
        {
            viewLog.Text = "Play";
            alertTB.ForeColor = Color.Red;
            alertTB.Text = "File Does Not Exist | Log Data To Create File";
            chart.Text = "Scope On";
        }

        if (File.Exists(logFile))
        {
            var lineCount = File.ReadLines(logFile).Count();//read text file line count to establish length for array
            if (lineCount < 2)
            {
                viewLog.Text = "Play";
                alertTB.ForeColor = Color.Red;
                alertTB.Text = "File Exists | No Data Has Been Recorded";
                chart.Text = "Scope On";
            }

            if (counter < lineCount && lineCount > 0)//if counter is less than lineCount keep reading lines
            {
                line = File.ReadAllLines(logFile).Skip(counter).Take(lineCount).First();

                string[] dataLog = line.Split(new[] { ',' }, StringSplitOptions.None);
                //-----------------------------------------Handling my data 
                counter++;
            }
            else
            {
                counter = 0;
                timer2.Enabled = false;
            }
        }

Это исправление, к которому я пришел, оно позволяет редактировать файл или удалять его содержимое. Я получаю количество строк, прежде чем пытаться загрузить файл. Затем я использую счетчик для перебора строк. Я могу изменить задержку между чтением следующей строки, основываясь на интервале между таймерами, приостановить или остановить его.

Открытие файла во время его использования

Я думаю, что вы ищете FileStream с FileShare.ReadWrite для примера вашего StreamReader (не тот случай, который вы закомментировали),

var fs = new FileStream("C:\foo.txt", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
var sr = new StreamReader(fs);

Установка положения потока

Судя по вашим комментариям, у вас возникли проблемы с позиционированием потока, вот как вы можете это сделать...

fs.Position = 0; // note this is the FileStream not the StreamReader!
// alternatively, you could use Seek

Разница между последовательным и произвольным доступом

Наконец, вы можете посмотреть ниже, чтобы увидеть разницу между последовательным и произвольным доступом

концепция


Потенциальное решение

Вот класс называется FileMonitor который проверит файл и обновит список при каждом изменении / обновлении файла.

Я понимаю, что вы хотите, чтобы таймер опрашивал данные в текстовом файле, но в случае, если таймер очень быстрый, я оптимизировал FileMonitor смотреть файл на предмет изменений и извлекать его только при наличии изменений.

Обратите внимание, что это только продолжает читать, где это было остановлено, в зависимости от положения потока. Таким образом, это не будет работать, если строки будут удалены или изменены до "извлечения". Это означает, что он работает только в соответствии с вашими требованиями и не улучшен для обработки многих других сценариев, но он должен адекватно соответствовать вашим требованиям.

public class FileMonitor : IDisposable
{
    private readonly FileStream _file;
    private readonly StreamReader _reader;

    private long _position;
    private List<string> _lines;

    public FileMonitor(string file)
    {
        if (String.IsNullOrEmpty(nameof(file))) throw new ArgumentNullException(nameof(file));

        _lines = new List<string>();

        FileSystemWatcher watcher = new FileSystemWatcher();
        watcher.Path = Path.GetDirectoryName(file);
        watcher.Filter = Path.GetFileName(file);
        watcher.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName;
        watcher.Changed += new FileSystemEventHandler(OnChanged);
        //watcher.Created += new FileSystemEventHandler(OnCreated);
        //watcher.Deleted += new FileSystemEventHandler(OnDeleted);
        //watcher.Renamed += new RenamedEventHandler(OnRenamed);

        // begin watching.
        watcher.EnableRaisingEvents = true;

        // begin reading
        _file = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
        _reader = new StreamReader(_file);
        _lines = ReadLines(_reader).ToList();
        _position = _file.Position;
    }

    private void OnChanged(object source, FileSystemEventArgs e)
    {
        List<string> update = ReadLines(_reader).ToList();
         // fix to remove the immidate newline
        if (update.Count() > 0 && String.IsNullOrEmpty(update[0])) update.RemoveAt(0);
        _lines.AddRange(update);
        _position = _file.Position;

        // just for debugging, you should remove this
        Console.WriteLine($"File: {e.FullPath} [{e.ChangeType}]");
    }

    public IEnumerable<string> Lines { get { return _lines; } }

    public void Reset()
    {
        _file.Position = 0;
        _position = _file.Position;
        _lines.Clear(); 
    }

    private static IEnumerable<string> ReadLines(StreamReader reader)
    {
        string line;
        while ((line = reader.ReadLine()) != null)
        {
            yield return line;
        }
    }

    public void Dispose()
    {
        _reader.Dispose();
        _file.Dispose();
    }
}

Вот как вы можете использовать его с таймером

private IEnumerable<string> _lines; // holds all the lines "extracted"

void Main()
{
    string file = @"C:\Data\foo.txt";

    using (var timer = new System.Timers.Timer())
    {
        timer.Interval = 2000; // 2 second interval
        timer.Elapsed += OnTimedEvent; // attach delegate
        timer.Enabled = true; // start the timer    

        // open the file
        using (var monitor = new FileMonitor(file))
        {
            _lines = monitor.Lines;

             // loop forever, remove this
            while (true) { }
        }
    }
}

public void OnTimedEvent(object sender, EventArgs e)
{
    // just for debugging, you should remove this
    Console.WriteLine($"current count: {_lines.Count()}");
}

Если неясно, извлеченные данные хранятся в списке строк. Выше вы можете получить "извлеченные" данные с монитора, используя monitor.Line имущество.

Другие вопросы по тегам