C++ Builder
| Главная | Уроки | Статьи | FAQ | Форум | Downloads | Литература | Ссылки | RXLib | Диски |

 
запись/считывание в файл
k_serg
Отправлено: 18.05.2006, 13:40


Не зарегистрирован







помогите разобраться с записью/считыванием в файл
тип string после открытия файла оказывается пустым.
тип int — нормально.
изменения происходят , перед записью и после проверял все присутствует, при перезапуске программы оба string пусты.
при этом если поменять местами
string string_spisok_ystroistv[10];
unsigned int qqq_n;
в обьявлении то вообще проиходит ерунда.

Unit1.h
...
class TIssledov
{
public:
string string_spisok_ystroistv[10];
unsigned int qqq_n;
string str1;
unsigned int qqq_k;
};
//---------------------------------------------------------------------------
class TForm1 : public TForm
{
__published: // IDE-managed Components
TMemo *Memo1;
TButton *Button1;
TButton *Button2;
void __fastcall Button1Click(TObject *Sender);
void __fastcall Button2Click(TObject *Sender);
private: // User declarations
public: // User declarations
__fastcall TForm1(TComponent* Owner);
TIssledov Issledov;
};
//---------------------------------------------------------------------------
extern PACKAGE TForm1 *Form1;
//---------------------------------------------------------------------------




Unit1.cpp
...

AnsiString put_dir;
//---------------------------------------------------------------------------
__fastcall TForm1::TForm1(TComponent* Owner)
: TForm(Owner)
{
put_dir=GetCurrentDir();
//----считать-----
AnsiString File_Name_str = put_dir + AnsiString("\\")+ AnsiString("Issledov.dat");
int iFile = FileOpen(File_Name_str, fmOpenRead);
if(iFile==-1){ShowMessage("ошибка открытия файла Issledov.dat"); }
int err = FileRead(iFile,&Issledov, sizeof(TIssledov));
if( err==-1) {ShowMessage("невозможно прочитать данные Issledov.dat");FileClose(iFile);return; }
FileClose(iFile);
Memo1->Lines->Add(Issledov.str1.c_str());
for (int k=0;k<10;k++) {Memo1->Lines->Add(AnsiString((Issledov.string_spisok_ystroistv[k]).c_str())); }

}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button1Click(TObject *Sender)
{
//------изменить-------
Issledov.qqq_n=1;
Issledov.str1="12345";
for (int k=0;k<10;k++) {Issledov.string_spisok_ystroistv[k]=(IntToStr(k)).c_str();}
Issledov.qqq_k=2;
Memo1->Lines->Add(Issledov.str1.c_str());
for (int k=0;k<10;k++) {Memo1->Lines->Add(AnsiString((Issledov.string_spisok_ystroistv[k]).c_str())); }
}
//---------------------------------------------------------------------------
void __fastcall TForm1::Button2Click(TObject *Sender)
{
//-----записать-----------
AnsiString File_Name_str = put_dir + AnsiString("\\")+ AnsiString("Issledov.dat");
int iFile = FileCreate(File_Name_str);
if(iFile==-1){ShowMessage("ошибка открытия файла Issledov.dat"); }
FileWrite(iFile,&Issledov, sizeof(TIssledov));
FileClose(iFile);
Memo1->Lines->Add(Issledov.str1.c_str());
for (int k=0;k<10;k++) {Memo1->Lines->Add(AnsiString((Issledov.string_spisok_ystroistv[k]).c_str())); }

}
//---------------------------------------------------------------------------
Guest
Отправлено: 18.05.2006, 14:17


Не зарегистрирован







Грубо говоря string это указатель на массив char. Поэтому для загрузки в string (при строках переменной длины и динамическом выделении памяти) надо
- узнать какова будет длина строки
- запросить под неё память (не забыть о EOS, но это как хранятся данные)
- в классе полю присвоить этот string
- прочитать в эту память байты из файла


