Обработка огромных файлов в 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. Вы пропускаете длину подстроки-1 и сравниваете последний байт, поэтому сравните C с P
  2. Это не соответствует, поэтому подстрока не первые 3 байта
  3. Кроме того, C вообще отсутствует в подстроке, поэтому вы можете пропустить еще 3 байта (длина подстроки)
  4. Сравнить F с P, не совпадает, F не находится в подстроке, пропустить 3
  5. Сравните 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));

Комбинируя это с примером Саида Амири о том, как читать в буфер, и одним из лучших двоичных алгоритмов поиска / замены, вы получите лучшие результаты.

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