Zapis zmiennej do pliku za pomocą fstream

dział ogólny

Zapis zmiennej do pliku za pomocą fstream

Nowy postprzez Krzysiu555 » niedziela, 25 stycznia 2009, 17:53

Kod: Zaznacz cały
String test = Label1->Caption;

ofstream outfile;
outfile.open("test.txt", ios::app);
outfile << endl << test;
outfile.close();


Chodzi mi o to żeby zapisać zawartość pola Label1 przez zmienną test do pliku, ale gdy tak zrobię wyskakuje:
[C++ Error] Unit1.cpp(237): E2094 'operator<<' not implemented in type 'ostream' for arguments of type 'AnsiString'


O co chodzi? i jak to naprawić? Proszę o pomoc.
Avatar użytkownika
Krzysiu555
Intelektryk
Intelektryk
 
Posty: 161
Dołączył(a): sobota, 23 sierpnia 2008, 16:55
Podziękował : 2
Otrzymał podziękowań: 1
System operacyjny: Windows 7 Professional 64
Kompilator: C++ Builder 6 Personal
Gadu Gadu: 0
    NieznanyNieznana

Re: Zapis zmiennej do pliku za pomocą fstream

Nowy postprzez polymorphism » niedziela, 25 stycznia 2009, 18:01

O co chodzi?

Chodzi o to, że nie ma zdefiniowanego operatora << dla AnsiStringa (tzn jest, o ile pamiętam, coś trzeba było zdefiniować żeby móc go użyć. Poszukaj w dokumentacji). Ewentualnie zapis możesz zrobić tak:
Kod: Zaznacz cały
outfile << test.c_str();
C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2156
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 200
System operacyjny: Windows 8.1
Windows 10
Linux Mint 21.1
Kompilator: Visual Studio
Visual Studio Code
MSYS2 (MinGW, clang)
g++
clang
Gadu Gadu: 0
    NieznanyNieznana

Re: Zapis zmiennej do pliku za pomocą fstream

Nowy postprzez kinio » niedziela, 25 stycznia 2009, 18:06

Witaj!

Jak wiesz w C++ działanie większości operatorów można przedefiniować.
Błąd który Ci się pokazuje mówi że nie masz zdefiniowanego operatora << dla typu AnsiString.
Istnieją dwa wyjścia:

1. Taki operator jest zdefiniowany dla typu char*, więc można go użyć np. w taki sposób:
Kod: Zaznacz cały
String test = Label1->Caption;

ofstream outfile;
outfile.open("test.txt", ios::app);
outfile << endl << test.c_str();
outfile.close();


2. Zdefiniować taki operator samemu, najlepiej nie jako metodę. Można to zrobić np. tak:
Kod: Zaznacz cały
ostream& operator<<(ostream& __stream, AnsiString const& _str)
{
     __stream << _str.c_str();
     return __stream;
}


Pozdr!
If a machine is expected to be infallible, it cannot also be intelligent.
-- A.Turing
Avatar użytkownika
kinio
Homos antropiczny
Homos antropiczny
 
Posty: 67
Dołączył(a): poniedziałek, 14 lipca 2008, 08:51
Podziękował : 0
Otrzymał podziękowań: 0
    NieznanyNieznana

Re: Zapis zmiennej do pliku za pomocą fstream

Nowy postprzez Cyfrowy Baron » niedziela, 25 stycznia 2009, 18:22

Przede wszystkim to endl na końcu, gdyż chyba ma kończyć linię, a nie zaczynać od nowej:

Kod: Zaznacz cały
ofstream outfile;
outfile.open("test.txt", ios::app);
outfile << test.c_str() << endl;
outfile.close();


► patrz serwis: Cyfrowy Baron dział: porady -> różne -> Zapisywanie danych do plików (klasa ofstream).
► patrz serwis: Cyfrowy Baron dział: porady -> różne -> Odczytywanie danych z pliku (klasa ifstream).
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    NieznanyNieznana