PS.
Чтение(запись) экземпляра класса из(в) файл более логично сделать методом класса TIssledov
Konstantine
Отправлено: 19.05.2006, 21:45


Мастер участка

Группа: Модератор
Сообщений: 545



если файл текстовый — проще использовать класс TStringList
если бинарный — то через TMemoryStream
exp
Отправлено: 26.05.2006, 22:15


Мастер участка

Группа: Участник
Сообщений: 304



Если работаешь апишными функциями — то вот для сохранения строки придется попотеть о посимвольно сохранять строку (метод SaveStr(string Str))
CODE

class TIssledov
{
public:
string string_spisok_ystroistv[10];
unsigned int qqq_n;
string str1;
unsigned int qqq_k;
//----Полезные методы--------
  void SaveStr(string FileName,string Str)
  {
        int hFile = 0;
if(!FileExists(FileName)) hFile = FileCreate(FileName);
else hFile = FileOpen(FileName,fmOpenWrite);

if(!hFile) return;
int Length = Str.size(); // насколько помню, такой метод есть
FileWrite(hFile,(char*)&Length,sizeof(Length)); // сохраняем длину строки, потому что нам еще загружать её
for(int i = 0; i < Length; i++) //Сохраняем посимвольно строку.
 FileWrite(hFile,(char*)&Temp[i+1],sizeof(char));
  }
  void LoadStr(string FileName,string &Str)
  {
       int hFile;
if(FileExists(FileName))
{
       hFile = FileOpen(FileName, fmOpenRead);
               int Length;
                FileRead(hFile,(char*)&Length,sizeof(Length)); // читаем кол-во символов
                string StrLoaded = "";
        for(int i = 0; i <Length; i++) // посимвольно читаем
        {
       FileRead(hFile,&C,sizeof(char));
       StrLoaded += C;
         }
        }
  }
// Сохранение объекта в файл
void  Save(string FileName)
{
  // мне лень писать открытие файла. сам сделаешь
  for(int i = 0; i <10; i++) SaveStr(FileName,string_spisok_ystroistv[i]);
  // Дальше мне тоже лень. дальше просто числа сохранить.  
}
};

Надеюсь, основная мысль понятна smile.gif. Ты сохранял не строку а указатель. После загрузки по адресу, который ты сохранил, совершенно не обязательно должна находиться твоя строка smile.gif. Функции FileRead и FileWrite используют параметром размер буфера чтения. Ты туда подсовываешь sizeof(TIssledov), который, кстати, не меняется в зависимости от длины строк твоего string_spisok_ystroistv[10].
Выправляй код. Удачи в отладке smile.gif.
Grigoriy
Отправлено: 27.05.2006, 00:56


Мастер участка

Группа: Участник
Сообщений: 381



QUOTE
Если работаешь апишными функциями — то вот для сохранения строки придется попотеть о посимвольно сохранять строку (метод SaveStr(string Str))


Да никакого подобного ограничения в API-функциях для работы с файлами нет.

QUOTE
string

А учёт регистра символов в идентификаторах... ?

FileWrite
FileRead
это вовсе не АПИ, а стандартные функции языка Builder.

QUOTE

CODE

if(!FileExists(FileName)) hFile = FileCreate(FileName);
else hFile = FileOpen(FileName,fmOpenWrite);



Ну, хорошо, а где же мы закрываем файл ?

QUOTE
int Length = Str.size(); // насколько помню, такой метод есть


Да нету такого метода.

QUOTE

Функции FileRead и FileWrite используют параметром размер буфера чтения. Ты туда подсовываешь sizeof(TIssledov), который, кстати, не меняется в зависимости от длины строк твоего string_spisok_ystroistv[10].

Ну и какой смысл, что он запишет в файл поля объекта TIssledov.
Строки
CODE

public
//------------
string string_spisok_ystroistv[10];
//------------
string str1;
//------------------

не сохранятся же в таком случае !

