Необходимо распарсить файл. Формат данных более-менее приемлимый: экспорт заявок, размер файлов 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
//проверка флагов для потоков
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 для обоих файлов.
Комментариев нет:
Отправить комментарий