代码之家  ›  专栏  ›  技术社区  ›  Bruce Adams

如何有效分割大文件

  •  19
  • Bruce Adams  · 技术社区  · 15 年前

    我想知道如何在不使用太多系统资源的情况下拆分一个大文件。 我目前正在使用此代码:

    public static void SplitFile(string inputFile, int chunkSize, string path)
    {
        byte[] buffer = new byte[chunkSize];
    
        using (Stream input = File.OpenRead(inputFile))
        {
            int index = 0;
            while (input.Position < input.Length)
            {
                using (Stream output = File.Create(path + "\\" + index))
                {
                    int chunkBytesRead = 0;
                    while (chunkBytesRead < chunkSize)
                    {
                        int bytesRead = input.Read(buffer, 
                                                   chunkBytesRead, 
                                                   chunkSize - chunkBytesRead);
    
                        if (bytesRead == 0)
                        {
                            break;
                        }
                        chunkBytesRead += bytesRead;
                    }
                    output.Write(buffer, 0, chunkBytesRead);
                }
                index++;
            }
        }
    }
    

    该操作需要52.370秒将1.6GB文件拆分为14MB文件。我不关心操作需要多长时间,我更关心使用的系统资源,因为这个应用程序将部署到一个共享的托管环境中。目前,这个操作使我的系统HDD IO使用率达到100%,大大降低了系统的速度。CPU使用率很低;RAM有点上升,但看起来不错。

    有没有方法可以限制此操作使用太多资源?

    谢谢

    5 回复  |  直到 7 年前
        1
  •  27
  •   Marc Gravell    15 年前

    在内存中组装每个输出文件似乎很奇怪;我怀疑您应该运行一个内部缓冲区(可能是20K或其他类型)并调用 Write 更频繁。

    最后,如果你需要IO,你需要IO。如果你想礼貌地对待共享的托管环境,你可以添加故意的暂停——可能是内部循环中的短暂暂停,也可能是外部循环中的较长暂停(可能是1s)。这不会对您的总体时间安排造成太大影响,但可能有助于其他进程获得一些IO。

    内部循环的缓冲区示例:

    public static void SplitFile(string inputFile, int chunkSize, string path)
    {
        const int BUFFER_SIZE = 20 * 1024;
        byte[] buffer = new byte[BUFFER_SIZE];
    
        using (Stream input = File.OpenRead(inputFile))
        {
            int index = 0;
            while (input.Position < input.Length)
            {
                using (Stream output = File.Create(path + "\\" + index))
                {
                    int remaining = chunkSize, bytesRead;
                    while (remaining > 0 && (bytesRead = input.Read(buffer, 0,
                            Math.Min(remaining, BUFFER_SIZE))) > 0)
                    {
                        output.Write(buffer, 0, bytesRead);
                        remaining -= bytesRead;
                    }
                }
                index++;
                Thread.Sleep(500); // experimental; perhaps try it
            }
        }
    }
    
        2
  •  1
  •   Michael Bahig    12 年前

    我对问题中的代码进行了一些修改,以防您希望按块分割,同时确保每个块以行尾结尾:

        private static void SplitFile(string inputFile, int chunkSize, string path)
        {
            byte[] buffer = new byte[chunkSize];
            List<byte> extraBuffer = new List<byte>();
    
            using (Stream input = File.OpenRead(inputFile))
            {
                int index = 0;
                while (input.Position < input.Length)
                {
                    using (Stream output = File.Create(path + "\\" + index + ".csv"))
                    {
                        int chunkBytesRead = 0;
                        while (chunkBytesRead < chunkSize)
                        {
                            int bytesRead = input.Read(buffer,
                                                       chunkBytesRead,
                                                       chunkSize - chunkBytesRead);
    
                            if (bytesRead == 0)
                            {
                                break;
                            }
    
                            chunkBytesRead += bytesRead;
                        }
    
                        byte extraByte = buffer[chunkSize - 1];
                        while (extraByte != '\n')
                        {
                            int flag = input.ReadByte();
                            if (flag == -1)
                                break;
                            extraByte = (byte)flag;
                            extraBuffer.Add(extraByte);
                        }
    
                        output.Write(buffer, 0, chunkBytesRead);
                        if (extraBuffer.Count > 0)
                            output.Write(extraBuffer.ToArray(), 0, extraBuffer.Count);
    
                        extraBuffer.Clear();
                    }
                    index++;
                }
            }
        }
    
        3
  •  0
  •   TomTom    15 年前

    目前这项行动是最大的我的 系统HDD IO使用率为100%。

    这是合乎逻辑的-IO将成为您的限制因素,而且您的系统可能与大多数计算机的糟糕IO相同(一个慢磁盘,而不是高性能磁盘的RAID 10)。

    您可以使用一个合适的chunk sze(向上1MB)来减少小的读写,但最后,您可以这样做。或者获得更快的光盘子系统。

        4
  •  0
  •   Pieter van Ginkel    15 年前

    您的一个选项是限制操作。例如,如果将缓冲区恢复到较小的大小(介于4K和1MB之间),并放置一个线程。在操作之间睡眠,您将使用较少的资源。

        5
  •  0
  •   LoztInSpace    12 年前

    这对你的主人来说是个问题,而不是你。假设这绝对是你需要做的事情,那么你做的大部分事情都是你能做的最有效的方式。由他们根据负载、优先级、SLA等来管理资源,这和您的管理程序/vm/os/app-server/做什么都是一样的。

    把文件分开使用你已经支付的设施!