Чтение изменений в файле в режиме реального времени с помощью .NET
-
01-07-2019 - |
Вопрос
У меня есть CSV-файл, который часто обновляется (примерно 20-30 раз в минуту).Я хочу вставить вновь добавленные строки в базу данных, как только они будут записаны в файл.
Тот Самый Просмотр файловой системы класс прослушивает уведомления об изменениях файловой системы и может вызывать событие всякий раз, когда происходит изменение в указанном файле.Проблема в том, что FileSystemWatcher не может точно определить, какие строки были добавлены или удалены (насколько я знаю).
Один из способов прочитать эти строки - сохранить и сравнить количество строк между изменениями и прочитать разницу между последним и предпоследним изменением.Тем не менее, я ищу более чистое (возможно, более элегантное) решение.
Решение
Я написал нечто очень похожее.Я использовал FileSystemWatcher для получения уведомлений об изменениях.Затем я использовал FileStream для чтения данных (отслеживая свою последнюю позицию в файле и стремясь к ней перед чтением новых данных).Затем я добавляю прочитанные данные в буфер, который автоматически извлекает полные строки и затем выводит их в пользовательский интерфейс.
Примечание:"this.MoreData(..) - это событие, прослушиватель которого добавляет данные в вышеупомянутый буфер и обрабатывает полное извлечение строки.
Примечание:Как уже упоминалось, это будет работать только в том случае, если изменения всегда являются дополнениями к файлу.Любые удаления вызовут проблемы.
Надеюсь, это поможет.
public void File_Changed( object source, FileSystemEventArgs e )
{
lock ( this )
{
if ( !this.bPaused )
{
bool bMoreData = false;
// Read from current seek position to end of file
byte[] bytesRead = new byte[this.iMaxBytes];
FileStream fs = new FileStream( this.strFilename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite );
if ( 0 == this.iPreviousSeekPos )
{
if ( this.bReadFromStart )
{
if ( null != this.BeginReadStart )
{
this.BeginReadStart( null, null );
}
this.bReadingFromStart = true;
}
else
{
if ( fs.Length > this.iMaxBytes )
{
this.iPreviousSeekPos = fs.Length - this.iMaxBytes;
}
}
}
this.iPreviousSeekPos = (int)fs.Seek( this.iPreviousSeekPos, SeekOrigin.Begin );
int iNumBytes = fs.Read( bytesRead, 0, this.iMaxBytes );
this.iPreviousSeekPos += iNumBytes;
// If we haven't read all the data, then raise another event
if ( this.iPreviousSeekPos < fs.Length )
{
bMoreData = true;
}
fs.Close();
string strData = this.encoding.GetString( bytesRead );
this.MoreData( this, strData );
if ( bMoreData )
{
File_Changed( null, null );
}
else
{
if ( this.bReadingFromStart )
{
this.bReadingFromStart = false;
if ( null != this.EndReadStart )
{
this.EndReadStart( null, null );
}
}
}
}
}
Другие советы
Верно, FileSystemWatcher ничего не знает о содержимом вашего файла.Он сообщит вам, изменилось ли это и т.д.но изменилось не то, что нужно.
Вы только добавляете что-то в файл?Из сообщения было немного неясно, были ли добавлены строки или их также можно было удалить.Предполагая, что они добавлены, решение довольно простое, в противном случае вы будете проводить некоторые сравнения.
Я думаю, вам следует использовать Журнал изменений NTFS или аналогичный:
Журнал изменений используется NTFS для обеспечения постоянного журнала всех изменений, внесенных в файлы на томе.Для каждого тома NTFS использует журнал изменений , чтобы отслеживание информации о добавленных, удаленных и измененных файлах.Журнал изменений намного эффективнее, чем временные метки или уведомления о файлах для определения изменений в заданном пространстве имен.
Вы можете найти описание в TechNet.Вам нужно будет использовать PInvoke в .NET.
Я бы сохранил текущий текст в памяти, если он достаточно мал, а затем использовал алгоритм diff, чтобы проверить, изменился ли новый текст и предыдущий текст.Эта библиотека, http://www.mathertel.de/Diff/, скажет вам не только о том, что что-то изменилось, но и о том, что изменилось.Таким образом, затем вы можете вставить измененные данные в базу данных.
навскидку мне пришло в голову, что вы могли бы сохранить последний известный размер файла.Проверьте размер файла и, когда он изменится, откройте программу чтения.
Затем найдите в программе чтения файл вашего последнего размера и начните чтение оттуда.
Вы правы насчет FileSystemWatcher.Вы можете прослушивать созданные, измененные, удаленные файлы и т.д.события, но вы не проникаете глубже, чем файл, который их вызвал.
Есть ли у вас контроль над самим файлом?Вы могли бы немного изменить модель, чтобы использовать файл в качестве буфера.Вместо одного файла заведите два.Один - это промежуточный результат, другой - сумма всех обработанных выходных данных.Прочитайте все строки из вашего файла "buffer", обработайте их, затем вставьте в конец другого файла, который представляет собой сумму всех обработанных строк.Затем удалите строки, которые вы обработали.Таким образом, вся информация в вашем файле ожидает обработки.Загвоздка в том, что если система представляет собой что-либо иное, кроме записи (т.е.также удаляет строки), то это не сработает.