мой вопрос является продолжением этого: (цикл для чтения различных типов и размеров данных из очень большого массива байтов из файла)
У меня есть необработанный поток байтов, хранящийся в файле (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, внутри блока данных может быть нечитаемым и, следовательно, может быть пропущено (на это точное количество байтов) — повреждение данных, с которым я сталкиваюсь, состоит из неизвестное количество байтов между допустимыми блоками данных.