fseek
fstream=fopen("file", "rb");
Затем возвращаюсь на 3 символа назад
fseek(fstream,-3,SEEK_CUR);
Но fseek возвращает -1.
В чем дело?
У меня следующая ситуация: читаю 3 байта из файла, открытого через
fstream=fopen("file", "rb");
Затем возвращаюсь на 3 символа назад
fseek(fstream,-3,SEEK_CUR);
Но fseek возвращает -1.
В чем дело?
Ты находишься в самом начале файла, в позиции номер 0. Позиции -3 в файле нет и быть не может. Прости.=)
Ты находишься в самом начале файла, в позиции номер 0. Позиции -3 в файле нет и быть не может. Прости.=)
Не согласен. Если использовать в качестве offset отрицательное число, указатель перемещается назад, а SEEK_CUR означает, что перемещение происходит относительно тек. позиции.
Не согласен. Если использовать в качестве offset отрицательное число, указатель перемещается назад, а SEEK_CUR означает, что перемещение происходит относительно тек. позиции.
Это ты все правилно говоришь, но есди код такой
fstream=fopen("file", "rb");
fseek(fstream,-3,SEEK_CUR);
/*...*/
То ты, все же, не прав. При инициализации указатель находится в нулевой позиции. И минус третьей не существует.
(Это все так, если между двумя строчками кода ничего другого нет.)
Вот.
Это ты все правилно говоришь, но есди код такой
fstream=fopen("file", "rb");
fseek(fstream,-3,SEEK_CUR);
/*...*/
То ты, все же, не прав. При инициализации указатель находится в нулевой позиции. И минус третьей не существует.
(Это все так, если между двумя строчками кода ничего другого нет.)
Вот.
Полный код такой (его можно целиком скопировать и запустить, создав файл "test" в директории с проектом).
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const char
toEOF = char(0),
toSymbol = char(1),
toString = char(2),
toInteger = char(3),
toFloat = char(4),
toWString = char(5);
const int ParseBufSize=4096;
char* LineStart(char* buffer, char* bufpos)
{
char *c;
c=bufpos;
while(c>=buffer && c<=bufpos)
{
if (c[0]=='\n') return (c);
c--;
}
return buffer;
}
class TParser
{
private:
FILE *stream;
int forigin; // позиция в тексте
char* fbuffer;// указатель на буфер с частью текста
char* fbufptr;// позиция в буфере
char* fbufend; // указывает на следующий за буфером байт
char* fsourceptr;// бежит по буферу
char* fsourceend;
char* ftokenptr;
char* fstringptr;
int fsourceline;
char fsavechar;
char ftoken;
char ffloattype;
void SkipBlanks();
void ReadBuffer();
public:
TParser(FILE *stream);
~TParser();
char NextToken();
};
TParser:: TParser(FILE *stream1)
{
stream=stream1;
fbuffer=(char*)malloc(ParseBufSize);
fbuffer[0]=0;
fbufptr=fbuffer;
fbufend=fbuffer+ParseBufSize;
fsourceptr=fbuffer;
fsourceend=fbuffer;
ftokenptr=fbuffer;
fsourceline=1;
NextToken();
}
TParser::~TParser()
{
if (fbuffer!=NULL)
{
fseek(stream, long (ftokenptr-fbufptr), SEEK_CUR);
free(fbuffer);
}
}
void TParser::SkipBlanks(void)
{
for(;;)
{
if (fsourceptr[0]==0)
{
ReadBuffer();
if (fsourceptr[0]==0) break;
continue;
}
else if (fsourceptr[0]==10)
{
fsourceline++;
}
else if (fsourceptr[0]>=33 && fsourceptr[0]<=255)
{
break;
}
fsourceptr++;
}
}
void TParser::ReadBuffer(void)
{
int count;
size_t br;
forigin += fsourceptr-fbuffer;
fsourceend[0]=fsavechar;
count=fbufptr-fsourceptr;
if (count!=0)
memmove((void*)fsourceptr[0],(void*)fbuffer[0], count);
fbufptr=fbuffer+count;
br = fread((void*)&fbufptr[0], (size_t)sizeof(char), size_t(fbufend - fbufptr), stream);
if( ferror( stream ) ) {
perror( "Read error" );
}
fbufptr +=br;
fsourceptr = fbuffer;
fsourceend = fbufptr;
if (fsourceend == fbufend)
{
fsourceend=LineStart(fbuffer, fsourceend-1);
if (fsourceend==fbuffer) ;//error(SLineTooLong);
}
fsavechar=fsourceend[0];
fsourceend[0]=0;
}
char TParser::NextToken(void)
{
char *p;
const char *const kav="'";
char result;
SkipBlanks();
p=fsourceptr;
ftokenptr=p;
if ( (*p>='a' && *p<='z') || (*p>='A' && *p<='Z') || (*p=='_') )
{
p++;
while ( (*p>='a' && *p<='z') || (*p>='A' && *p<='Z') || (*p=='_') || (*p>='0' && *p<='9') )
p++;
result=toSymbol;
}
else
{
switch (*p)
{
//case '#': case kav:
// break;
default:
result=*p;
if (result!=toEOF) p++;
}
}
fsourceptr = p;
ftoken = result;
return result;
}
void main(void)
{
FILE *file;
file=fopen("test","rb");
TParser Parser(file);
fclose(file);
}
Это класс парсера. Всего четыре операции с файлами:
1) открытие бинарного файла на чтение - в main
2) в конструкторе классе TParser в процедуре ReadBuffer - чтение данных (4096 байт) из файла в буфер
3) в деструкторе класса - возврат позиции в файле на прежнее место
4) закрытие файла - в main
А теперь, если в файле "test" поместить 3 символа (3 байта) и поставить метку на fseek в деструкторе класса можно увидеть - возникает ошибка (fseek возвращает -1).
{
FILE *file;
file=fopen("test","rb");
TParser Parser(file);
fclose(file);
}
Деструктор класса TParser вызывается после fclose. После этого никакие fseek работать с файлом не могут.
С вас 20 коп.
Деструктор класса TParser вызывается после fclose. После этого никакие fseek работать с файлом не могут.
С вас 20 коп.
Хорошо, я вставил вызов деструктора перед fclose,
Parser.~TParser();
Но тогда деструктор вызывается дважды - явно перед fclose и после fclose. Как добиться однократного выполнения деструктора?
Хорошо, я вставил вызов деструктора перед fclose,
Parser.~TParser();
Но тогда деструктор вызывается дважды - явно перед fclose и после fclose. Как добиться однократного выполнения деструктора?
Деструкторы никто так не вызывает, если только к тому нет чрезвычайно важных оснований. Поставьте лучше fclose() в дестуктор.
Деструкторы никто так не вызывает, если только к тому нет чрезвычайно важных оснований. Поставьте лучше fclose() в дестуктор.
А если мне нужно будет работать затем с этим файлом... В конструкторе TParser файл ведь не открывается.
В Delphi можно явно вызвать деструктор и он сработает один раз.
В Си++ это вообще возможно?
А если мне нужно будет работать затем с этим файлом... В конструкторе TParser файл ведь не открывается.
В Delphi можно явно вызвать деструктор и он сработает один раз.
В Си++ это вообще возможно?
Команду возврата позиции на прежнее место можно перенести в конструктор в последнюю позицию.
Команду возврата позиции на прежнее место можно перенести в конструктор в последнюю позицию.
Зачем? Ведь вернуть каретку нужно после чтения всех данных, т.е. в деструкторе.
Я решил сделать по-другому - вместо FILE *stream; использовать CFile *stream; (из MFC)
Тогда проблема полностью снимается и явно уничтожать объект класса Tparser и закрывать файл не придется - вызовутся соответствующие деструторы.
Но. написал процедуру error, которая должна выдать сообщение при возникновении ошибки парсинга и возбудить исключение
void TParser::errorstr(const char*message)
{
char buffer[100]="1";
sprintf(buffer,"%s: ñòðîêà %d",message,fsourceline);
AfxMessageBox( (LPCTSTR)&buffer[0] );
AfxThrowUserException( );
}
Но в консольном проекте на AfxMessageBox возникает Unhandled Exception. Что делать?
Зачем? Ведь вернуть каретку нужно после чтения всех данных, т.е. в деструкторе.
Я решил сделать по-другому - вместо FILE *stream; использовать CFile *stream; (из MFC)
Тогда проблема полностью снимается и явно уничтожать объект класса Tparser и закрывать файл не придется - вызовутся соответствующие деструторы.
Но. написал процедуру error, которая должна выдать сообщение при возникновении ошибки парсинга и возбудить исключение
void TParser::errorstr(const char*message)
{
char buffer[100]="1";
sprintf(buffer,"%s: ошибка; %d",message,fsourceline);
AfxMessageBox( (LPCTSTR)&buffer[0] );
AfxThrowUserException( );
}
Но в консольном проекте на AfxMessageBox возникает Unhandled Exception. Что делать?
Сначала о простом.
Функции Afx* можно вызывать только в проекте с поддержкой MFC. То же относится и к MFCшным классам вроде CFile.
Если мне не изменяет память, в MS VC v.6 можно сделать консольное приложение с MFC. Ну, а если не хотите, тогда пользуйтесь MessageBox().
Далее. Деструкторы на то и созданы, чтобы вызываться при уничтожении своего объекта. Поэтому можете сделать, например, так:
{
FILE *file;
file=fopen("test","rb");
TParser* pParser = new TParser(file);
delete pParser;
fclose(file);
}
Но если по-честному, то в стиле C++ деструктор должен чистить ресурсы за объектом, и ничего более. В вашем случае, как видно, TParser должен вызываться много раз, парсить что-то и готовить окружение к следующему парсингу. Тогда надо создать класс, который будет всем этим управлять. Например, так
public:
CControlParsing(FILE *file); // тут открывается файл
~CControlParsing(); // тут закрывается файл
void Parse(); // тут создаётся парсер с уже открытым файлом и тут же он убивается
};
Сначала о простом.
Функции Afx* можно вызывать только в проекте с поддержкой MFC. То же относится и к MFCшным классам вроде CFile.
Если мне не изменяет память, в MS VC v.6 можно сделать консольное приложение с MFC. Ну, а если не хотите, тогда пользуйтесь MessageBox().
Далее. Деструкторы на то и созданы, чтобы вызываться при уничтожении своего объекта. Поэтому можете сделать, например, так:
{
FILE *file;
file=fopen("test","rb");
TParser* pParser = new TParser(file);
delete pParser;
fclose(file);
}
Но если по-честному, то в стиле C++ деструктор должен чистить ресурсы за объектом, и ничего более. В вашем случае, как видно, TParser должен вызываться много раз, парсить что-то и готовить окружение к следующему парсингу. Тогда надо создать класс, который будет всем этим управлять. Например, так
public:
CControlParsing(FILE *file); // тут открывается файл
~CControlParsing(); // тут закрывается файл
void Parse(); // тут создаётся парсер с уже открытым файлом и тут же он убивается
};
На самом деле TParser, который здесь пытаюсь реализовать есть в Delphi и CBuilder'e. Я решил его адаптировать для Visual C++.
Дополнительный класс вводить необязательно - все делается через TParser. А класс CFile (вместо FILE) нужен для того, чтобы осуществлять ввод-вывод из любого окна, файла, области памяти. Поскольку в любом случае будут использоваться операции seek и read.
Открытие и закрытие файлов при использовании класса CFile осуществляются прозрачно для пользователя вне класса TParser. TParser только читает и позиционирует обратно каретку.
Кстати, в CBuilder'е этот класс описан (файл classes), но там используется класс TStream для чтения и позиционирования.