大文件处理 - 读取算法中断 - C#

Sli*_*lim 2 .net c# large-files

所以我有一个从(非常大的,~155 + MB)二进制文件读取的算法,根据规范解析它并写出必要的信息(到CSV,平面文本).对于前1550万行输出,它可以完美地工作,产生约0.99-1.03 GB的CSV文件.这几乎超过了二进制文件的20%.在此之后它会中断,因为突然打印的数据根本不是二进制文件中显示的内容.我检查了二进制文件,相同的模式继续(数据分成"数据包" - 见下面的代码).由于它的处理方式,内存使用量从未真正增加(稳定~15K).功能代码如下所示.这是我的算法(如果是这样,为什么它会在1550万行之后中断?!)......是否还有其他影响因为大文件大小我没有考虑?有任何想法吗?

(fyi:每个"数据包"长度为77个字节,以3字节"开始代码"开头,以5字节"结束代码"结束 - 您将看到下面的模式)

编辑代码已根据以下建议更新...谢谢!

private void readBin(string theFile)
{
    List<int> il = new List<int>();
    bool readyForProcessing = false;

    byte[] packet = new byte[77];

    try
    {
        FileStream fs_bin = new FileStream(theFile, FileMode.Open);
        BinaryReader br = new BinaryReader(fs_bin);

        while (br.BaseStream.Position < br.BaseStream.Length && working)
        {
            // Find the first startcode
            while (!readyForProcessing)
            {
                // If last byte of endcode adjacent to first byte of startcod...
                // This never occurs outside of ending/starting so it's safe
                if (br.ReadByte() == 0x0a && br.PeekChar() == (char)0x16)
                    readyForProcessing = true;
            }

            // Read a full packet of 77 bytes
            br.Read(packet, 0, packet.Length);

            // Unnecessary I guess now, but ensures packet begins
            // with startcode and ends with endcode
            if (packet.Take(3).SequenceEqual(STARTCODE) &&
                packet.Skip(packet.Length - ENDCODE.Length).SequenceEqual(ENDCODE))
            {
                il.Add(BitConverter.ToUInt16(packet, 3)); //il.ElementAt(0) == 2byte id
                il.Add(BitConverter.ToUInt16(packet, 5)); //il.ElementAt(1) == 2byte semistable
                il.Add(packet[7]); //il.ElementAt(2) == 1byte constant

                for(int i = 8; i < 72; i += 2) //start at 8th byte, get 64 bytes
                    il.Add(BitConverter.ToUInt16(packet, i));

                for (int i = 3; i < 35; i++)
                {
                    sw.WriteLine(il.ElementAt(0) + "," + il.ElementAt(1) +
                        "," + il.ElementAt(2) + "," + il.ElementAt(i));
                }

                il.Clear();
            }
            else
            {
                // Handle "bad" packets
            }
        } // while

        fs_bin.Flush();
        br.Close();                
        fs_bin.Close();
    }
    catch (Exception e)
    {
        MessageBox.Show(e.ToString());
    }
}
Run Code Online (Sandbox Code Playgroud)

LBu*_*kin 17

您的代码正在静默捕获while循环中发生的任何异常并吞下它.

这是一种不好的做法,因为它掩盖了您遇到的问题.

最有可能的是,你在循环中调用的方法之一(int.Parse()例如)抛出一个异常,因为它遇到了数据格式的某些问题(或者你对该格式的假设).

一旦发生异常,读取数据的循环就会被抛弃,因为它不再位于记录边界.

您应该做几件事来使这段代码更具弹性:

  1. 不要在运行循环中静默地吞下异常.处理它们.
  2. 不要在循环中逐字节或逐字段读取数据.由于您的记录是固定大小(77字节) - 将整个记录读入字节[],然后从那里处理它.这将有助于确保您始终在记录边界阅读.