2

だから私は(非常に大きな、〜155 + MB)バイナリファイルから読み取り、仕様に従ってそれを解析し、必要な情報を(CSV、フラットテキストに)書き出すアルゴリズムを持っています。出力の最初の 1,550 万行は問題なく動作し、約 0.99 ~ 1.03 GB の CSV ファイルが生成されます。これは、バイナリ ファイルの 20% 以上をほとんど通過しません。この後、突然印刷されたデータがバイナリファイルに表示されているものとまったく異なるため、壊れます。バイナリ ファイルを確認しましたが、同じパターンが続きます (データは「パケット」に分割されます - 以下のコードを参照してください)。処理方法により、メモリの使用量が実際に増加することはありません (安定して ~15K)。機能コードを以下に示します。それは私のアルゴリズムですか (もしそうなら、なぜ 1,550 万行で壊れるのですか?!)...ファイル サイズが大きいために考慮していない他の影響はありますか? 何か案は?

(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());
    }
}
4

2 に答える 2

17

あなたのコードは、while ループで発生した例外を黙ってキャッチし、それを飲み込んでいます。

これは、あなたが直面しているような問題を隠してしまうため、悪い習慣です。

ほとんどの場合、ループ内で呼び出すメソッドの 1 つ (int.Parse()たとえば) が例外をスローしています。これは、データの形式 (またはその形式に関する仮定) に何らかの問題が発生したためです。

例外が発生すると、データを読み取るループは、もはやレコード境界に配置されていないため、異常な状態でスローされます。

このコードの回復力を高めるには、いくつかのことを行う必要があります。

  1. 実行ループで黙って例外を飲み込まないでください。それらに対処します。
  2. ループ内でデータをバイト単位またはフィールド単位で読み取らないでください。レコードは固定サイズ (77 バイト) であるため、レコード全体を byte[] に読み取り、そこから処理します。これにより、常にレコード境界で読み取りを行うことができます。
于 2009-10-22T17:58:08.867 に答える
3
  • ここに空の一般的なブロックを置かないでくださいcatch。黙ってキャッチして続行してください。そこに実際の例外が発生しているかどうかを確認して、そこから移動する必要があります。
  • 関数の必要はありませんbyteToHexString0x16 進数の前にプレフィックスを使用するだけで、バイナリ比較が行われます。

すなわち

if(al[0] == 0x16 && al[1] == 0x3C && al[2] == 0x02)
{
    ...
}
  • あなたの関数が何をするのかはわかりませんdoConvertが (あなたはそのソースを提供しませんでした)、BinaryReaderクラスは多くの異なる関数を提供します。そのうちの 1 つがReadInt16. がエンコードされた形式で保存されていない限り、shortかなり難読化された混乱する変換を行うよりも使いやすいはずです。それらがエンコードされている場合でも、byte文字列への変換でいくつかのラウンドトリップを行うよりも、 s を読み取って操作する方がはるかに簡単です。

編集

LINQ 拡張メソッド (特に) を非常に自由に使用しているようElementAtです。その関数を呼び出すたびに、その数に達するまでリストを列挙します。リストで組み込みのインデクサーを使用するだけで、コードのパフォーマンスが大幅に向上します (冗長性も低くなります)。

つまりal[3]、ではなくal.ElementAt(3)

Flushまた、 inputを呼び出す必要はありませんStreamFlushストリームに、書き込みバッファにあるものを基になる OS ファイル ハンドルに書き込むように指示するために使用されます。入力ストリームの場合は何もしません。

sw.WriteLine現在の通話を次のように置き換えることをお勧めします。

sw.WriteLine(BitConverter.ToString(packet));そして、混乱し始めた行で期待しているデータが実際に得ているものかどうかを確認してください。

私は実際にこれを行います:

if (packet.Take(3).SequenceEqual(STARTCODE) &&
    packet.Skip(packet.Length - ENDCODE.Length).SequenceEqual(ENDCODE))
{
    ushort id = BitConverter.ToUInt16(packet, 3);
    ushort semistable = BitConverter.ToUInt16(packet, 5);
    byte contant = packet[7];

    for(int i = 8; i < 72; i += 2)
    {
        il.Add(BitConverter.ToUInt16(packet, i));
    }

    foreach(ushort element in il)
    {
        sw.WriteLine(string.Format("{0},{1},{2},{3}", id, semistable, constant, element);
    }

    il.Clear();
}
else
{
    //handle "bad" packets
}
于 2009-10-22T18:02:17.437 に答える