Как эффективно объединить гигантские файлы с C # - PullRequest
7 голосов
/ 24 августа 2010

У меня есть более 125 файлов TSV по ~ 100 МБ каждый, которые я хочу объединить. Операция объединения позволяет уничтожить 125 файлов, но не данные. Что важно, так это то, что в конце я получаю большой файл содержимого всех файлов один за другим (без определенного порядка).

Есть ли эффективный способ сделать это? Мне было интересно, если Windows предоставляет API, чтобы просто сделать большой "Союз" из всех этих файлов? В противном случае мне придется прочитать все файлы и написать большой.

Спасибо!

Ответы [ 4 ]

17 голосов
/ 24 августа 2010

Значит, «слияние» - это просто запись файлов один за другим? Это довольно просто - просто откройте один выходной поток, а затем несколько раз откройте входной поток, скопируйте данные, закройте. Например:

static void ConcatenateFiles(string outputFile, params string[] inputFiles)
{
    using (Stream output = File.OpenWrite(outputFile))
    {
        foreach (string inputFile in inputFiles)
        {
            using (Stream input = File.OpenRead(inputFile))
            {
                input.CopyTo(output);
            }
        }
    }
}

Используется метод Stream.CopyTo, новый в .NET 4. Если вы не используете .NET 4, вам пригодится другой вспомогательный метод:

private static void CopyStream(Stream input, Stream output)
{
    byte[] buffer = new byte[8192];
    int bytesRead;
    while ((bytesRead = input.Read(buffer, 0, buffer.Length)) > 0)
    {
        output.Write(buffer, 0, bytesRead);
    }
}

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

РЕДАКТИРОВАТЬ: Как отмечено в комментариях, есть способы, которыми вы можете поиграть с параметрами файла, чтобы потенциально сделал его немного более эффективным с точки зрения того, что файловая система делает с данными. Но, по сути, вы будете читать данные и записывать их, буфер за раз, в любом случае.

2 голосов
/ 24 августа 2010

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

В случае последнего, возможно, вам вообще не нужно делать это программно, просто сгенерируйте один пакетфайл с этим (/b для двоичного файла, удалить, если не нужно):

copy /b "file 1.tsv" + "file 2.tsv" "destination file.tsv"

Используя C #, я бы использовал следующий подход.Напишите простую функцию, которая копирует два потока:

void CopyStreamToStream(Stream dest, Stream src)
{
    int bytesRead;

    // experiment with the best buffer size, often 65536 is very performant
    byte[] buffer = new byte[GOOD_BUFFER_SIZE];

    // copy everything
    while((bytesRead = src.Read(buffer, 0, buffer.Length)) > 0)
    {
        dest.Write(buffer, 0, bytesRead);
    }
}

// then use as follows (do in a loop, don't forget to use using-blocks)
CopStreamtoStream(yourOutputStream, yourInputStream);
2 голосов
/ 24 августа 2010

Сделайте это из командной строки:

copy 1.txt+2.txt+3.txt combined.txt

или

copy *.txt combined.txt
0 голосов
/ 24 августа 2010

Почему вы хотите это сделать?

Одним из способов может быть возиться с фрагментацией низкого уровня, было бы здорово, если бы вы заставили ее работать.

Вот обертка для C #.

http://blogs.msdn.com/b/jeffrey_wall/archive/2004/09/13/229137.aspx

...