Auslesen einer Textdatei
-
Der bessere Weg wäre natürlich, dem struct gameData nen operator >> zu spendieren... Dann kann das ja einfach so einlesen:
for (gameData tmp; stream >> tmp; ) { dein_vector.push_back (tmp); }gibt nen tollen Artikel zum Thema operatoren-Überladung hier - wenn du das allerdings noch nie gehört hast, dann wird das wohl nicht reichen...
bb
-
unskilled schrieb:
Der bessere Weg wäre natürlich, dem struct gameData nen operator >> zu spendieren... Dann kann das ja einfach so einlesen:
for (gameData tmp; stream >> tmp; ) { dein_vector.push_back (tmp); }gibt nen tollen Artikel zum Thema operatoren-Überladung hier - wenn du das allerdings noch nie gehört hast, dann wird das wohl nicht reichen...
bb
Der operator muss aber auch programmiert werden - und wenn die ganze Prozedur nicht mehrmals im Programm gemacht werden muss, ist das sogar imho unnoetig.
Gruss
Cartman
-
Eric Cartman schrieb:
unskilled schrieb:
Der bessere Weg wäre natürlich, dem struct gameData nen operator >> zu spendieren... Dann kann das ja einfach so einlesen:
for (gameData tmp; stream >> tmp; ) { dein_vector.push_back (tmp); }gibt nen tollen Artikel zum Thema operatoren-Überladung hier - wenn du das allerdings noch nie gehört hast, dann wird das wohl nicht reichen...
bb
Der operator muss aber auch programmiert werden - und wenn die ganze Prozedur nicht mehrmals im Programm gemacht werden muss, ist das sogar imho unnoetig.
Gruss
CartmanKlar, muss man den auch implementieren - aber das ist dann immer der Teil, wo ich erst mal wissen wöllte, ob ihm das was nutzt oder er das gar net versteht - zum Code c&p-fertig machen bin ich nich der richtige...
Nein, unnötig ist es niemals... Auch, wenns nur ein einziges mal vorkommt - genau das is der Sinn der op-Überladung: Übersichtlichkeit UND die einfacheree Handhabung, wenn man den Code ma ändern möchte...bb
-
Was hier auch gehen würde, übersichtlich und einfach wäre wäre noch sscanf wenn man die Zeile schonmal in nem Puffer hat kann man damit auch die zeile einfach zerlegen und fertig. So kompliziert dafür erst herum zu lesen und dann zuzuweisen würde ich nicht arbeiten.
int main() { using namespace std; char gameline[256]; gameData gameInfos; ifstream infile("gamedata.txt"); if(!infile.is_open()) { cout << "Fehler aufgetreten\n"; return 0; } while(getline(gameline,256)) { cout << gameline << "\n"; sscanf("%d,%s,%d,%d", gameInfos.m_gameId,gameInfos.m_gameName, &gameInfos.m_gameAltId,&gameInfos.m_gameLevel) m_gameVec.push_back(gameInfos); ZeroMemory(gameline,256); } return 0; }Dieses Beispiel ist kurz und übersichtlich. Einziger Haken man muß die zeile als char* einlesen, deswegen auch das Char array und wennd er Name als String vorliegen soll muß man die Char* die sscanf liefert noch in String umwandeln. Es wäre aber eine recht einfache Lösung.
-
Hallo
Ich habe es dir mal schwind mit std::ifstream geschrieben. Den "m_gameName" musste eventuell nochmal überarbeiten, weil der vielleicht falsch eingelesen werden könnte. Ansonsten bekommste den richtigen Code mit dem Iteratordurchlauf später, hab noch zu tun.
Code:
#include <iostream> #include <fstream> #include <string.h> #include <vector> #include <sstream> #include <conio.h> int convertToInt(std::string nStr) { return ::atoi(nStr.c_str()); } std::string const getWord(std::string Line, int Pos) { std::vector<std::string> Vec; std::stringstream ss(Line); std::string Word, a; while(ss >> Word) Vec.push_back(Word); return (Pos+1 > Vec.size() ? "" : Vec[Pos] ); } #pragma pack(1); struct Data { Data(): m_gameId (0), m_gameAltId (0), m_gameLevel (0) {} int m_gameId; std::string m_gameName; int m_gameAltId; int m_gameLevel; }; #pragma pack(); int main(void) { std::ifstream inFile("Test.txt", std::ios::in); if(inFile.fail()) std::cout<<"Tja, war wohl nix..."<<std::endl; std::string Line; size_t find; find = Line.find(","); std::vector<Data> DataObjects; std::vector<Data>::const_iterator iData; while(!inFile.eof()) { Data data; getline(inFile, Line); data.m_gameId = convertToInt(getWord(Line, 0)); Line.replace(Line.find(","), 1, " "); data.m_gameName = getWord(Line, 1); Line.replace(Line.find(","), 1, " "); data.m_gameAltId = convertToInt(getWord(Line, 2)); Line.replace(Line.find(","), 1, " "); data.m_gameLevel = convertToInt(getWord(Line, 3)); DataObjects.push_back(data); }; getch(); }(Ungetestet!)
Mfg.
way
-
Hi,
Werde ich mir durchlesen, danke. Danke alle, habs so gelöst, falls irgendwas "schlimm" ist, bitte sagen.
GameSetup.h
#include <iostream> #include <string> #include <fstream> #include <vector> #include <cstdlib> struct gameData { int m_gameId; std::string m_gameName; int m_gameAltId; int m_gameLevel; }; class GameSetup { public: GameSetup(); ~GameSetup(); void outputGame(); bool loadGame(); private: std::vector<gameData> m_gameVec; gameData data_; }; std::vector<std::string> TokenizeString(const std::string& str, const std::string& delim);GameSetup.cpp
#include "GameSetup.h" GameSetup::GameSetup() { data_.m_gameAltId = 0; data_.m_gameId = 0; data_.m_gameLevel = 0; data_.m_gameName = ""; } GameSetup::~GameSetup() { m_gameVec.clear(); } void GameSetup::outputGame() { using namespace std; std::vector<gameData>::const_iterator Itr; for(Itr = m_gameVec.begin(); Itr != m_gameVec.end(); Itr++ ) { cout << Itr->m_gameId << "," << Itr->m_gameName << "," << Itr->m_gameAltId << "," << Itr->m_gameLevel << "," << "\n"; } } bool GameSetup::loadGame() { using namespace std; string gameline; gameData gameInfos; std::vector<std::string> args; ifstream infile("gamedata.txt"); if(!infile.is_open()) { cout << "Error\n"; return false; } while(getline(infile, gameline)) { args = TokenizeString(gameline, ","); string args0 = args[0]; string args2 = args[2]; string args3 = args[3]; int id = atoi(args0.c_str()); int altid = atoi(args2.c_str()); int level = atoi(args3.c_str()); gameInfos.m_gameId = id; gameInfos.m_gameName = args[1]; gameInfos.m_gameAltId = altid; gameInfos.m_gameLevel = level; m_gameVec.push_back(gameInfos); } return true; } std::vector<std::string> TokenizeString(const std::string& str, const std::string& delim) { // http://www.gamedev.net/community/forums/topic.asp?topic_id=381544 using namespace std; vector<string> tokens; size_t p0 = 0, p1 = string::npos; while(p0 != string::npos) { p1 = str.find_first_of(delim, p0); if(p1 != p0) { string token = str.substr(p0, p1 - p0); tokens.push_back(token); } p0 = str.find_first_not_of(delim, p1); } return tokens; }main.cpp
#include "GameSetup.h" int main() { using namespace std; GameSetup * game_ = new GameSetup(); if(!game_->loadGame()) { cout << "::LoadGame Failed\n"; } game_->outputGame(); delete game_; return 0; }Ne frage noch, sollte ich eig die strings wie z.B "args0, args2, args3" In dem loop deklarieren oder bevor dem loop? Oder gibt es da keine großen unterschiede?
Grüße
-
way schrieb:
...
Ich wollte ja erst nur nen paar Teile korrigieren, aber war ja.... naja - nicht so dolle iwie...
Beispiele#include <string.h> //<string> int convertToInt(const std::string &nStr) //klassen werden nicht per Value übergeben, wenn man keine Kopie braucht... #pragma pack(1); //';' ist unnötig - und der Sinn hinter dem pack hier, erschließt sich mir auch nicht ganz... int main() //void schreibt man in C++ nicht mehr in die Parameterliste find = Line.find_first_of(','); //statt find (",") std::vector<Data>::const_iterator iData; //brauchst du _nie_gibt noch mehr, aber egal - hier meine Lösung:
#include <iostream> #include <fstream> #include <string> #include <vector> #include <conio.h> struct Data { Data () : m_gameId (0), m_gameAltId (0), m_gameLevel (0) {} int m_gameId; std::string m_gameName; int m_gameAltId; int m_gameLevel; }; template< char C > std::istream& Char( std::istream& in ) { char c; if( in >> c && c != C ) in.setstate( std::ios_base::failbit ); return in; } std::istream& operator >> (std::istream& s, Data &val) { s >> m_gameId >> Char<','> >> m_gameName >> Char<','> >> m_gameAltId >> Char<','> >> m_gameLevel; return s; } std::ostream& operator << (std::ostream& s, const Data &val) { s << m_gameId << ',' << m_gameName << ',' << m_gameAltId << ',' << m_gameLevel; return s; } int main() { std::ifstream inFile("Test.txt", std::ios::in); if(inFile.fail()) std::cout<<"Tja, war wohl nix..."<<std::endl; std::vector<Data> DataObjects; for (Data tmp; inFile >> tmp; inFile >> Char<'\n'>) DataObjects.push_back (tmp); getch(); }ist auch ungetestet
bname schrieb:
class GameSetup { public: GameSetup(); ~GameSetup(); //unnötig void outputGame(); //würd ich nich als fkt machen... bool loadGame(); //das auch nicht, falls aber doch, würd ichs "void LoadGames()" nennen private: typedef std::vector<gameData> TgameVec; //ich mag so ne typedefs - ist einfach leichter umzustellen und weniger zu tippen TgameVec m_gameVec; gameData data_; //brauchst du wofür? };Ich würde so einrücken...
bname schrieb:
int main() { using namespace std; GameSetup game; if(game.loadGame()) { cout << "::LoadGame Failed\n"; } game.outputGame(); }So würde die main-fkt bei mir aussehen

bname schrieb:
GameSetup::GameSetup() { data_.m_gameAltId = 0; data_.m_gameId = 0; data_.m_gameLevel = 0; data_.m_gameName = ""; }Für so was gibts
a) ne Initialisierungsliste
b) nen (Default-) Konstruktor für gameDataAußerdem brauchst du data_ ja eh nie - brauchst also auch keinen CTor ^^
bname schrieb:
Ne frage noch, sollte ich eig die strings wie z.B "args0, args2, args3" In dem loop deklarieren oder bevor dem loop? Oder gibt es da keine großen unterschiede?
Das Thema gabs vor kurzem (also innerhalb der letzten 3 Tage) iwie auch ma im Forum - wenn ich mich recht entsinne, ist folgendes rausgekommen:
- der Compiler optimiert es so und so
- der Zeitunterschied wird (wenn überhaupt) minimal sein - bei bei den Build-In-Datentypen (also int/...) sollte es nicht mal nen Unterschied machen
- falls es dich wirklich interessiert wirst du nen Profiler nehmen müssen (und davor mal im asm-Code gucken, obs der Compiler vll doch ganz anders optimiert hat ^^)bb
PS: Falls du Fragen hast, stell sie ^^ Hab jz scho so ewig hier rum getippt, dass es schade wäre, wenn dus einfach nur als "versteh ich nich" abtun würdest ^^
-
Hi,
-
Wieso sollte ich outputgame/loadGame nicht als Funktion machen? Wie sonst?
-
_data brauch ich ja jetzt nicht, dank dem ctor für gameData, danke.

