Line feed (Multiplattform) und ein zeilenweises Einlesen
-
Badestrand schrieb:
Ich weiß nicht, ob's da eine fertige Lösung für gibt, zur Not musst du es eben selber machen... Einfach bis '\r' oder '\n' einlesen, das Gelesene als neue Zeile abspeichern und alle folgenden '\r' und '\n'-Zeichen überspringen.
Das würde natürlich gehen... performant wäre es aber wahrscheinlich nicht. Kann man denn davon ausgehen, dass wenn eine datei im unix format auf einer windows maschine geöffnet und bearbeitet wird, diese datei auch wieder den windows typischen line feed in der gesamten datei bekommt ? dann würde ich die erste zweite dritte zeile nach dem line feed scannen und anschließend mit getline über die datei huschen...
-
Winn schrieb:
Badestrand schrieb:
Ich weiß nicht, ob's da eine fertige Lösung für gibt, zur Not musst du es eben selber machen... Einfach bis '\r' oder '\n' einlesen, das Gelesene als neue Zeile abspeichern und alle folgenden '\r' und '\n'-Zeichen überspringen.
Das würde natürlich gehen... performant wäre es aber wahrscheinlich nicht.
Naja, irgendwie muss das die STL ja auch machen

Winn schrieb:
Kann man denn davon ausgehen, dass wenn eine datei im unix format auf einer windows maschine geöffnet und bearbeitet wird, diese datei auch wieder den windows typischen line feed in der gesamten datei bekommt ?
Wenn du die Datei mit dem Win-Editor öffnest, ist das bestimmt so. Ich würde sie aber so lassen wie sie ist; sie könnte von einem anderen Programm schreibend geöffnet sein, so dass du nicht schreiben kannst, oder sie liegt in einem Verzeichnis, auf dass du keinen Schreibzugriff hast.
Das Problem lässt sich sicherlich auch so lösen! Du könntest z.B. erstmal Byte für Byte einlesen, bis du auf '\r' oder '\n' stößt. Ist es ein '\r' und kommt danach kein '\n', ist es die Mac-Version, folgt ein '\n', ist es Windows. Gibt's gar kein '\r', ist es wohl Linux
-
ganz kranke idee:
Du könntest die datei auch in einen ganz großen string einlesen und dann mit boost::tokenizer bei \n oder \r trennen:
ich tipps jetzt ma nur ausm kopf, aber so soltle es gehen
std::string datei; // datei einlesen typedef boost::tokenizer<boost::char_separator<char> > tokenizer; boost::char_separator<char> sep("\r\n"); tokenizer tokens(datei, sep); for(tokenizer::iterator it = tokens.begin(); it != tokens.end(); ++it) cout << *it << endl; // hier werden die einzelnen zeilen ausgegebenBoost::tokenizer splittet nicht in einzelne tokens, wenn die beiden trenner aufeinander kommen, also wäre das genau das was du brauchst. Wie das mit der performance ist weiß ich nicht. Und du musst die ganze datei ja in den speicher laden. Wenn die zu groß is is auch nich so gut
-
hola
ne eigene getline version is ja nicht so gross:
bool isEndlineChar(char c) { return c == '\n' || c == '\r'; } std::istream& getline_special(std::istream &in, std::string &str) { std::streambuf *buf = in.rdbuf(); int c; while(!isEndlineChar(c = buf->sbumpc()) && c != EOF) str += static_cast<char>(c); if(c != EOF) while(isEndlineChar(buf->sgetc())) buf->sbumpc(); return in; }habs nicht getestet, sollte aber funktionieren.
Meep Meep
-
Hm, werden dabei aber auch Leerzeilen erfasst? Oder macht das getline auch nicht? Man dürfte eigentlich maximal ein '\r' und ein '\n' überspringen..
-
Badestrand schrieb:
Hm, werden dabei aber auch Leerzeilen erfasst? Oder macht das getline auch nicht? Man dürfte eigentlich maximal ein '\r' und ein '\n' überspringen..
leere zeilen werden damit natuerlich eliminiert. um es konform zum std::getline zu machen, muesste man es noch anpassen. sollte aber kein problem darstellen.
EDIT:
bool isEndlineChar(char c) { return c == '\n' || c == '\r'; } std::istream& getline_special(std::istream &in, std::string &str) { std::streambuf *buf = in.rdbuf(); int c; while(!isEndlineChar(c = buf->sbumpc()) && c != EOF) str += static_cast<char>(c); if(c != EOF) { if(c == '\r') if(buf->snextc() == '\n') buf->sbumpc(); } return in; }so sollte es nun passen
Meep Meep
-
Hm, habs mal probiert, die letzte Zeile wird aber nicht mehr dargestellt:
std::istream& getline_special2(std::istream &in, std::string &str) { str.clear(); char c; while ( in.get(c) && !isEndlineChar(c) ) str.push_back( c ); if ( c == '\r' ) if ( in.peek() == '\n' ) in.get(c); return in; } std::ifstream f( "C:\\Test.txt" ); std::string line; while ( getline_special2(f,line) ) std::cout << line << std::endl;Ist ja auch logisch, weil der stream nicht mehr gültig ist nach dem Einlesen der letzten Zeile, aber wie macht das normale getline das?
-
vielleicht so:
std::istream& getline_special(std::istream &in, std::string &str) { std::streambuf *buf = in.rdbuf(); if(buf->sgetc() == EOF) { in.setstate(std::ios::eof); return in; } int c; while(!isEndlineChar(c = buf->sbumpc()) && c != EOF) str += static_cast<char>(c); if(c != EOF) { if(c == '\r') if(buf->snextc() == '\n') buf->sbumpc(); } return in; }beim ersten durchgang wird am ende kein eof-bit gesetzt, weil ich die einzelen bytes ueber streambuf lese. wenn dann nochmal aus dem istream gelesen werden soll, und man bekommt EOF zurueck, setzt man das EOF-bit und gibt den istream zurueck. hab leider keinen compiler bei der hand zum testen.
Meep Meep
-
Winn schrieb:
ich habe Textdateien, welche von unterschiedlichen Plattformen (Unix, Windows, Mac) stammen, welche ich gerne Zeilenweise einlesen möchte.
...
aber getline scheint als Delimiter standardmässig "\n" zu verwenden, was unter Windows "\r\n" und Unix "\n" Systemen kein Problem ist, aber auf dem Mac wird mit "\r" abgeschlossen.Der C++-Standard stellt für Probleme dieser Art eine Facette codecvt<> zur Verfügung. Mal angenommen Deine Facette hieße
class EndOfLine : public std::codecvt< char, char, std::mbstate_t >dann hängt man die mit
ifstream quelle( "input.txt" ); quelle.imbue( locale( quelle.getloc(), new EndOfLine( Windows Unix oder Mac-Datei ) ) );in den Stream ein. Bei ofstream das gleiche. In den protected Methoden do_in und do_out kannst Du dann die Konvertierung vornehmen.
Du musst bloss aufpassen, dass Dein unterliegendes Dateisystem nicht schon eine Konvertierung vornimmt. Man kann das umgehen indem man die Dateien mit std::ios_base::binary öffnet.Gruß
Werner
-
Werner Salomon schrieb:
Du musst bloss aufpassen, dass Dein unterliegendes Dateisystem nicht schon eine Konvertierung vornimmt. Man kann das umgehen indem man die Dateien mit std::ios_base::binary öffnet.
*erstaunt*
Heißt das, dass das entsprechende OS schon konvertiert, wenn man eine Datei im Textmodus öffnet? Was macht es genau, weißt du noch mehr darüber?
-
Badestrand schrieb:
Heißt das, dass das entsprechende OS schon konvertiert, wenn man eine Datei im Textmodus öffnet? Was macht es genau, weißt du noch mehr darüber?

Also ganz konkret: Wenn man unter mit dem Visual Studio 8 einen Code schreibt, der eine Datei öffnet und die Zeichen einzeln einliest, so fehlt immer das '\r' vor dem '\n', wenn in der Datei selbst diese Folge steht.
Umgekehrt wird beim Schreiben vor jedes '\n' ein '\r' eingefügt. Immer vorausgesetzt man öffnet die Datei nicht im binary-mode.
Soweit ich das nachvollziehen kann, passiert diese Konvertierung jenseits des basic_filebuf< char >, und es gilt wohl gleichermaßen für C - also printf. Der Code dazu steckt irgendwo in der MS-C-Runtime-Library. Und ist damit wohl nicht Teil des OS.Unix (also auch Linux) braucht nichts tun, '\n' bleibt '\n'. Wie es beim Mac ist, weiß ich nicht.
Gruß
Werner