Re: Zapis zmiennej do pliku za pomocą fstream

Nowy postprzez polymorphism » niedziela, 25 stycznia 2009, 18:38

Cyfrowy Baron napisał(a): ► patrz serwis: Cyfrowy Baron dział: porady -> różne -> Odczytywanie danych z pliku (klasa ifstream).

Baron, ale z tym to pojechałeś:
Kod: Zaznacz cały
void __fastcall TForm1::Button1Click(TObject *Sender)
{
    char * Buf; //<--- !!!
    ...
    infile >> Buf;
    ...
}

Buf jest niezainicjowanym wskaźnikiem. Jakie konsekwencje to niesie, chyba tłumaczyć nie trzeba.

No i to popraw:
Kod: Zaznacz cały
if(((AnsiString)Buf).IsEmpty())

wprawdzie nie jest to błąd, ale jest skrajnie niewydajne. Wystarczy:
Kod: Zaznacz cały
if(Buf[0] == '\0'){ ... }


swoją drogą w C++ należy unikać na ile to możliwe tablic char do przechowywania napisów - string i tylko string.
C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2156
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 200
System operacyjny: Windows 8.1
Windows 10
Linux Mint 21.1
Kompilator: Visual Studio
Visual Studio Code
MSYS2 (MinGW, clang)
g++
clang
Gadu Gadu: 0
    NieznanyNieznana

Re: Zapis zmiennej do pliku za pomocą fstream

Nowy postprzez Cyfrowy Baron » niedziela, 25 stycznia 2009, 19:21

Drobne przeoczenie w trakcie pisania porady, ale każdy kto się zna od razu dostrzeże błąd, powinno oczywiście być:

Kod: Zaznacz cały
char * Buf = new char;

ifstream infile;
infile.open("plik.txt");
infile >> Buf;
infile.close();

Memo1->Text = Buf;
delete Buf;




No i to popraw:

Kod: Zaznacz cały
if(((AnsiString)Buf).IsEmpty())



wprawdzie nie jest to błąd, ale jest skrajnie niewydajne.


Nie ma czego poprawiać, nie jest niewydajne, gdyż funkcja IsEmpty sprawdza, czy łańcuch znaków jest pusty, czyli nie przetwarza całego łańcucha.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    NieznanyNieznana

Re: Zapis zmiennej do pliku za pomocą fstream

Nowy postprzez Krzysiu555 » niedziela, 25 stycznia 2009, 19:35

Dziękuję wszystkim za pomoc. Działa... zresztą, jak zawsze gdy odpowiada CB albo Kinio... ;) dzięki też polymorphism'owi ;)

PS CB - nie właśnie chodziło mi o to żeby zaczynał od nowej lini
Avatar użytkownika
Krzysiu555
Intelektryk
Intelektryk
 
Posty: 161
Dołączył(a): sobota, 23 sierpnia 2008, 16:55
Podziękował : 2
Otrzymał podziękowań: 1
System operacyjny: Windows 7 Professional 64
Kompilator: C++ Builder 6 Personal
Gadu Gadu: 0
    NieznanyNieznana

Re: Zapis zmiennej do pliku za pomocą fstream

Nowy postprzez polymorphism » niedziela, 25 stycznia 2009, 22:50

ale każdy kto się zna od razu dostrzeże błąd

Niby tak, ale ten tekst jest raczej dla początkujących, więc oni tego "drobnego" błędu nie dostrzegą.

powinno oczywiście być:
Kod: Zaznacz cały
char * Buf = new char;
...

Niestety nie. Alokujesz jeden bajt i przy odczycie jakiegoś wyrazu wyjedziesz poza zakres tablicy - podając operatorowi >> wskaźnik char* dajesz do zrozumienia, że chcesz czytać ze strumienia c-stringi, a nie pojedynczy znak. Nawet jakbyś wpisał jedną literę, to i tak byłby błąd, bo jeszcze zero musiałoby dojść na końcu (wszak to c-string), a tu miejsca brak :roll: Jeśli już, to tak:
Kod: Zaznacz cały
char * Buf = new char[255];

