C# 如何用C语言高效地合并巨大的文件#

C# 如何用C语言高效地合并巨大的文件#,c#,file-io,filesystems,C#,File Io,Filesystems,我有超过125个TSV文件,每个文件大小约为100Mb,我想合并。允许合并操作销毁125个文件,但不销毁数据。重要的是,一个结束,我结束了一个大文件的内容,所有的文件一个接一个(没有具体的顺序) 有没有一种有效的方法?我想知道Windows是否提供了一个API来简单地将所有这些文件组成一个大的“联合体”?否则,我将不得不读取所有文件并编写一个大文件 谢谢 那么“合并”实际上就是一个接一个地写文件?这非常简单——只需打开一个输出流,然后重复打开一个输入流,复制数据,然后关闭。例如: static

我有超过125个TSV文件,每个文件大小约为100Mb,我想合并。允许合并操作销毁125个文件,但不销毁数据。重要的是,一个结束,我结束了一个大文件的内容,所有的文件一个接一个(没有具体的顺序)

有没有一种有效的方法?我想知道Windows是否提供了一个API来简单地将所有这些文件组成一个大的“联合体”?否则,我将不得不读取所有文件并编写一个大文件

谢谢

那么“合并”实际上就是一个接一个地写文件?这非常简单——只需打开一个输出流,然后重复打开一个输入流,复制数据,然后关闭。例如:

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);
            }
        }
    }
}
这是使用.NET4中新增的
Stream.CopyTo
方法。如果您不使用.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);
    }
}
据我所知,没有比这更有效的了。。。但重要的是,这根本不会占用系统上太多内存。它不像是重复地将整个文件读入内存,然后再将其全部写出来


编辑:正如评论中所指出的,有一些方法可以使文件系统处理数据的效率稍微提高一些。但从根本上讲,您将读取并写入数据,一次一个缓冲区,无论是哪种方式。

从命令行执行:

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

您的意思是说,使用merge时,您想用一些自定义逻辑来决定哪些行要放在哪里?或者你的意思是你主要想把文件连接成一个大文件

对于后者,您可能根本不需要以编程方式执行此操作,只需使用此文件生成一个批处理文件(
/b
用于二进制文件,如果不需要,请删除):

使用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);

使用一个包含100MB文本文件(总计约12GB)的文件夹,我发现通过使用
File.ReadAllBytes
并将其写入流,可以在接受的答案上节省一点时间

        [Test]
        public void RaceFileMerges()
        {
            var inputFilesPath = @"D:\InputFiles";
            var inputFiles = Directory.EnumerateFiles(inputFilesPath).ToArray();

            var sw = new Stopwatch();
            sw.Start();

            ConcatenateFilesUsingReadAllBytes(@"D:\ReadAllBytesResult", inputFiles);

            Console.WriteLine($"ReadAllBytes method in {sw.Elapsed}");

            sw.Reset();
            sw.Start();

            ConcatenateFiles(@"D:\CopyToResult", inputFiles);

            Console.WriteLine($"CopyTo method in {sw.Elapsed}");
        }

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

        private static void ConcatenateFilesUsingReadAllBytes(string outputFile, params string[] inputFiles)
        {
            using (var stream = File.OpenWrite(outputFile))
            {
                foreach (var inputFile in inputFiles)
                {
                    var currentBytes = File.ReadAllBytes(inputFile);
                    stream.Write(currentBytes, 0, currentBytes.Length);
                }
            }
        }
00:01:22.2753300中的ReadAllBytes方法

00:01:30.312215中的复制到方法


我重复了很多次,得到了类似的结果。

我想你对这个问题的答案是否定的?@Marcus:我想是的。。。虽然我不确定OP是否能轻松地编写上面的流版本。谢谢Jon的帮助!:)我不知道“CopyTo”。听到
CopyTo
真是太好了,现在我可以删除我的答案;-)Copystream方法看起来很像CopyTo的实现,是故意的吗?你知道他说的125个文件,对吧?这将是非常漫长和乏味的打字。如果你给了一个C#程序来生成复制字符串,那可能是部分答案。伙计,然后使用第二个选项,使用文件掩码。或者执行dir命令(即dir/b仅获取文件名),将文件名捕获到文件中,并在良好的文本编辑器中构造该命令。有很多方法可以避免键入125个文件名。问题是,你甚至都没有回答这个问题。你对问题领域做了很多你不可能知道的假设。可以询问更多关于该领域的细节,但不能简单地假设问题作者选择了一种不正确的方法来解决他的问题-因为你可能不相关的解决方案和你争论的语气,“老兄。”哈哈,我喜欢自封的mods。冷静点。你对事物读得太多了(巧合的是,这正是你指责我的;谈论你自己)。OP询问如何组合文件,我给出了一个有效的答案。它可能完全适合这个问题,也可能不适合。OP知道是不是这样,但你不知道。不过我不会参加一场激烈的比赛,所以这是我最后一次回复你。@Aaronaught:我提交的时候已经完成了一半,然后我写了第二部分。但是,还要注意第二段中的一点提示:“只需生成一个批处理文件”。通过生成,我的意思是:自动创建。但是后来我决定添加C代码:)PS:看看这里(可能重复):
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);
        [Test]
        public void RaceFileMerges()
        {
            var inputFilesPath = @"D:\InputFiles";
            var inputFiles = Directory.EnumerateFiles(inputFilesPath).ToArray();

            var sw = new Stopwatch();
            sw.Start();

            ConcatenateFilesUsingReadAllBytes(@"D:\ReadAllBytesResult", inputFiles);

            Console.WriteLine($"ReadAllBytes method in {sw.Elapsed}");

            sw.Reset();
            sw.Start();

            ConcatenateFiles(@"D:\CopyToResult", inputFiles);

            Console.WriteLine($"CopyTo method in {sw.Elapsed}");
        }

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

        private static void ConcatenateFilesUsingReadAllBytes(string outputFile, params string[] inputFiles)
        {
            using (var stream = File.OpenWrite(outputFile))
            {
                foreach (var inputFile in inputFiles)
                {
                    var currentBytes = File.ReadAllBytes(inputFile);
                    stream.Write(currentBytes, 0, currentBytes.Length);
                }
            }
        }