QUOTE

CODE

for(int i = 0; i <10; i++) SaveStr(FileName,string_spisok_ystroistv[i]);



Так что же делает этот цикл ?
Он просто пишет на одно и то же место в файле с именем FileName разные строки ?
Знаете почему ?
Смотрите...
Вы открываете файл. Указатель файла после открытия устанавливается в начало файла, а не в конец. И начинаем перезаписывать начало файла каждый раз снова. Мы просто затираем старую строку новой и всЁ.
Там же надо перемещать указатель файла в конец файла.

Тогда непонятен смысл функции чтения строки -
LoadStr

Функция может читать только первую строку.



Ну, да ладно.
Вот мой код.
АПИ чистые.

CODE

//********************************************************
int SaveStr(AnsiString FileName,AnsiString Str)
{
unsigned long* NumberOfBytesWritten=new unsigned long;
void* hFile = 0;
void* p;
int* L=new int;
if (
!(hFile=CreateFile(FileName.c_str(),GENERIC_WRITE,0,0,OPEN_ALWAYS,FILE_ATTRIBUTE_NORMAL,0)))
return -1;
SetFilePointer(hFile,0,0,FILE_END);
p=Str.c_str();
*L=Str.Length();
WriteFile(hFile,L,4,NumberOfBytesWritten,0);
WriteFile(hFile,p,*L,NumberOfBytesWritten,0);
CloseHandle(hFile);
delete L;
delete NumberOfBytesWritten;
return 0;
};
//********************************************************
int LoadStr(AnsiString FileName,AnsiString &Str)
{
unsigned long* lpNumberOfBytesRead=new unsigned long;
void* hFile = 0;
void* p;
int* L=new int;
if (
!(hFile=CreateFile(FileName.c_str(),GENERIC_READ,0,0,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0)))
return -1;
ReadFile(hFile,L,4,lpNumberOfBytesRead,0);
Str.SetLength(*L);
p=Str.c_str();
ReadFile(hFile,p,*L,lpNumberOfBytesRead,0);
CloseHandle(hFile);
if (*lpNumberOfBytesRead != *L) return -1;
delete L;
delete lpNumberOfBytesRead;
return 0;
};
//********************************************************


И пример его использования

CODE

void __fastcall TForm1::Button1Click(TObject *Sender)
{
AnsiString ffr;
ffr=Edit1->Text;
SaveStr("C:\\fffdsdcsdsdf.fwr",ffr);
}
//---------------------------------------------------------------------------

void __fastcall TForm1::Button2Click(TObject *Sender)
{
AnsiString ffr1;
LoadStr("C:\\fffdsdcsdsdf.fwr",ffr1);
Edit2->Text=ffr1;
}



Но у меня тоже LoadStr читает только первую строку.
Мне лень самому ...

Отредактировано Grigoriy — 27/05/2006, 01:31
** exp
Отправлено: 27.05.2006, 16:02


Не зарегистрирован







Какой блистательный разнос ты мне устроил!!! Я упал со стула и лежу в нокауте. Как оказалось, я вааааааще не знаю как работать с файлами.

Ести ты, Grigory, не заметил, то автор темы и сам знает когда надо закрывать файлы, единственное, что ему надо было — строки сохранять. Вот я и написал ему, идею, как сохранить строку, но твой код, конечно, круче! Я даже уверен, что k_serg даже им воспользуется. Ведь ты файлы закрываешь, в отличие от меня.

QUOTE

Ну и какой смысл, что он запишет в файл поля объекта TIssledov.
Строки

CODE  

public
//------------
string string_spisok_ystroistv[10];
//------------
string str1;
//------------------


не сохранятся же в таком случае !

Гениально! ты понял, что я хотел сказать! Чудеса проницательности!

QUOTE

