Обработка огромных файлов в C#
У меня есть файл 4 Гб, для которого я хочу выполнить поиск и замену на основе байтов. Я написал простую программу для этого, но это занимает слишком много времени (90 минут +), чтобы сделать только один поиск и замену. Несколько шестнадцатеричных редакторов, которые я пробовал, могут выполнить задачу менее чем за 3 минуты и не загружать весь целевой файл в память. Кто-нибудь знает метод, где я могу сделать то же самое? Вот мой текущий код:
public int ReplaceBytes(string File, byte[] Find, byte[] Replace)
{
var Stream = new FileStream(File, FileMode.Open, FileAccess.ReadWrite);
int FindPoint = 0;
int Results = 0;
for (long i = 0; i < Stream.Length; i++)
{
if (Find[FindPoint] == Stream.ReadByte())
{
FindPoint++;
if (FindPoint > Find.Length - 1)
{
Results++;
FindPoint = 0;
Stream.Seek(-Find.Length, SeekOrigin.Current);
Stream.Write(Replace, 0, Replace.Length);
}
}
else
{
FindPoint = 0;
}
}
Stream.Close();
return Results;
}
Найти и заменить сравнительно мало по сравнению с 4Gb "Файл", кстати. Я легко могу понять, почему мой алгоритм работает медленно, но я не уверен, как бы я мог сделать это лучше.
5 ответов
Частично проблема может заключаться в том, что вы читаете поток по одному байту за раз. Попробуйте прочитать большие куски и заменить их. Я бы начал примерно с 8 КБ, а затем протестировал с некоторыми большими или меньшими порциями, чтобы увидеть, что дает вам лучшую производительность.
Есть много лучших алгоритмов для поиска подстроки в строке (это то, что вы делаете в основном)
Начни здесь:
http://en.wikipedia.org/wiki/String_searching_algorithm
Суть их в том, что вы можете пропустить много байтов, анализируя вашу подстроку. Вот простой пример
4ГБ Файл начинается с: A B C D E F G H I J K L M N O P
Ваша подстрока: N O P
- Вы пропускаете длину подстроки-1 и сравниваете последний байт, поэтому сравните C с P
- Это не соответствует, поэтому подстрока не первые 3 байта
- Кроме того, C вообще отсутствует в подстроке, поэтому вы можете пропустить еще 3 байта (длина подстроки)
- Сравнить F с P, не совпадает, F не находится в подстроке, пропустить 3
- Сравните I с P и т. Д.
Если вы подходите, идите назад. Если символ не совпадает, но находится в подстроке, то вам нужно сделать еще несколько сравнений на этом этапе (подробнее см. Ссылку)
Вместо того, чтобы читать файл побайтно, читайте его по буферу:
buffer = new byte[bufferSize];
currentPos = 0;
length = (int)Stream .Length;
while ((count = Stream.Read(buffer, currentPos, bufferSize)) > 0)
{
currentPos += count;
....
}
Вы должны попробовать использовать отображенные в памяти файлы. C# поддерживает их, начиная с версии 4.0.
Файл с отображением в памяти содержит содержимое файла в виртуальной памяти.
Постоянные файлы - это отображенные в памяти файлы, связанные с исходным файлом на диске. Когда последний процесс завершил работу с файлом, данные будут сохранены в исходном файле на диске. Эти отображенные в память файлы подходят для работы с очень большими исходными файлами.
Другой, более простой способ чтения более одного байта за раз:
var Stream = new BufferedStream(new FileStream(File, FileMode.Open, FileAccess.ReadWrite));
Комбинируя это с примером Саида Амири о том, как читать в буфер, и одним из лучших двоичных алгоритмов поиска / замены, вы получите лучшие результаты.