Страницы

Поиск по вопросам

среда, 10 июля 2019 г.

Парсинг бинарного файла

Необходимо распарсить файл. Формат данных более-менее приемлимый: экспорт заявок, размер файлов 1-25 мб, в каждой строке до символа ':' тэг, после него данные, строки кончаются 0x0D 0x0A, заявки отделены 0x0C).
Читал бы построчно, но проблема в символах 0x00, которых понатыкано очень много и в разных местах и последовательностях (да и 0x0A 0x0D местами 10-20 подряд).
Поэтому файл пробую читать как бинарный, для проверки пишу содержимое в другой файл.
Под debug нет проблем, в конце чтения выставляются EOF и FAIL, в конце записи почему-то BAD
Под release данные пишутся, но в конце записи все флаги не выставлены, в конце файла многочисленные символы 0x00 (для Unicode при сколько-нибудь крупном файле, странно - для мелочи в 1-100 строк такое не происходит) или вообще мусор в разнобой (для multibyte).
В чём может быть проблема? Тестировал на исходном файле с экспортом заявок и на файле-тестовике, генерируемом PrepareForTestBinary. Пробовал включить исключения для потоков, но ничего интереснее ios_base::failbit set ; iostream:1 не выдаёт.
У меня MS VS2010
Командная строка компилятора под Release
/Zi /nologo /W3 /WX- /O2 /Oi /Oy- /GL /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /Gm- /EHsc /GS /Gy /fp:precise /Zc:wchar_t /Zc:forScope /Fp"Release\binread.pch" /Fa"Release\" /Fo"Release\" /Fd"Release\vc100.pdb" /Gd /analyze- /errorReport:queue
Командная строка компилятора под Debug
/ZI /nologo /W3 /WX- /Od /Oy- /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_UNICODE" /D "UNICODE" /Gm /EHsc /RTC1 /GS /fp:precise /Zc:wchar_t /Zc:forScope /Fp"Debug\binread.pch" /Fa"Debug\" /Fo"Debug\" /Fd"Debug\vc100.pdb" /Gd /analyze- /errorReport:queue
Игры с опциями ничего не дали.
Вот код:
#ifndef _UNICODE #define TCOUT std::cout #else #define TCOUT std::wcout
typedef std::basic_ios tios; typedef std::basic_ifstream tifstream; typedef std::basic_ofstream tofstream; typedef std::basic_string tstring;
//проверка флагов для потоков void fState(tios &fs) { TCOUT << _T("fail bad eof good
"); TCOUT << (fs.rdstate()&fs.failbit) << _T(" ; ") << (fs.rdstate()&fs.badbit) << _T(" ; ") << (fs.rdstate()&fs.eofbit) << _T(" ; ") << (fs.rdstate()&fs.goodbit) << endl; }
//Для тестирования генерируем исходный файл void PrepareForTestBinary(tstring PATH) { tofstream os; os.open (PATH, ios_base::binary ||ios_base::out); for (int i = 0; i<200000;i++) os << _T("0123456789") << '\0' << _T("ABCDEFGHIJ") << std::endl; os.close(); } // Читаем и пишем void TestBinary(tstring inPATH,tstring outPATH) { LONGLONG len; TCHAR * buffer; tifstream is; fState(is); is.open (inPATH, ios_base::binary ||ios_base::in ); fState(is); is.seekg (0, ios::end); len = is.tellg(); is.seekg (0, ios::beg); buffer = new TCHAR [len]; fState(is); is.read (buffer,len); fState(is); is.close(); tofstream os; fState(os); os.open (outPATH, ios_base::binary ||ios_base::out); fState(os); os.write(buffer,len); fState(os); os.close(); delete[] buffer; }
int _tmain(int argc, _TCHAR *argv[]) { PrepareForTestBinary(_T("D:\\!binary\\1\\11")); TestBinary(_T("D:\\!binary\\1\\11"),_T("D:\\!binary\\1\\22")); system("pause"); return 0; }
При этом такой способ никаких проблем не даёт (но и проблем моих с парсом не решает):
void TestBinary_bufferExch(tstring inPATH,tstring outPATH) { tifstream is; tofstream os; is.open (inPATH, ios_base::binary || ios_base::in ); os.open (outPATH,ios_base::binary ||ios_base::out); os << is.rdbuf(); is.close(); os.close(); }
Сопутствующие вопросы:
Как оптимально вырезать из потока все символы '0x00' ? Пока ничего умнее не придумал, как в цикле посимвольно сравнивать и все отличные от '0x00' писать в другой буфер(или поток). Как вообще правильно читать поток, содержимое и формат которого не известны? Прошу ткнуть в мало-мальски грамотный парсер(код пощупать).
Обновление
Пробовал даже так, никаких изменений.
void TestBinary(tstring inPATH,tstring outPATH) { LONGLONG len = 0; TCHAR * buffer = 0; tifstream is; fState(is); is.open (inPATH, ios_base::binary ||ios_base::in ); fState(is); is.seekg (0, ios::end); len = is.tellg(); is.seekg (0, ios::beg); buffer = new TCHAR [len]; memset(buffer,0,len); fState(is); is.read (buffer,len); fState(is); is.close(); tofstream os; fState(os); os.open (outPATH, ios_base::binary ||ios_base::out); fState(os); os.write(buffer,len); fState(os); os.close(); delete[] buffer; }
Обновление 2
Провёл доп. исследования. Проблема возникает только при наличии в файле 0x0D 0x0A (endl помещённый в поток). При наличии '\0' (0x00) проблема не возникает.


Ответ

В вашем коде мне кажется я вижу одну неточность. "Побитовое ИЛИ" должно писаться через один символ | (ios_base::binary |ios_base::in). В вашем случае это операция "логического ИЛИ" и результат его скорее всего равен 1 для обоих файлов.

Комментариев нет:

Отправить комментарий