 |
JLI Spieleprogrammierung
|
Vorheriges Thema anzeigen :: Nächstes Thema anzeigen |
Autor |
Nachricht |
valentin_ Mini JLI'ler

Alter: 34 Anmeldedatum: 16.07.2006 Beiträge: 28 Wohnort: Graz Medaillen: Keine
|
Verfasst am: 01.03.2007, 15:31 Titel: Problem mit STL-Strings |
|
|
Hi.
Ich hab hier ein recht simples Programm, wo ich einfach nicht verstehe, weshalb es nicht so funktioniert, wie ich es will!!
Soll ein Adressbuch, oder soetwas in der Art werden.
Hier erst mal der Code:
CPP: | #include <string>
#include <iostream>
#include <stdio.h>
using namespace std;
struct Eintrag
{
int Nummer;
string Vorname;
string Nachname;
};
void main(void)
{
Eintrag Buffer;
FILE *MainDatei = NULL;
int Menu = 0;
puts(" Adressbuch\n");
printf("\n 1 ... Neuer Kontakt");
printf("\n 2 ... Kontakte Anzeigen");
printf("\n Waehle Aktion: ");
cin >> Menu;
switch(Menu)
{
case 1:
MainDatei = fopen("d:\\Kontakt.bin","ab");
printf("\n Nummer: ");
cin >> Buffer.Nummer;
getchar();
printf(" Vorname: ");
getline(cin,Buffer.Vorname);
getchar();
printf(" Nachname: ");
getline(cin,Buffer.Nachname);
getchar();
printf("\n Neuer Kontakt: Nr.%d Name: %s %s ",Buffer.Nummer,Buffer.Vorname.c_str(),Buffer.Nachname.c_str());
fwrite(&Buffer,sizeof(Buffer),1,MainDatei);
fclose(MainDatei);
getchar();
break;
case 2:
MainDatei = fopen("d:\\Kontakt.bin","rb");
fread(&Buffer,sizeof(Buffer),1,MainDatei);
printf("\n Kontakt: Nr.%d Name: %s %s ",Buffer.Nummer,Buffer.Vorname.c_str(),Buffer.Nachname.c_str());
fclose(MainDatei);
getchar();
break;
}
printf("\n\n Beenden mit <RET>\n");
getchar();
}
|
Also.
Wenn ich die Daten einles funktioniert ja alles noch, aber wenn ich sie aus der Datei wieder auslesen möchte, werden anstelle der strings irgendwelche Zeichen ausgegeben. Weiß, aber nicht wieso?
Weiters würde ich gern wissen, ob man irgendwie in erfahrung bringen kann, wie viele Elemente eine Datei enthält. Denn ich weiß ja nicht, wie viele Kontakte in der Datei bereits vorhanden sind. Bei Textdatein lässt sich das mit while(!feof(Datei)) sehr elegant lösen. Was gibt es da für möglichkeiten bei Binärdateien??
Und als letztes hab ich noch ne Frage zu getline(). Zwar funktioniert es bei mir mit getchar() nach jeder eingabe, aber das ist etwas nervig. Fällt euch da etwas besseres ein?
Ich dank euch schon mal im vorraus für eure hilfreichen Antworten!!
valentin_ |
|
Nach oben |
|
 |
GreveN JLI Master

Alter: 38 Anmeldedatum: 08.01.2004 Beiträge: 901 Wohnort: Sachsen - Dresden Medaillen: Keine
|
Verfasst am: 01.03.2007, 16:01 Titel: |
|
|
Naja, dein Code ist etwas ulkig, du mixt munter alte C-Konstrukte wie FILE, fopen etc. mit neueren C++-Konstrukten. Ich denke du wirst sicher versehentlich die selbe Datei einmal als Textdatei und einmal als Binärdatei behandeln, daher werden deine Daten unterschiedlich interpretiert und der Murks mit den Zeichen entsteht, hab grade auch nicht die Lust mich da durch zufitzen. Am besten du haust den ganzen alten Mist von wegen fopen, fclose usw. raus, benutzt Streams, und streamst deine Einträge direkt in die Datei, geht easy, sauber und stabil mit wenigen Zeilen. ;)
Zuletzt bearbeitet von GreveN am 01.03.2007, 16:05, insgesamt einmal bearbeitet |
|
Nach oben |
|
 |
Christian Rousselle Site Admin

Alter: 48 Anmeldedatum: 19.07.2002 Beiträge: 1630
Medaillen: Keine
|
Verfasst am: 01.03.2007, 16:05 Titel: |
|
|
Das wird so nicht funktionieren, weil die Größe von string nicht das ist, was du meinst. Bei sizeof(std::string) bekommst du 32 - das ist die Größe der Daten, die eine String-Klasse nutzt, nicht jedoch die länge eines Strings. |
|
Nach oben |
|
 |
GreveN JLI Master