ale zaraz zaraz, po co w ogóle alokować, przecież można tak:
Kod: Zaznacz cały
char Buf[255];

co ciekawe taką deklarację zrobiłeś w następnych przykładach. I to jest poprawna forma, ale w stylu C. W C++ powinno to wyglądać tak:
Kod: Zaznacz cały
string text;
...
infile >> text;
getline(infile,text); //<--- czytanie całej linii.

Jest to wersja bezpieczna.

gdyż funkcja IsEmpty sprawdza, czy łańcuch znaków jest pusty, czyli nie przetwarza całego łańcucha.

Oczywiście, że przetwarza, bo zapis:
Kod: Zaznacz cały
((AnsiString)Buf)

to nic innego jak:
Kod: Zaznacz cały
AnsiString(Buf) //<--- wywołanie ctor'a __fastcall AnsiString::AnsiString(const char* src);

Czyli, żeby Empty zadziałało AnsiString musi zostać zainicjowany c-stringiem - czyli skonstruowany. A tu wiadomo: strlen, alokacja, kopiowanie - to trochę trwa (na pewno dłużej niż dostęp do pierwszego znaku w Buf) 8-)

Jeśli nie wierzysz, ustaw breakpointa na tym wyrażeniu, przejdź na widok CPU i zobaczysz, że pierwsza funkcja (call), która zostanie wywołana to konstruktor AnsiStringa, dopiero później będzie to Empty.
C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2156
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 200
System operacyjny: Windows 8.1
Windows 10
Linux Mint 21.1
Kompilator: Visual Studio
Visual Studio Code
MSYS2 (MinGW, clang)
g++
clang
Gadu Gadu: 0
    NieznanyNieznana

Re: Zapis zmiennej do pliku za pomocą fstream

Nowy postprzez Cyfrowy Baron » poniedziałek, 26 stycznia 2009, 00:00

char * Buf = new char;


mylisz się, to działa bez zarzutu i nie wywołuje żadnych błędów.

Przykładowy projekt:
Nie masz wystarczających uprawnień, aby zobaczyć pliki załączone do tego postu.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    NieznanyNieznana

Re: Zapis zmiennej do pliku za pomocą fstream

Nowy postprzez Cyfrowy Baron » poniedziałek, 26 stycznia 2009, 00:05

char * Buf = new char;


mylisz się, to działa bez zarzutu i nie wywołuje żadnych błędów, chociaż nie jest do końca poprawna, niemniej jednak w tym konkretnym przypadku zadziała

Przykładowy projekt:
project.rar


ale zaraz zaraz, po co w ogóle alokować, przecież można tak:


może po to że czasami nieznany jest rozmiar pliku z góry i nie wiadomo jaki rozmiar zmiennej ustawić:
Kod: Zaznacz cały
FILE* f;
if(!(f = fopen(fname.c_str(), "r")))
  return String("");

long seekOff = 0;
int fd = fileno(f), count = 0;
char digit;

do
{
  fseek(f, --seekOff, SEEK_END);
  digit = getc(f);
}
while
  (digit >= '0' && digit <= '9');

long fSize = filelength(fd);
++seekOff;
if(!seekOff || -seekOff >= fSize)
return String("");

char *txt, *res;
txt = new char[-seekOff + 1];
fread(txt, sizeof(char), -seekOff, f);
txt[-seekOff] = '\0';
sscanf(txt, "%d", &count);

if(!count)
{
  delete txt;
  return String("");
}

res = new char[count + 1];
fseek(f, seekOff - count, SEEK_END);
fread(res, sizeof(char), count, f);
res[count] = '\0';
delete txt;
fclose(f);
String resTxt(res, count);
delete res;
return resTxt;
Nie masz wystarczających uprawnień, aby zobaczyć pliki załączone do tego postu.
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    NieznanyNieznana

