Открытие файла во время его использования
Я думаю, что вы ищете 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
.