Alter: 38 Anmeldedatum: 08.01.2004 Beiträge: 901 Wohnort: Sachsen - Dresden Medaillen: Keine
|
Verfasst am: 01.03.2007, 16:21 Titel: |
|
|
Ich hab mal eben ein kleines Beispiel zusammen geschustert, müsste so funktionieren. ;)
Übrigens, du kannst natürlich auch direkt Daten von der Konsole in eine Datei streamen und umgekehrt, überhaupt lassen sich da noch jede Menge andere nette Dinge anstellen, aber ich hab's bewusst einfach gehalten... ;)
CPP: | #include <iostream>
#include <string>
#include <fstream>
struct eintrag
{
unsigned int nummer;
std::string vorname;
std::string nachname;
};
int main()
{
eintrag buf;
std::cin >> buf.nummer;
std::cin >> buf.vorname;
std::cin >> buf.nachname;
std::fstream out("test.dat", std::ios::binary | std::ios::out);
out << buf.nummer;
out << buf.vorname;
out << std::endl;
out << buf.nachname;
out.close();
std::fstream in("test.dat", std::ios::binary | std::ios::in);
eintrag buf2;
in >> buf2.nummer;
in >> buf2.vorname;
in >> buf2.nachname;
std::cout << buf2.nummer << std::endl;
std::cout << buf2.vorname << std::endl;
std::cout << buf2.nachname << std::endl;
return 0;
}
|
|
|
Nach oben |
|
 |
valentin_ Mini JLI'ler

Alter: 34 Anmeldedatum: 16.07.2006 Beiträge: 28 Wohnort: Graz Medaillen: Keine
|
Verfasst am: 01.03.2007, 16:48 Titel: |
|
|
Danke erstmal.
Der Mix der verschiedenen Konstrukte entsteht halt, wenn man sich durch verschiedenste Interner-Tuts ackert.
Hab zwar noch nie mit Streams gearbeitet, aber das werd ich schon hinbekommen!
valentin_ |
|
Nach oben |
|
 |
Jonathan_Klein Living Legend

Alter: 37 Anmeldedatum: 17.02.2003 Beiträge: 3433 Wohnort: Siegerland Medaillen: Keine
|
Verfasst am: 01.03.2007, 17:47 Titel: |
|
|
Von wegen der Anzahl der Einträge: Schreib das einfach mit in die Datei.
Zum Beispiel könntest du alle Einträge in einem Array, einer Liste oder sonstwas speichern. Auf jeden Fall wirst du immer im Programm die Länge dieses Konstruktes erfahren können. Diese schreibst du einfach als Integer (kommt halt auf den maximal angepeilten Wert an, wie groß die Variable für die Datei sein muss, aber 32bit brauchen nicht viel Platz in der Datei und es passen schon sehr große Zahlen rein) an den Anfang der Datei.
Beim Laden lädst du erst diese Größe, und kannst dann in einer einfachen for-Schleife alle Einträge auslesen.
Natürlich geht auch in Binärdateien "eof" (müsste es ja bei jeglicher Art von Dateileseoperationen geben), aber geschickter ist halt, wenn das in der Datei drin steht.
Außerdem empfiehlt es sich eine Versionsnummer in die Datei zu schreiben. Wenn du das Dateiformat irgendwann mal ändern solltest, lässt du die alte Ladefunktion und schreibst eine neue und kannst an der Nummer gucken welche du nehemn muss (mit switch oder so). _________________ https://jonathank.de/games/ |
|
Nach oben |
|
 |
valentin_ Mini JLI'ler

Alter: 34 Anmeldedatum: 16.07.2006 Beiträge: 28 Wohnort: Graz Medaillen: Keine
|
Verfasst am: 04.03.2007, 18:29 Titel: |
|
|
Ich schreibe jetzt die Anzahl der Einträge ans Anfang meiner Datei.
Nur weiß ich jetzt nicht, wie ich die Anzahl ändern soll, wenn eich einen neuen Eintrag mache.
Oder wenn ich einen Eintrag lösche möchte ich ja die jenige Zeile löschen, aber auch das gelingt mir nicht.
Mit Datei.seekp(...) kann man sich zwar bewegen, aber ich weiß nicht wie man Daten manipulieren kann.
valentin_ |
|
Nach oben |
|
 |
DirectXer Dark JLI'ler