Он просто пишет на одно и то же место в файле с именем FileName разные строки ?
Знаете почему ?
Смотрите...
Вы открываете файл. Указатель файла после открытия устанавливается в начало файла, а не в конец. И начинаем перезаписывать начало файла каждый раз снова. Мы просто затираем старую строку новой и всЁ.
Там же надо перемещать указатель файла в конец файла.

Да ты что!!!!? А я-то думал.....

Знаешь, я пришёл с работы оооочень уставший. И мне не хотелось выковыривать всех жуков из того кода, который я написал. Ты, смотрю, тоже под конец задолбался, раз самому лень.

Ну а за АПИ, ты уж прости меня. Я заблуждался.
Grigoriy
Отправлено: 27.05.2006, 16:28


Мастер участка

Группа: Участник
Сообщений: 381



biggrin.gif biggrin.gif biggrin.gif

QUOTE
Я упал со стула и лежу в нокауте.


Ну-ну.
Только падать не надо. Могут быть плохие последствия, а хорошего ничего...

QUOTE
Как оказалось, я вааааааще не знаю как работать с файлами.


Ну никто из нас с рождения не знает как работать с файлами.

QUOTE
Я даже уверен, что k_serg даже им воспользуется.


И более того — усовершенствует...

Mapcuk
  Отправлено: 09.06.2006, 09:29


Не зарегистрирован







#include // она получше будет причём вкючает и iostream
void main()
{...
string IFile,OFile;
cout<<"Vvedite imya vhodnogo i vyhodnogo failov: ";
cin>>IFile>>OFile; // имена файлов

ifstream infile ( IFile.c_str() ); создаём объект откуда будем читать
ofstream outfile ( OFile.c_str() );

if ( ! outfile ) // false, если файл не открыт
cout << "Oshibka otkrytiya faila-vyvoda.\n";
if ( ! infile ) // false, если файл не открыт
cout << "Oshibka otkrytiya faila-vvoda.\n";

string buffer;

while ( infile>> buffer ) // читать из файла по словам по не кончаться wink.gif
cout << buffer << "\n" ; // причём считывает слово до пробела табуляции
}
или конца строки и конца файла
тока string и AnsiString не особо совместимы
Mapcuk
Отправлено: 09.06.2006, 09:31


Не зарегистрирован







в начале забыл указать
#include
Shagg
Отправлено: 09.06.2006, 12:42


Дежурный стрелочник

Группа: Участник
Сообщений: 69



cool.gif тож мне апишники млин... если помните d С++ определены операции смещения "<<" и ">>", которые перегружены для потоковых классов(istream,ostream, fstream...) как операции чтения/записи.
То бишь их можно использовать так:
CODE

#include <fstream>
#include <string>
using namespace std;

void __fastcall TForm1::Button1Click(TObject *Sender)
{
 ofstream f("c:\t.txt", ios::out);
 string s;
 s = "adsadasd";
 f<<s;
 f.close();
}
//---------------------------------------------------------------------------

А еще, кстати, существуют манипуляторы, используя которые можно выводить отформатированный текст. Для того чтоб использовать их нуна подключить файл iomanip...
хотя нет API это круто,надо на апи писать — дядя Билли так сказал. biggrin.gif
Gedeon
Отправлено: 13.06.2006, 11:42


Ветеран

Группа: Модератор
Сообщений: 1742



QUOTE (Shagg @ 09/06/2006, 12:42)
хотя нет API это круто,надо на апи писать — дядя Билли так сказал. biggrin.gif

Смотря что и под что писать!
Shagg
Отправлено: 13.06.2006, 14:08


Дежурный стрелочник

Группа: Участник
Сообщений: 69



QUOTE (Gedeon @ 13/06/2006, 11:42)
Смотря что и под что писать!

Я не спорю, но, по моему, для того, чтобы записать пару строк в файл не обязательно создавать проекцию файла и ловить системные привелегии.
под что писать? встроенные функции с++ работают на большинстве поатформ, а апи только под окнами biggrin.gif

Вернуться в Вопросы программирования в C++Builder