Re: Zapis zmiennej do pliku za pomocą fstream

Nowy postprzez polymorphism » poniedziałek, 26 stycznia 2009, 00:29

Cyfrowy Baron napisał(a):mylisz się, to działa bez zarzutu i nie wywołuje żadnych błędów.

Mylić to się nie mylę. Dla przykładu podam ci komunikat, jaki VC zwraca przy wywołaniu delete:

    Unhandled exception at 0x77f767cd in test_console.exe: User breakpoint.
Oczywiście sam komunikat niewiele mówi, ale fakt, że wyskakuje przy wywołaniu delete świadczy, że sterta jest uszkodzona. Uszkodzona tym, że za dużo zapisałem do pamięci. Nie dziwi mnie to, że pod BCB działa. VC w trybie debug robi "logi" wszystkich alokacji poprzez new/malloc/calloc, sprawdza stos itd. Zapisując poza zakresem pamięci niszczę jakąś wewnętrzną strukturę opisującą blok pamięci, która jest wykorzystywana przez VC do wykrywania między innymi wycieków (normalnie powinien dać mi info, że pisałem poza dozwolonym obszarem pamięci, widocznie przesadziłem 8-) ). Stąd to natychmiastowe wykrycie błędu. Z kolei BCB nie miał wbudowanej takiej diagnostyki (BCB5 na pewno nie), dlatego program nie wysypał się - co nie znaczy, że wszystko jest ok, bo nie jest. Mógłbyś się zastanawiać jakim cudem nie wysypał się, jeśli pisałeś po niedostępnej pamięci. Odpowiedź jest w sumie prosta, wystarczy zapoznać się z budową i zasadą działania sterty.

może po to że czasami nieznany jest rozmiar pliku z góry i nie wiadomo jaki rozmiar zmiennej ustawić:

Tak, ale to, co napisałem było w kontekście tego, co sam napisałeś, czyli:
Kod: Zaznacz cały
char * Buf = new char;


That's All...
C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2156
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 200
System operacyjny: Windows 8.1
Windows 10
Linux Mint 21.1
Kompilator: Visual Studio
Visual Studio Code
MSYS2 (MinGW, clang)
g++
clang
Gadu Gadu: 0
    NieznanyNieznana

Re: Zapis zmiennej do pliku za pomocą fstream

Nowy postprzez Cyfrowy Baron » poniedziałek, 26 stycznia 2009, 10:29

No i pięknie to wszystkim wyjaśniłeś, sam bym tego lepiej nie zrobi. Od strony teoretycznej jesteś raczej mocny, tylko po co ta nerwowość. Krzycząc nie sprawisz, że inni lepiej Ciebie usłyszą.
Masz absolutną rację co do tego że zmienna typu char musi mieć zdefiniowany z góry rozmiar. Użycie operatora new ma jednak częste zastosowanie tam, gdzie rozmiar tej zmiennej nie jest definiowany jawnie, lecz jest przechowywany np. w innej zmiennej.
Kod: Zaznacz cały
int sizeBuf = 255;
char *Buf = new char[sizeBuf];


podczas gdy nie da się tego zrobić tak:

zapis niedopuszczalny
Kod: Zaznacz cały
int sizeBuf = 255;
char Buf[sizeBuf];
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    NieznanyNieznana

Re: Zapis zmiennej do pliku za pomocą fstream

Nowy postprzez polymorphism » poniedziałek, 26 stycznia 2009, 11:48

tylko po co ta nerwowość. Krzycząc nie sprawisz, że inni lepiej Ciebie usłyszą.

E tam, żadna nerwowość. Zwróciłem Ci uwagę, że masz błąd w artykule. A że upierałeś się, że masz dobrze, no to próbowałem udowodnić, że jednak tak nie jest. Zresztą co jak co, ale tobie powinno zależeć na tym, żeby w serwisie były podane rozwiązania poprawne, więc to moje małe wytknięcie błędu jest z korzyścią dla serwisu.

