【问题标题】:how to continue a loop despite error in parsing byte data尽管解析字节数据时出错,如何继续循环
【发布时间】:2013-06-19 09:58:39
【问题描述】:

我的问题是这个问题的延续:(loop for reading different data types & sizes off very large byte array from file)

我有一个存储在文件(rawbytes.txt 或 bytes.data)中的原始字节流,我需要对其进行解析并输出到 CSV 样式的文本文件。

原始字节的输入(当被读取为字符/长/整数等时)看起来像这样:

A2401028475764B241102847576511001200C...

解析后应该是这样的:

输出A.txt

(Field1,Field2,Field3) - heading

A,240,1028475764

输出B.txt

(Field1,Field2,Field3,Field4,Field5) - heading

B,241,1028475765,1100,1200

输出C.txt

C,...//and so on

本质上,它是一个十六进制转储样式的字节输入,它是连续的,没有任何行终止符或需要解析的数据之间的间隙。如上所示,数据由一个接一个的不同数据类型组成。

这是我的代码的 sn-p - 因为在任何字段中都没有逗号,并且不需要使用“”(即 CSV 包装器),我只是使用 TextWriter 来创建 CSV 样式的文本文件如下:

if (File.Exists(fileName))
        {
        using (BinaryReader reader = new BinaryReader(File.Open(fileName, FileMode.Open)))
            {
        while (reader.BaseStream.Position != reader.BaseStream.Length)
            {
                inputCharIdentifier = reader.ReadChar();
                switch (inputCharIdentifier)
                     case 'A':

                        field1 = reader.ReadUInt64();
                        field2 = reader.ReadUInt64();
                        field3 = reader.ReadChars(10);
                        string strtmp = new string(field3);
                        //and so on
                        using (TextWriter writer = File.AppendText("outputA.txt"))
                        {
                            writer.WriteLine(field1 + "," + field2 + "," + strtmp); // +  
                        }
                        case 'B':
                        //code...

我的问题是基于这样一个事实,即某些原始字节数据包含难以解析的空值 - 因为存在 unknown 数量的空字节(或非空,连续数据块之间的异位字节)(如果数据块没有损坏,则每个数据块以 A、B 或 C 开头)。

问题

那么,我如何添加默认情况或其他机制来继续循环,尽管可能由于数据损坏或错误而出现错误? 以下代码是否可行?

    inputCharIdentifier = reader.ReadChar();
    ...
    case default:
    //I need to know what to add here, instead of default 
    //(i.e. the case when the character could not be read)
    while (binReader.PeekChar() != -1)
    {
         filling = binReader.readByte();
         //filling is a single byte
         try {
             fillingChar = Convert.ToChar(filling);

             break;
         }
         catch (Exception ex) { break; }
         if (fillingChar == 'A' || fillingChar == 'B')
             break;

剩下的部分 - 向每个 switch case(例如'A')添加代码以继续而不停止程序 - 有没有办法在没有多个 try-catch 块的情况下做到这一点? [IE。代码块字符标识符为 A,但 A 之后的字节已损坏 - 在这种情况下,我需要退出循环或读取(即跳过)定义的字节数 - 如果消息头正确识别,则此处将知道剩余的字节数。

[注意:案例 A、B 等具有不同大小的输入 - 换句话说,A 总共可能是 40 个字节,而 B 是 50 个字节。因此,使用固定大小的缓冲区,例如 inputBuf[1000] 或 [50] - 如果它们的大小都相同 - AFAIK 也无法正常工作。]

有什么建议吗?请帮忙!我对 C# 比较陌生(2 个月)...

更新:我的整个代码如下:

         class Program
{
    const string fileName = "rawbytes.txt";
    static void Main(string[] args)
    {
                    try
        {
            var program = new Program();
            program.Parser();
        }
        catch (Exception e)
        {
            Console.WriteLine(e);
        }
        Console.ReadLine();
    }
    public void Parser()
    {
        char inputCharIdentifier = 'Z';
        //only because without initializing inputCharIdentifier I ended up with an error
        //note that in the real code, 'Z' is not a switch-case alphabet
        //it's an "inconsequential character", i.e. i have defined it to be 'Z'
        //just to avoid that error, and to avoid leaving it as a null value
        ulong field1common = 0;
        ulong field2common = 0;
        char[] charArray = new char[10];
        char char1;
        char char2;
        char char3;
        int valint1 = 0;
        int valint2 = 0;
        int valint3 = 0;
        int valint4 = 0;
        int valint5 = 0;
        int valint6 = 0;
        int valint7 = 0;
        double valdouble;
        /*
        char[] filler = new char[53];
        byte[] filling = new byte[4621];
        byte[] unifiller = new byte[8];
        //these values above were temporary measures to manually filter through
        //null bytes - unacceptable for the final program
        */
        if (File.Exists(fileName))
        {
            using (BinaryReader reader = new BinaryReader(File.Open(fileName, FileMode.Open)))
            {
                while (reader.BaseStream.Position != reader.BaseStream.Length)
                {
                    //inputCharIdentifier = reader.ReadChar();
                    //if (inputCharIdentifier != null)
                    //{
                        try
                        {
                            inputCharIdentifier = reader.ReadChar();
                            try
                            {
                                switch (inputCharIdentifier)
                                {
                                    case 'A':

                                        field1common = reader.ReadUInt64();
                                        field2common = reader.ReadUInt64();
                                        //unifiller = reader.ReadBytes(8);
                                        //charArray = reader.ReadString();
                                        //result.ToString("o");
                                        //Console.WriteLine(result.ToString());
                                        charArray = reader.ReadChars(10);
                                        string charArraystr = new string(charArray);
                                        char1 = reader.ReadChar();
                                        valint1 = reader.ReadInt32();
                                        valint2 = reader.ReadInt32();
                                        valint3 = reader.ReadInt32();
                                        valint4 = reader.ReadInt32();
                                        using (TextWriter writer = File.AppendText("A.txt"))
                                        {
                                            writer.WriteLine(field1common + "," + /*result.ToString("o")*/ field2common + "," + charArraystr + "," + char1 + "," + valint1 + "," + valint2 + "," + valint3 + "," + valint4);
                                            writer.Close();
                                        }
                                        break;


                                    case 'B':
                                    case 'C':

                                        field1common = reader.ReadUInt64();
                                        field2common = reader.ReadUInt64();
                                        //charArray = reader.ReadString();
                                        charArray = reader.ReadChars(10);
                                        string charArraystr2 = new string(charArray);
                                        char1 = reader.ReadChar();
                                        valint1 = reader.ReadInt32();
                                        valint2 = reader.ReadInt32();
                                        using (TextWriter writer = File.AppendText("C.txt"))
                                        {
                                            writer.WriteLine(field1common + "," + result2.ToString("o") + "," + charArraystr2 + "," + char1 + "," + valint1 + "," + valint2);
                                            writer.Close();
                                        }
                                        break;
                                    case 'S':
                                        //market status message
                                        field1common = reader.ReadUInt64();
                                        char2 = reader.ReadChar();
                                        char3 = reader.ReadChar();
                                        break;
                                    case 'L':
                                        filling = reader.ReadBytes(4);
                                        break;
                                    case 'D':
                                    case 'E':
                                        field1common = reader.ReadUInt64();
                                        field2common = reader.ReadUInt64();
                                        //charArray = reader.ReadString();
                                        charArray = reader.ReadChars(10);
                                        string charArraystr3 = new string(charArray);
                                        //char1 = reader.ReadChar();
                                        valint1 = reader.ReadInt32();
                                        valint2 = reader.ReadInt32();
                                        valint5 = reader.ReadInt32();
                                        valint7 = reader.ReadInt32();
                                        valint6 = reader.ReadInt32();
                                        valdouble = reader.ReadDouble();
                                        using (TextWriter writer = File.AppendText("D.txt"))
                                        {
                                            writer.WriteLine(field1common + "," + result3.ToString("o") + "," + charArraystr3 + "," + valint1 + "," + valint2 + "," + valint5 + "," + valint7 + "," + valint6 + "," + valdouble);
                                            writer.Close();
                                        }
                                        break;
                                    }
                            }
                            catch (Exception ex)
                            {
                                Console.WriteLine("Parsing didn't work");
                                Console.WriteLine(ex.ToString());
                                break;
                            }
                        }
                        catch (Exception ex)
                        {
                            Console.WriteLine("Here's why the character read attempt didn't work");
                            Console.WriteLine(ex.ToString());

                            continue;
                            //continue;
                        }
                    //}
                }
            }
            }
            }

我收到的错误如下:

    Here's why the character read attempt didn't work

    System.ArgumentException: The output char buffer is too small to contain the decoded characters, encoding 'Unicode (UTF-8)' fallback 'System.Text.DecoderReplacementFallback'.
    Parameter name: chars
    at System.Text.Encoding.ThrowCharsOverflow()
    at System.Text.Encoding.ThrowCharsOverflow(DecoderNLS decoder, Boolean nothingDecoded)
    at System.Text.UTF8Encoding.GetChars(Byte* bytes, Int32 byteCount, Char* chars, Int32 charCount, DecoderNLS baseDecoder)
    at System.Text.DecoderNLS.GetChars(Byte* bytes, Int32 byteCount, Char* chars, Int32 charCount, Boolean flush)
    at System.Text.DecoderNLS.GetChars(Byte[] bytes, Int32 byteIndex, Int32 byteCount, Char[] chars, Int32 charIndex, Boolean flush)
    at System.Text.DecoderNLS.GetChars(Byte[] bytes, Int32 byteIndex, Int32 byteCount, Char[] chars, Int32 charIndex)
    at System.IO.BinaryReader.InternalReadOneChar()
    at System.IO.BinaryReader.Read()
    at System.IO.BinaryReader.ReadChar()
    at line 69: i.e. inputCharIdentifier = reader.ReadChar();

更新:生成上述相同错误的示例文件位于以下链接: http://www.wikisend.com/download/106394/rawbytes.txt

请特别注意连续数据块之间的 8 个意外空字节,即使数据块标头 - 即 inputCharIdentifier - 是有效的。此类标头后面的字节数总是无法预测,并且通常会有所不同。我的问题是,当出现下一个可用的非损坏数据块时,我需要能够删除或跳过这种情况 - 对于示例文件,最后一个(单个)数据块发生在8 个异位空字节。

8个空字节可以在文件中定位如下: 字节计数器:1056 第 2 行,第 783 列(根据 Notepad++)

问题的症结在于 8 个空字节可以是任意大小 - 3、7、15、50 等。它始终是未知的 - 作为数据损坏的直接结果。但与“传统”数据损坏不同,即在数据块中固定数量的字节,比如 50,可能不可读,因此可以跳过(按确切的字节数) - 我面临的数据损坏包括有效数据块之间的字节数未知。

【问题讨论】:

    标签: c# .net parsing loops binaryreader


    【解决方案1】:

    您不能为这些情况分配案例,因为目标变量 (inputCharIdentifier) 为空;因此,避免这些情况就足够了。我还包含了一个 try...catch,只是为了完全确定(执行所有给定操作时的任何错误都会使代码自动跳到下一个迭代)。

    try
    {
        using (BinaryReader reader = new BinaryReader(File.Open(fileName, FileMode.Open), Encoding.ASCII))
        {
            while (reader.BaseStream.Position != reader.BaseStream.Length)
            {
                inputCharIdentifier = reader.ReadChar();
                if(inputCharIdentifier != null)
                {
                   switch (inputCharIdentifier)
                     case 'A':
                        field1 = reader.ReadUInt64();
                        field2 = reader.ReadUInt64();
                        field3 = reader.ReadChars(10);
                        string strtmp = new string(field3);
                        //and so on
                        using (TextWriter writer = File.AppendText("outputA.txt"))
                        {
                           writer.WriteLine(field1 + "," + field2 + "," + strtmp); 
                        }
                     case 'B':
                       //code...
                }
            }
        }
    }
    catch
    {
    }
    

    【讨论】:

    • 好的,谢谢!这对具有损坏数据的块起到了作用(例如,案例 A 具有预期的 40 个字节,但有 40 个不可读字节)。我现在需要解决 A 是代码块标头但数据损坏更严重的情况 - 例如,考虑 46 个字节(我事先不知道其长度),全部为空或不可读。我如何进行下一个代码块?我之所以问,是因为当确定的案例正确(或具有误导性)时,我上面的 peekchar 代码将不起作用,但数据本身不是 [由于数据损坏]。
    • 这是我收到的错误:System.ArgumentException: 输出字符缓冲区太小,无法包含解码的字符,编码为 'Unicode (UTF-8)' fallback 'System.Text.DecoderReplace mentFallback'。参数名称:chars 基于第61行代码,即inputCharIdentifier = reader.ReadChar();
    • 在给定级别上模拟这些想法(确保给定变量不为 null + try...catch 的条件)。您可以将一个嵌套在其他任意多个条件中,并根据需要尝试...cath。
    • 无论如何,请记住,建议的 try...catch 会处理其中发生的任何错误。那么这个变量到底在哪里呢?
    • 我看到了问题。我修改了我的答案并将 inputCharIdentifier = reader.ReadChar();里面试试。在任何情况下,请记住 try...catch 的效率非常低,只能在极端条件下使用。您应该理解为什么会出现这些错误并改进算法,而不是使用错误的算法并让捕获来处理所有事情。
    猜你喜欢
    • 2012-09-16
    • 1970-01-01
    • 1970-01-01
    • 2019-01-11
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多