как продолжить цикл, несмотря на ошибку при разборе байтовых данных

мой вопрос является продолжением этого: (цикл для чтения различных типов и размеров данных из очень большого массива байтов из файла)

У меня есть необработанный поток байтов, хранящийся в файле (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

По сути, это непрерывный ввод байтов в стиле шестнадцатеричного дампа без каких-либо разделителей строк или пробелов между данными, которые необходимо проанализировать. Данные, как показано выше, состоят из разных типов данных один за другим.

Вот фрагмент моего кода — поскольку ни в одном поле нет запятых и нет необходимости использовать «» (т. е. оболочку 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...

Мой вопрос основан на том факте, что некоторые необработанные байтовые данные содержат нулевые значения, которые трудно проанализировать, потому что существует неизвестное количество нулевых байтов (или ненулевых, -разместите байты) между последовательными блоками данных (каждый из которых начинается с 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;

Оставшаяся часть — добавление кода в каждый случай переключения (например, «A») для продолжения без остановки программы — есть ли способ сделать это без нескольких блоков try-catch? [т.е. идентификатор символа блока кода - A, но байты после A повреждены - в этом случае мне нужно выйти из цикла ИЛИ прочитать (т.е. пропустить) определенное количество байтов - которое здесь будет известно, если заголовок сообщения правильно идентифицирует остальные байты.

[Примечание: Варианты A, B и т. д. имеют разный размер входных данных — другими словами, A может иметь всего 40 байтов, а B — 50 байтов. Таким образом, использование буфера фиксированного размера, например, inputBuf[1000] или [50] — если бы все они были одинакового размера — тоже не сработало бы, насколько мне известно.]

Какие-либо предложения? Пожалуйста помоги! Я относительно новичок в С# (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, внутри блока данных может быть нечитаемым и, следовательно, может быть пропущено (на это точное количество байтов) — повреждение данных, с которым я сталкиваюсь, состоит из неизвестное количество байтов между допустимыми блоками данных.


person John    schedule 19.06.2013    source источник


Ответы (1)


Вы не можете назначить случай для этих ситуаций, потому что целевая переменная (inputCharIdentifier) ​​имеет значение null; таким образом, достаточно условия, избегающего этих случаев. Я также включил 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
{
}
person varocarbas    schedule 19.06.2013
comment
ок здорово, спасибо! это сработало для блока с поврежденными данными (скажем, случай A с 40 байтами, как и ожидалось, но 40 нечитаемых байтов). теперь мне нужно рассмотреть ситуацию, когда A является заголовком блока кода, но повреждение данных более серьезное - рассмотрим, например, 46 байтов (длина которых я не знаю заранее), все либо нулевые, либо нечитаемые. как перейти к следующему блоку кода? я спрашиваю, потому что мой код peekchar выше не будет работать, когда идентифицированный регистр правильный (или вводящий в заблуждение), но сами данные нет [из-за повреждения данных]. - person John; 19.06.2013
comment
вот ошибка, которую я получаю: System.ArgumentException: буфер выходных символов слишком мал, чтобы содержать декодированные символы, кодировка «Юникод (UTF-8)», резервный вариант «System.Text.DecoderReplace mentFallback». Имя параметра: chars на основе кода в строке 61, т. е. inputCharIdentifier = reader.ReadChar(); - person John; 19.06.2013
comment
Эмулируйте эти идеи (условие, гарантирующее, что данная переменная не равна нулю + try... catch) на заданном уровне. Вы можете вкладывать одно в другое сколько угодно условий и пробовать ... кат, сколько пожелаете. - person varocarbas; 19.06.2013
comment
В любом случае имейте в виду, что предложенный try...catch позаботится о любой ошибке, возникающей внутри него. Так где именно находится эта переменная? - person varocarbas; 19.06.2013
comment
Я вижу проблему. Я изменил свой ответ и поставил inputCharIdentifier = reader.ReadChar(); внутри попробуй. В любом случае, имейте в виду, что try...catch очень неэффективен и должен использоваться только в экстремальных условиях. Вместо того, чтобы иметь неправильный алгоритм и позволить отлову заботиться обо всем, вы должны понять, почему возникают эти ошибки, и улучшить алгоритм. - person varocarbas; 19.06.2013
comment
на данный момент я получаю бесконечный цикл исключений, помещая reader.ReadChar() в блок try-catch. Но, в частности, как мне пропустить те 46 байтов, которые все нулевые или нечитаемые, но имеют (в противном случае) действительный заголовок? есть ли способ использовать цикл do while в случае по умолчанию, чтобы продолжить попытки прочитать символ, пока он не достигнет заголовка блока A/B, а затем попытаться проанализировать данные для этого конкретного блока? и пропустить нулевые байты, если блок поврежден? - person John; 19.06.2013
comment
Правильная информация должна быть получена от reader.ReadChar(), если информация неверна (или равна нулю), ничего нельзя сделать (весь приведенный ниже код), кроме как игнорировать. Ваша ошибка и ваше обновление говорят о том, что переменная inputCharIdentifier не может обрабатывать информацию от reader.ReadChar() в какой-то момент, и поэтому чтение не происходит, и это является причиной бесконечного цикла (цикл завершается, когда все чтение делается, но если чтение никогда не выполняется...). Если вы загрузите этот файл и весь код, я могу провести несколько тестов и предложить вам лучшее решение. - person varocarbas; 19.06.2013
comment
Как вы объявляете inputCharIdentifier? - person varocarbas; 19.06.2013
comment
inputCharIdentifier инициализируется как несущественный символ, скажем, «X» — вне цикла — и затем динамически назначается внутри цикла одному из случаев в зависимости от заданных данных — «A», «B», «C» и скоро. Я скоро загружу код на свой вопрос - спасибо за вашу помощь! - person John; 19.06.2013
comment
Честно говоря, я не понимаю, что вы имели в виду под несущественным персонажем (Чар?!); Я лучше посмотрю код и проблемный файл. Имейте в виду, что ошибка относится к проблеме с кодировкой; для файла может потребоваться кодировка, отличная от кодировки в потоковом считывателе. Это больше тестирование и исправление методом проб и ошибок, чем другие вещи (= то, что вы должны сделать самостоятельно), но я быстро посмотрю, чтобы увидеть, могу ли я чем-то помочь. - person varocarbas; 19.06.2013
comment
несущественный символ относится к инициализации inputCharidentifier как ненулевой переменной, чтобы избежать ошибки на раннем этапе. который можно изменить при необходимости. пожалуйста, взгляните на полный код, который я загрузил, так как на данный момент я зашел в тупик. Благодарность - person John; 21.06.2013
comment
(Инициализация переменной является обязательным предварительным действием для чего-либо; не знал, что у нее есть специальное имя). Этот код (+ предлагаемые включения) должен подойти практически для любого случая в стандартных условиях. Проблема в том, что вы хотите иметь дело с поврежденными файлами (вся кодировка мне не ясна из-за ошибки, которую вы получаете). Если вам нужна быстрая и конкретная помощь от меня (или любого другого), вы должны включить файл, который вызывает проблемы. - person varocarbas; 21.06.2013
comment
Я добавил к вопросу образец входного файла rawbytes.txt. Пожалуйста, посмотрите, когда сможете - я зашел в тупик, как решить эту проблему. Спасибо - person John; 24.06.2013
comment
Я посмотрю на это и обновлю свой ответ как можно скорее. - person varocarbas; 24.06.2013
comment
Этот файл отлично работает на компьютере или, по крайней мере, так кажется. Это тот результат, который вы ожидали? (Первые 3 строки) 123456789299876543212, pcrstuvwxy, z, 1236 5680 56029802 12345678959876543215, pdrstuvwxy, z, 1239 568356999987683678999876543219, perstuvwxy, z, 1243 5687 5609 9809 Проблема связана с кодировкой; вы не устанавливаете тип кодировки в своем двоичном ридере, и поэтому он использует тот, который по умолчанию используется на вашем компьютере (очевидно, отличается от того, что у меня). Пожалуйста, подтвердите, что это правильный вывод (или напишите правильный), и я расскажу вам, как его получить. - person varocarbas; 24.06.2013
comment
Да, я получаю тот же результат и для первых нескольких строк, но моя программа падает, как только достигает этих 8 нулевых байтов ближе к концу файла. По сути, после PZRSTUVWXY,Z есть пакет, который вызывает ошибку. Я до сих пор не смог определить, как пропустить такие неуместные байты, учитывая, что количество этих байтов всегда варьируется и что им обычно предшествует допустимый в остальном inputCharIdentifier, т.е. A в этом случае. Какую кодировку вы (или ваша система) используете для BinaryReader? - person John; 25.06.2013
comment
Я использую ASCII (я отредактировал свой ответ, включая его), и я не получаю никаких ошибок (только некоторые странные символы). Поймите, что проблема, с которой вы столкнулись, в основном инициализирует массив символов размером 3, а затем намеревается поместить что-то с длиной 5; у вас точно будет ошибка. Если часть, которой назначено это значение, также отвечает за то, чтобы цикл продолжался, у вас есть проблема, единственное решение которой, похоже, не может справиться с этим файлом. В любом случае, я надеюсь, что ASCII избежит этой или любой другой проблемы, и мы наконец сможем закрыть этот вопрос :) - person varocarbas; 25.06.2013
comment
Имейте в виду, что вы имеете дело не с битами/байтами (простыми в управлении), а с символами (где имеет значение кодировка и, следовательно, вышеупомянутые различия между длиной данного элемента и длиной, ожидаемой целевой переменной/массивом). - person varocarbas; 25.06.2013
comment
Точно. Последнее предложение - вкратце, есть ли способ перемещать счетчик потока Binary Reader вперед на 1 каждый раз, когда возникает ошибка? Другими словами, если за назначением inputCharIdentifier следуют 7 нулевых байтов вместо ожидаемых 50, можем ли мы просто указать программе повторить попытку присвоения символа еще 7 раз, пока не будет достигнут следующий допустимый блок данных? - person John; 25.06.2013
comment
Я не могу придумать надежного решения для этого. Видите ли, во-первых, есть элементы 1-длины (в случае неправильного, вы можете перейти к следующему); но символы не имеют постоянной длины (именно это провоцирует вашу проблему) и, таким образом, как вы можете знать, что следующая часть будет читаться, не зная поврежденной длины в битах? (это не так просто, как null = 1, но сбой = ?). Вы можете установить вторичные биты чтения цикла, чтобы определить первый допустимый бит, но это также не обязательно должно работать. Решение простое: полагайтесь на правильную систему кодирования, и все должно быть в порядке. - person varocarbas; 25.06.2013
comment
Кроме того, я думаю, что это довольно долго для простого вопроса. Если вы не считаете, что этот ответ является адекватным (или что его можно улучшить), возможно, вам следует создать награду. - person varocarbas; 25.06.2013
comment
Нет, я не могу этого сделать, потому что у меня еще недостаточно репутации, чтобы создать награду. Спасибо за твою помощь. Я попытаюсь решить проблему извне (т. е. создать ситуацию, когда анализируемый файл не содержит повреждений данных). Другими словами, двоичный/текстовый файл больше не будет состоять из необработанных байтов, а будет представлять собой предварительно обработанные байты. - person John; 25.06.2013
comment
Добро пожаловать. Но если для вас это так важно и предложенная кодировка не дает желаемого, решение явно идет по крупицам. Вы должны сосредоточить свои тесты на поврежденных файлах (это проблемные файлы) и выяснить, может ли побитовый анализ позволить вам распознавать разные фрагменты (символы). Вы должны продолжать фокусироваться на отправленном файле и, в любом случае, на поврежденных файлах. - person varocarbas; 25.06.2013
comment
давайте продолжим это обсуждение в чате - person John; 25.06.2013