Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

fseek

284
17 мая 2005 года
michael_is_98
587 / / 25.02.2005
У меня следующая ситуация: читаю 3 байта из файла, открытого через
fstream=fopen("file", "rb");
Затем возвращаюсь на 3 символа назад
fseek(fstream,-3,SEEK_CUR);
Но fseek возвращает -1.
В чем дело?
301
17 мая 2005 года
lord Kelvin
897 / / 08.11.2004
Цитата:
Originally posted by michael_is_98
У меня следующая ситуация: читаю 3 байта из файла, открытого через
fstream=fopen("file", "rb");
Затем возвращаюсь на 3 символа назад
fseek(fstream,-3,SEEK_CUR);
Но fseek возвращает -1.
В чем дело?


Ты находишься в самом начале файла, в позиции номер 0. Позиции -3 в файле нет и быть не может. Прости.=)

284
18 мая 2005 года
michael_is_98
587 / / 25.02.2005
Цитата:
Originally posted by lord Kelvin
Ты находишься в самом начале файла, в позиции номер 0. Позиции -3 в файле нет и быть не может. Прости.=)


Не согласен. Если использовать в качестве offset отрицательное число, указатель перемещается назад, а SEEK_CUR означает, что перемещение происходит относительно тек. позиции.

301
18 мая 2005 года
lord Kelvin
897 / / 08.11.2004
Цитата:
Originally posted by michael_is_98
Не согласен. Если использовать в качестве offset отрицательное число, указатель перемещается назад, а SEEK_CUR означает, что перемещение происходит относительно тек. позиции.


Это ты все правилно говоришь, но есди код такой

 
Код:
/*...*/
fstream=fopen("file", "rb");
fseek(fstream,-3,SEEK_CUR);
/*...*/

То ты, все же, не прав. При инициализации указатель находится в нулевой позиции. И минус третьей не существует.
(Это все так, если между двумя строчками кода ничего другого нет.)
Вот.
284
18 мая 2005 года
michael_is_98
587 / / 25.02.2005
Цитата:
Originally posted by lord Kelvin
Это ты все правилно говоришь, но есди код такой
 
Код:
/*...*/
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).

425
18 мая 2005 года
sq_deep
498 / / 18.02.2005
Цитата:
Originally posted by michael_is_98
 
Код:
void main(void)
{
    FILE *file;
    file=fopen("test","rb");
    TParser Parser(file);
    fclose(file);
}

Деструктор класса TParser вызывается после fclose. После этого никакие fseek работать с файлом не могут.

С вас 20 коп.

284
18 мая 2005 года
michael_is_98
587 / / 25.02.2005
Цитата:
Originally posted by sq_deep
Деструктор класса TParser вызывается после fclose. После этого никакие fseek работать с файлом не могут.

С вас 20 коп.



Хорошо, я вставил вызов деструктора перед fclose,
Parser.~TParser();
Но тогда деструктор вызывается дважды - явно перед fclose и после fclose. Как добиться однократного выполнения деструктора?

425
18 мая 2005 года
sq_deep
498 / / 18.02.2005
Цитата:
Originally posted by michael_is_98
Хорошо, я вставил вызов деструктора перед fclose,
Parser.~TParser();
Но тогда деструктор вызывается дважды - явно перед fclose и после fclose. Как добиться однократного выполнения деструктора?

Деструкторы никто так не вызывает, если только к тому нет чрезвычайно важных оснований. Поставьте лучше fclose() в дестуктор.

284
18 мая 2005 года
michael_is_98
587 / / 25.02.2005
Цитата:
Originally posted by sq_deep
Деструкторы никто так не вызывает, если только к тому нет чрезвычайно важных оснований. Поставьте лучше fclose() в дестуктор.


А если мне нужно будет работать затем с этим файлом... В конструкторе TParser файл ведь не открывается.
В Delphi можно явно вызвать деструктор и он сработает один раз.
В Си++ это вообще возможно?

488
18 мая 2005 года
Mоngооsе
465 / / 01.04.2005
Цитата:
Originally posted by michael_is_98
А если мне нужно будет работать затем с этим файлом... В конструкторе TParser файл ведь не открывается.
В Delphi можно явно вызвать деструктор и он сработает один раз.
В Си++ это вообще возможно?

Команду возврата позиции на прежнее место можно перенести в конструктор в последнюю позицию.