-
Wegen dem in/before declaration @ loop, danke für die infos.
-
Von typedef hab ich gehört, dachte das es aber eher C-Stil wäre? Da tippst du doch mehr als ich.

Zu deinem code, sieht besser aus und natürlich kleiner. Nur das problem ist, dass es nicht ganz funktioniert, leider konnte ich das problem bis jetzt nicht finden.
- Falls es kein whitespace gibt, dann addiert er noch 2 nullen dazu.
gamedata.txt 2,gfdgdd,3,5 output: 2,gfdgdd,3,5,0,0- Falls der string ein whitespace hat klappts auch nicht.
gamedata.txt 2,gfd gdd,3,5 output: 2,gfd,0,0Danke für die hilfe, grüße.
-
-
- Falls es kein whitespace gibt, dann addiert er noch 2 nullen dazu.
Versteh ich nicht
- Falls der string ein whitespace hat klappts auch nicht.
scheiße - hatte ich vergessen

entweder, format ändern (string als letztes)
oder ne längenangabe des strings hinzufügen
oder du machst es ganz anders
- Von typedef hab ich gehört, dachte das es aber eher C-Stil wäre? Da tippst du doch mehr als ich.
Im Header auf jeden Fall... Und ich kann dann auch ganz einfach aus dem vector ne list/deque/... machen ^^
Ich änder einfach das typedef von std::vector<X> auf std::list<X> oder wie auch immer...Und im Source ist es kürzer und wie gesagt - ich hab eben auch die Freiheit, den Container jederzeit zu ändern:
for (TgameVec::iterator i(gameVec.begin()), e(gameVec.end()); i != e; ++i) { std::cout << *i << std::endl; }bb
-
unskilled schrieb:
- Falls der string ein whitespace hat klappts auch nicht.
scheiße - hatte ich vergessen

entweder, format ändern (string als letztes)
oder ne längenangabe des strings hinzufügen
oder du machst es ganz anders
Ich möchte an dieser Stelle nochmal auf die vorzüge von sscanf hinweisen, wie ich weietr oben schon vorgeschlagen hatte.
-
Xebov schrieb:
unskilled schrieb:
- Falls der string ein whitespace hat klappts auch nicht.
scheiße - hatte ich vergessen

entweder, format ändern (string als letztes)
oder ne längenangabe des strings hinzufügen
oder du machst es ganz anders
Ich möchte an dieser Stelle nochmal auf die vorzüge von sscanf hinweisen, wie ich weietr oben schon vorgeschlagen hatte.
Und ich möchte auf EINIGE Nachteile aufmerksam machen:
- Länge > 255 --> ?
- String enthält ',' -> liegt aber am "undurchdachten" Format
- keine Typsicherheitsscanf("%d,%s,%d,%d", gameInfos.m_gameId,gameInfos.m_gameName, &gameInfos.m_gameAltId,&gameInfos.m_gameLevel)glaube nicht, dass das compiliert

sscanf (...., gameInfos.m_gameName, ...);Wenn überhaupt:
gameInfos.m_gameName.reserve(250); sscanf(...., &gameInfos.m_gameName[0], ...);allerdings hat das nichts mehr mit Standard-konform zu tun, so weit ich weiß ^^
Er soll einfach den string ans Ende stecken, dann kann er auch schön nen std::getline verwenden - oder er speichert davor die Länge - wäre der bessere Weg (auch, wenn ein Spielname für gewöhnlich keinen Zeilenumbruch enthält ^^)
bb
-
unskilled schrieb:
Und ich möchte auf EINIGE Nachteile aufmerksam machen:
- Länge > 255 --> ?
- String enthält ',' -> liegt aber am "undurchdachten" Format
- keine TypsicherheitJa gut ok wenn ein Komma drinsteht dann funktioniert es nicht, aber sscanf ist Grundsätzlich nicht so schlecht wenn man etwas auf die ecken und Kannten aufpasst.
unskilled schrieb:
sscanf("%d,%s,%d,%d", gameInfos.m_gameId,gameInfos.m_gameName, &gameInfos.m_gameAltId,&gameInfos.m_gameLevel)glaube nicht, dass das compiliert

sscanf (...., gameInfos.m_gameName, ...);allerdings hat das nichts mehr mit Standard-konform zu tun, so weit ich weiß ^^
Das ist auch der Grund wieso drunetr noch steht (was bei deinem Quote abgeschnitten ist) das das nur mit char* funktioniert und er es wieder in nen String umwandeln müsste.