Użycie operatora new ma jednak częste zastosowanie tam, gdzie rozmiar tej zmiennej nie jest definiowany jawnie, lecz jest przechowywany np. w innej zmiennej.

Nie, no przecież tego nie neguje. W końcu po to jest ten rodzaj przydziału pamięci. Ale złą praktyką jest nadużywanie go wszędzie tam, gdzie nie jest to konieczne. W C++ zamiast ręcznego zarządzania pamięcią lepiej skorzystać z STL'a, a dokładniej z vectora w przypadku wszelkiego rodzaju buforów lub stringa, jeśli mamy do czynienia z tekstem.
C++ Reference - opis wszystkich klas STL-a i funkcji C.
Avatar użytkownika
polymorphism
Doświadczony Programista ● Moderator
Doświadczony Programista ● Moderator
 
Posty: 2156
Dołączył(a): piątek, 19 grudnia 2008, 13:04
Podziękował : 0
Otrzymał podziękowań: 200
System operacyjny: Windows 8.1
Windows 10
Linux Mint 21.1
Kompilator: Visual Studio
Visual Studio Code
MSYS2 (MinGW, clang)
g++
clang
Gadu Gadu: 0
    NieznanyNieznana

Re: Zapis zmiennej do pliku za pomocą fstream

Nowy postprzez Cyfrowy Baron » poniedziałek, 26 stycznia 2009, 12:36

Ale złą praktyką jest nadużywanie go wszędzie tam, gdzie nie jest to konieczne.

Użycie operatora new dla zmiennej char zaprezentowałem w poradzie, a nie w odpowiedzi na post, gdyż w poradach staram się w możliwie najprostszy sposób zaprezentować jak najwięcej dostępnych rozwiązań i o ile w przytoczonym (w poradzie) przykładzie użycie operatora new nie ma absolutnie żadnego uzasadnienia i można się obejść bez niego, to zaprezentować jedną z możliwości. Błędy w poradach powtarzają się sporadycznie, gdyż piszę je w wolnych chwilach, a czasu często mi brak, więc mimo iż staram się przekazać jak najwięcej, to nie da się tego uniknąć. Poza tym skupiam się raczej nad praktyczną stroną programowania i stronę teoretyczną często traktuję po macoszemu. Każdą poradę testuję zanim ją umieszczę w serwisie, jednakże często wersja finalna umieszczana w serwisie różnie się od kodu, który testowałem. Prawdopodobnie z tą błędną poradą było podobnie, gdyż często definiuję zmienne globalne wykorzystywane dla różnych zadań, ale gdy piszę poradę zmieniam je - nie w kodzie programu - lecz w poradzie na zmienne lokalne, w ten sposób prawdopodobnie pominąłem konieczność zdefiniowania zmiennej char w poradzie mimo iż w programie testowym dokonałem tej operacji.
Dzięki temu, ze rozgorzała dyskusja na temat konieczności definiowania zmiennej, osoby które przeczytają te posty będą miały większe pogląd na problem, tylko, że mi nie chce się opisywać problemów od strony teoretycznej, wiec cieszę się, że inni to robią za mnie...
Avatar użytkownika
Cyfrowy Baron
Administrator
Administrator
 
Posty: 4716
Dołączył(a): niedziela, 13 lipca 2008, 15:17
Podziękował : 12
Otrzymał podziękowań: 442
System operacyjny: Windows 7 x64 SP1
Kompilator: Embarcadero RAD Studio XE2
C++ Builder XE2 Update 4
SKYPE: cyfbar
Gadu Gadu: 0
    NieznanyNieznana


  • Podobne tematy
    Odpowiedzi
    Wyświetlone
    Ostatni post

Powrót do Ogólne problemy z programowaniem

Kto przegląda forum

Użytkownicy przeglądający ten dział: Brak zalogowanych użytkowników i 2 gości

cron