284
19 мая 2005 года
michael_is_98
587 / / 25.02.2005
Цитата:
Originally posted by Mоngооsе
Команду возврата позиции на прежнее место можно перенести в конструктор в последнюю позицию.


Зачем? Ведь вернуть каретку нужно после чтения всех данных, т.е. в деструкторе.
Я решил сделать по-другому - вместо FILE *stream; использовать CFile *stream; (из MFC)
Тогда проблема полностью снимается и явно уничтожать объект класса Tparser и закрывать файл не придется - вызовутся соответствующие деструторы.

Но. написал процедуру error, которая должна выдать сообщение при возникновении ошибки парсинга и возбудить исключение

void TParser::errorstr(const char*message)
{
char buffer[100]="1";
sprintf(buffer,"%s: &#241;&#242;&#240;&#238;&#234;&#224; %d",message,fsourceline);

AfxMessageBox( (LPCTSTR)&buffer[0] );
AfxThrowUserException( );
}
Но в консольном проекте на AfxMessageBox возникает Unhandled Exception. Что делать?

425
19 мая 2005 года
sq_deep
498 / / 18.02.2005
Цитата:
Originally posted by michael_is_98
Зачем? Ведь вернуть каретку нужно после чтения всех данных, т.е. в деструкторе.
Я решил сделать по-другому - вместо 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().

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

 
Код:
void main(void)
{
    FILE *file;
    file=fopen("test","rb");
    TParser* pParser = new TParser(file);
    delete pParser;
    fclose(file);
}
Тогда будет работать.

Но если по-честному, то в стиле C++ деструктор должен чистить ресурсы за объектом, и ничего более. В вашем случае, как видно, TParser должен вызываться много раз, парсить что-то и готовить окружение к следующему парсингу. Тогда надо создать класс, который будет всем этим управлять. Например, так
 
Код:
class CControlParsing
public:
    CControlParsing(FILE *file); // тут открывается файл
    ~CControlParsing();  // тут закрывается файл
    void Parse();        // тут создаётся парсер с уже открытым файлом и тут же он убивается
};
И вообще принцип обектного программирования такой: появилась какая-нибудь сущность — создай из неё класс. В будущем, когда окажется, что сущность изменилась, вы модифицируете этот класс, не трогая остальной части программы.
284
21 мая 2005 года
michael_is_98
587 / / 25.02.2005
Цитата:
Originally posted by sq_deep
Сначала о простом.

Функции Afx* можно вызывать только в проекте с поддержкой MFC. То же относится и к MFCшным классам вроде CFile.

Если мне не изменяет память, в MS VC v.6 можно сделать консольное приложение с MFC. Ну, а если не хотите, тогда пользуйтесь MessageBox().

Далее. Деструкторы на то и созданы, чтобы вызываться при уничтожении своего объекта. Поэтому можете сделать, например, так:
 
Код:
void main(void)
{
    FILE *file;
    file=fopen("test","rb");
    TParser* pParser = new TParser(file);
    delete pParser;
    fclose(file);
}
Тогда будет работать.

Но если по-честному, то в стиле C++ деструктор должен чистить ресурсы за объектом, и ничего более. В вашем случае, как видно, TParser должен вызываться много раз, парсить что-то и готовить окружение к следующему парсингу. Тогда надо создать класс, который будет всем этим управлять. Например, так
 
Код:
class CControlParsing
public:
    CControlParsing(FILE *file); // тут открывается файл
    ~CControlParsing();  // тут закрывается файл
    void Parse();        // тут создаётся парсер с уже открытым файлом и тут же он убивается
};
И вообще принцип обектного программирования такой: появилась какая-нибудь сущность — создай из неё класс. В будущем, когда окажется, что сущность изменилась, вы модифицируете этот класс, не трогая остальной части программы.



На самом деле TParser, который здесь пытаюсь реализовать есть в Delphi и CBuilder'e. Я решил его адаптировать для Visual C++.

Дополнительный класс вводить необязательно - все делается через TParser. А класс CFile (вместо FILE) нужен для того, чтобы осуществлять ввод-вывод из любого окна, файла, области памяти. Поскольку в любом случае будут использоваться операции seek и read.

Открытие и закрытие файлов при использовании класса CFile осуществляются прозрачно для пользователя вне класса TParser. TParser только читает и позиционирует обратно каретку.

Кстати, в CBuilder'е этот класс описан (файл classes), но там используется класс TStream для чтения и позиционирования.

Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог