String umbrechen
-
Mein Vorschlag:
Suche nach ' ' std::string::find() und merke dir die Position. Wenn die neue Position > Zeilenlänge ist ersetzt du das ' ' an der vorhergegangenen Position mit einem '\n'
-
thordk schrieb:
würd mir den string in token zerlegen und zwischenspeichern (z.b. in nen vector packen).
Das hab ich mir auch schon überlegt. Aber geht es nicht ohne daß ich den gesamten String erstmal auslagere und danach wieder zusammensetze? Immerhin kann man in den meisten Fällen ja das Leerzeichen durch ein \n ersetzen. Das heißt, Umschichtungen gäbe es nur beim Bindestrich und bei zu langen Wörtern. Deshalb suche ich nach einer Variante, wo ich gleich mit dem Originalstring arbeiten kann.
Michael E. schrieb:
substr
Wow! Danke. Du hast mir die Augen geöffnet. Richtig: substr! Jetzt weiß ich sofort, wie ich es machen soll. Ich muß Dich wirklich loben. Das beste ist ja, daß ich durch Deinen Beitrag jetzt auch sofort weiß, wie ich die Sonderfälle mit Bindestrichen und zu langen Wörtern realisieren muß. Du hast mir wirklich geholfen. (Das war übrigens Sarkasmus. Ich find's immer wieder toll, wie Leute bei nicht trivialen Problemen einen einzelnen Begriff in den Raum schmeißen und meinen, sie hätten damit irgendwie geholfen.)
darthdespotism schrieb:
Mein Vorschlag:
Suche nach ' ' std::string::find() und merke dir die Position. Wenn die neue Position > Zeilenlänge ist ersetzt du das ' ' an der vorhergegangenen Position mit einem '\n'
Ja, o.k., das würde gehen, wenn man nur nach den Leerzeichen suchen würde. Aber damit habe ich immer noch nicht das Problem mit den Bindestrichen gelöst. Es sei denn, es gibt eine Funktion
int find_char1_or_char2 (char c1, char c2);
Gibt es nicht irgendwo einen fertigen Algorithmus für sowas? Ich finde es irgendwie Mist, daß C++ das nicht schon standardmäßig anbietet. Immerhin müssen des öfteren mal Texte auf der Konsole ausgegeben werden und da wäre es schon hilfreich, wenn man so einen Algorithmus hätte, damit man die Zeilenumbrüche nicht immer manuell eingeben muß.
-
NES-Spieler schrieb:
Michael E. schrieb:
substr
Wow! Danke. Du hast mir die Augen geöffnet. Richtig: substr! Jetzt weiß ich sofort, wie ich es machen soll. Ich muß Dich wirklich loben. Das beste ist ja, daß ich durch Deinen Beitrag jetzt auch sofort weiß, wie ich die Sonderfälle mit Bindestrichen und zu langen Wörtern realisieren muß. Du hast mir wirklich geholfen. (Das war übrigens Sarkasmus. Ich find's immer wieder toll, wie Leute bei nicht trivialen Problemen einen einzelnen Begriff in den Raum schmeißen und meinen, sie hätten damit irgendwie geholfen.)
Ich mag dich auch. Ich hab das C++-Äquivalent zu strtok genannt, weils thordk nicht eingefallen ist.
Zu deinem Sonderfallproblem: find_first_of
-
Ich hab das C++-Äquivalent zu strtok genannt, weils thordk nicht eingefallen ist.
Vielleicht solltest Du das nächste Mal schreiben: "Das C++-Äquivalent zu strtok ist substr." Ansonsten sieht es nämlich so aus, als sollte sieser eine Begriff die Antwort auf die Ursprungsfrage sein.
Zu deinem Sonderfallproblem: find_first_of
Danke dafür.
Edit: Irgendwie funktioniert das mit dem find_first_of nicht. Also, damit meine ich nicht, daß die Funktion nicht gehen würde, ich meine nur, daß das für diese Sache nicht das richtige ist. Der Grund: Alle drei Fälle (Leerzeichen, Absatz, Bindestrich) verhalten sich völlig unterschiedlich: Bei einem Absatz kommt auf jeden Fall eine neue Zeile, so daß die Zählung hier sofort wieder von vorn anfangen muß, während bei einem Leerezeichen ja nur von vorn zu zählen angefangen wird, wenn dieses wirklich an einer Stelle ist, wo der Text die Zeilenanzahl überschreitet. Und der Bindestrich geht nochmal ganz anders: Während die beiden anderen Zeichen nämlich selbst über den Rand hinausragen dürfen (also wenn die Zeile 10 Zeichen lang sein darf, dann können ja das Leerzeichen und der Absatz, da es Whitespacezeichen sind, durchaus das 11. Zeichen sein), geht das beim Bindetsrich nicht (der muß eben noch in den 10 Zeichen enthalten sein). Ein simples Suchen und dann zum letzten Punkt zurückspringen reicht also nicht.
Und dann ist mir noch folgendes Problem eingefallen: Wenn ich so einen Text habe:Ein Text
und die Zeile darf vier Zeichen lang sein, dann soll er so aussehen:
Ein Text
und nicht so:
Ein Text
Wenn das ganze aber in eine Zeile paßt, dann sollen da auch alle Leerzeichen stehen. (Eben wie in Word: Wenn man mehrere Leerzeichen hat, stehen die auch alle da. Wenn sie aber am Ende einer zeile stehen, dann gehen alle Leerzeichen über den Rand hinaus ins Nichts.)
Oh Mann, bei diesem Algorithmus muß man sein Gehirn verrenken, wenn man da alle Fälle mit beinhaltet haben will. Es muß doch sowas irgendwo fertig geben.
-
NES-Spieler schrieb:
Wenn das ganze aber in eine Zeile paßt, dann sollen da auch alle Leerzeichen stehen.
Wieso das denn? Das widerspricht eigentlich allem, was ich über Textsatz weiß. Wenn sowas unbedingt sein muss, verwendet man für diesen Zweck "harte" Leerzeichen (in HTML ).
Oh Mann, bei diesem Algorithmus muß man sein Gehirn verrenken, wenn man da alle Fälle mit beinhaltet haben will. Es muß doch sowas irgendwo fertig geben.
Also, ohne die Sache mit den überflüssigen Leerzeichen ist der Algorithmus sehr einfach (dieser Algorithmus macht *alle* Leerzeichen platt, Dein Text mit den Leerzeichen über mehrere Zeichen funktioniert also):
vector<string> split_lines(string const& text, unsigned int maxlen) { // Text in Wörter aufteilen. vector<string> tokens(istream_iterator<string>(stringstream(text)), (istream_iterator<string>())); vector<string> ret; for (vector<string>::const_iterator i = tokens.begin(); i != tokens.end(); ) { string line = ""; unsigned int len = 0; // Überlange Wörter filtern. while (i != tokens.end() and i->length() > maxlen) ret.push_back(*i++); while (i != tokens.end() and (len += i->length())++ <= maxlen) line += *i++ + ' '; ret.push_back(line); } return ret; }
Das schöne an de Code ist, dass man sich um nix kümmern muss. Wenn man diese komischen Leerzeichen berüchsichtigen will, bleibt einem nichts anderes übrig, als den Text Zeichen für Zeichen abzuarbeiten. Schwer ist das auch nicht, nur mühselig, weil man diverse Sachen beachten muss. Am besten könnte man das über ne Statusmaschine lösen.
-
-
Konrad Rudolph schrieb:
NES-Spieler schrieb:
Wenn das ganze aber in eine Zeile paßt, dann sollen da auch alle Leerzeichen stehen.
Wieso das denn?
Weil Word es auch zuläßt. In wie weit es Sinn macht, mehrere Leerzeichen zu setzen, darüber kann man ja streiten. Aber es sollte schon möglich sein. Texteditoren schmeißen ja auch nicht die Leerzeichen raus.
Was die Funktion betrifft: Welche Headerdateien muß man dafür eigentlich einbauen? Ich bekomme da nur kryptische Fehlermeldungen. Und deckt sie auch die Sache mit den Bindestrichen ab? Denn das ist mein eigentliches Problem. (Und die Sache mit den mehreren Leerzeichen.) Ohne Bindestriche würde ich das auch hinkriegen, denke ich mal.
finix schrieb:
In dem Pseudocode wird auch nur das Leerzeichen durchgenommen und nicht erklärt, wie das mit dem Bindestrich geht.
-
NES-Spieler schrieb:
Texteditoren schmeißen ja auch nicht die Leerzeichen raus.
Na. Gute Textsatzsysteme tun das sehr wohl.
Was die Funktion betrifft: Welche Headerdateien muß man dafür eigentlich einbauen?
<string> für 'std::string', <vector> für 'std::vector', <iterator> für 'std::istream_iterator', <sstream> für 'std::stringstream'.
Ich bekomme da nur kryptische Fehlermeldungen. Und deckt sie auch die Sache mit den Bindestrichen ab?
Oh, Sonderbehandlung für Bindestriche. Hmm. Du hast recht, das ist sinnvoll. Dann sollte man aber auch gleich "soft hyphens" (U+00AD, US-ASCII 0xAD) mitberücksichtigen. Und dann kommt man wirklich in einen Bereich, einen Automaten implementieren zu müssen, der ähnlich wie der TeX-Algorithmus zu Werke geht.
Hierfür kann ich jetzt auch nicht auf die Schnelle einen Algorithmus aus dem Ärmel schütteln aber es sollte eigentlich auch nicht allzu schwer sein.
-
NES-Spieler schrieb:
Was die Funktion betrifft: Welche Headerdateien muß man dafür eigentlich einbauen?
#include <sstream> // std::stringstream #include <iterator> // std::istream_iterator
NES-Spieler schrieb:
finix schrieb:
In dem Pseudocode wird auch nur das Leerzeichen durchgenommen und nicht erklärt, wie das mit dem Bindestrich geht.
Naja, das lässt sich doch leicht modifizieren. Einfach im ersten Fall nachsehen ob ein Bindestrich im Wort ist, und falls es so passt nach dem Bindestrich den Umbruch einfügen.
Vielleicht solltest du dir doch mal die externen Links im Titel ansehen.
-
So, ich hab jetzt mal einen eigenen Algorithmus geschrieben. Das mit den mehreren Leerzeichen hab ich zwar nicht hingekriegt, stattdessen werden sie rausgefiltert, aber das mit dem Bindestrich und das mit den zu langen Wörtern funktioniert. Wenn Euch noch Fehler auffallen, sagt mir bitte bescheid.
(Unten steht der Code nochmal ohne Kommentare.)
//Diese Funktion ersetzt alle Vorkommen des alten Strings mit dem neuen. void Ersetzen (string &text, const string &alterText, const string &neuerText) { UINT pos; do { pos=text.find (alterText); if (pos!=string::npos) text.replace (pos, alterText.length (), neuerText); } while (pos!=string::npos); } void TextUmbrechen (string &text, UINT &zeilenlaenge) { if (zeilenlaenge!=0) { //Alle doppelten Leerzeichen und Leerzeichen vor Absätzen //werden gelöscht. Ersetzen (text, " ", " "); Ersetzen (text, " \n", "\n"); //Feld für die einzelnen Wörter vector<string> feld (1); //Zählervariable für später int n; //Teil 1: Aufteilen des Textes //---------------------------- for (int i=0; i<text.length (); i++) { string &wort=feld [feld.size ()-1]; char &zeichen=text [i]; //Dem aktuellen Feldeintrag wird ein Zeichen hinzugefügt wort+=zeichen; //Wenn es ein Umbruchszeichen ist oder wenn die Zeilenlänge erreicht ist, if (zeichen=='\n' || zeichen==' ' || zeichen=='-' || wort.length ()==zeilenlaenge) feld.push_back (""); //wird ein neuer Feldeintrag erstellt. } text.erase (); //Teil 2: Wiederzusammensetzen des Textes //--------------------------------------- //Das n stellt den verbleibenden Platz einer Zeile dar. n=zeilenlaenge; for (i=0; i<feld.size (); i++) { string &wort=feld [i]; char &letztesZeichen=wort [wort.length ()-1]; //Für die Frage, ob in der Zeile noch genug Platz ist, //wird das letzte Zeichen, wenn es ein Absatzzeichen //oder ein Leerzeichen ist, nicht mitgezählt int laenge=wort.length ()-(letztesZeichen=='\n' || letztesZeichen==' '?1:0); //Wenn noch Platz ist (Whitespacezeichen nicht mitgezählt), if (laenge<=n) { //wird das Wort hinzugefügt. text+=wort; //und die Anzahl der Zeichen (diesmal mit //Whitespacezeichen) abgezogen. n-=wort.length (); //Wenn das letzte Zeichen ein \n war, haben wir ohnehin //eine neue Zeile und n wird wieder auf den Maximalwert //gesetzt. if (letztesZeichen=='\n') n=zeilenlaenge; } else { //Wenn kein Platz mehr war, wird eine neue Zeile begonnen. text+='\n'; n=zeilenlaenge; //i wird um eins zurückgesetzt, damit im nächsten Durchlauf //das Wort, das hier nicht mehr gepaßt hat, bearbeitet wird. i--; } } //Zum Schluß eine weitere Ersetzung. Ersetzen (text, " \n", "\n"); } }
void Ersetzen (string &text, const string &alterText, const string &neuerText) { UINT pos; do { pos=text.find (alterText); if (pos!=string::npos) text.replace (pos, alterText.length (), neuerText); } while (pos!=string::npos); } void TextUmbrechen (string &text, UINT &zeilenlaenge) { if (zeilenlaenge!=0) { Ersetzen (text, " ", " "); Ersetzen (text, " \n", "\n"); vector<string> feld (1); int n; for (int i=0; i<text.length (); i++) { string &wort=feld [feld.size ()-1]; char &zeichen=text [i]; wort+=zeichen; if (zeichen=='\n' || zeichen==' ' || zeichen=='-' || wort.length ()==zeilenlaenge) feld.push_back (""); } text.erase (); n=zeilenlaenge; for (i=0; i<feld.size (); i++) { string &wort=feld [i]; char &letztesZeichen=wort [wort.length ()-1]; int laenge=wort.length ()-(letztesZeichen=='\n' || letztesZeichen==' '?1:0); if (laenge<=n) { text+=wort; n-=wort.length (); if (letztesZeichen=='\n') n=zeilenlaenge; } else { text+='\n'; n=zeilenlaenge; i--; } } Ersetzen (text, " \n", "\n"); } }