Anmeldedatum: 05.02.2005 Beiträge: 1201 Wohnort: Köln Medaillen: Keine
|
Verfasst am: 04.03.2007, 18:40 Titel: |
|
|
an den Anfang der Datei kannst du den Stream mit s.seekp( 0, std::ios::beg ) setzen. Das klappt immer, und ist im Gegensatz zu s.seekp( 0 ) auch offset-sicher. Danach einfach ganz normal mit operator << oder s.put() o.Ä. weiter schreiben. Die Daten, die vorher an dieser Stelle standen, werden überschrieben. Beim Löschen also einfach auf deine Zeile gehen und mit einer neuen oder dem Dateiende etc. überschreiben-.
btw: du kannst mit seekp (auch bei seekg) noch andere Ausgangspositionen als 2. parameter angeben wie std::ios::end fürs Dateiende oder std::ios::cur(um vom jetzigen Standort aus zu verschieben) oder eben std::ios::beg für den Beginn. Der 1. Parameter gibt dazu den relativen abstand an. Um bspw. den Stream 5 Zeichen zurück zu bewegen, müsstest du seekp( -5, std::ios::cur ) machen.[/code]
Gruß DXer |
|
Nach oben |
|
 |
GreveN JLI Master

Alter: 38 Anmeldedatum: 08.01.2004 Beiträge: 901 Wohnort: Sachsen - Dresden Medaillen: Keine
|
Verfasst am: 04.03.2007, 18:43 Titel: |
|
|
Öh, wenn du es wirklich so machen willst, dann einfach die Daten überschreiben. Schreibzeiger auf das Offset setzen und einen neuen Wert eintragen.
Einfach mitten in der (Binär)Datei etwas löschen geht nicht so einfach (zumindest kenn ich keine Methode), im Prinzip musst du die Daten, die hinter dem betroffenen Abschnitt liegen neu an das Offset des gelöschten Absatzes schreiben - ich hoffe ich laber hier nicht totalen Mist. ;) Also quasi den hinteren Teil vorrücken an das Ende des ersten Teils, aber dafür sind die überladenen Stream-Operatoren nicht unbedingt gedacht, in dem Fall bieten sich die write- und read-Methode des Streams eher an. Schön geht in den meisten Fällen auch das Arbeiten auf dem internen stream_buf-Objekt, was die eigentlichen Daten hält.
Achja, sehr smart gehen solche Operationen auch mit den Stream-Iteratoren und den Algorithmen aus der STL.
Am besten du durchstöberst mal die gängigen Referenzen, die Stream- und Pufferklassen sind eigentlich alle ziemlich intuitiv aufgebaut und ein tiefergehendes Verständnis schadet bekanntlich eigentlich ja nie... ;)
http://www.cplusplus.com/reference/iostream/ |
|
Nach oben |
|
 |
Jonathan_Klein Living Legend

Alter: 37 Anmeldedatum: 17.02.2003 Beiträge: 3433 Wohnort: Siegerland Medaillen: Keine
|
Verfasst am: 04.03.2007, 20:25 Titel: |
|
|
Ähm. Ich mache das immer so, dass ich die komplette Datei in den Hauptspeicher lade. Da kann ich dann mit den Daten machen was ich möchte. Zum speichern überschreibe ich einfach die gesamte Datei.
Das geht sehr viel einfacher, als bei jeder Veränderung die passende Stelle in der Datei zu finden und zu ersetzen.
Das einzige was IMHO dagegen spricht die Datei einmal komplett zu laden und dann einmal wieder komplett zu überschreiben wäre, wenn diese sehr sehr groß ist. So 500 mb oder so, halt dass es mit der Hauptspeichergröße Probleme geben könnte. _________________ https://jonathank.de/games/ |
|
Nach oben |
|
 |
GreveN JLI Master

Alter: 38 Anmeldedatum: 08.01.2004 Beiträge: 901 Wohnort: Sachsen - Dresden Medaillen: Keine
|
Verfasst am: 04.03.2007, 20:59 Titel: |
|
|
Ja, im Prinzip funktioniert es ja auch mit den Streams von Haus aus so. Wenn du bestimmte Bereiche zum "Löschen" überschreibst, arbeitest du im Normalfall ja auch immer auf irgendwelchen Puffern - wie eben z.B. den filebuf-Objekten. Ich hatte ja schonmal angedeutet, dass man schön mit den streambuf- bzw. filebuf-Objekten arbeiten kann... die halten im Prinzip auch den kompletten Inhalt der Datei bzw. des Ein-/Ausgabe-Objektes, welches dem Stream zugrunde liegt und werden nur hin und wieder mit dem jeweiligen "physischen Ende" des Stroms, also der eigentlichen Datei/Konsole/... synchronisiert... std::endl macht das zum Beispiel, einfach weil das ganze Drumherum um das Speichern/Anzeigen/... das Ganze sonst zu unperformant machen würde. |
|
Nach oben |
|
 |
|
|
Du kannst keine Beiträge in dieses Forum schreiben. Du kannst auf Beiträge in diesem Forum nicht antworten. Du kannst deine Beiträge in diesem Forum nicht bearbeiten. Du kannst deine Beiträge in diesem Forum nicht löschen. Du kannst an Umfragen in diesem Forum nicht mitmachen.
|
Powered by phpBB © 2001, 2005 phpBB Group Deutsche Übersetzung von phpBB.de
|