std::string encoding


  • Mod

    bjarne stroustrup55 schrieb:

    Nicht ganz, der C++-String hat eine Optimierung für kleine Längen.

    Nein. Er hat möglicherweise Optimierungen für Zeichenketten, welche möglicherweise auch eine Optimierung für kurze Längen sein kann.



  • SeppJ schrieb:

    bjarne stroustrup55 schrieb:

    Nicht ganz, der C++-String hat eine Optimierung für kleine Längen.

    Nein. Er hat möglicherweise Optimierungen für Zeichenketten, welche möglicherweise auch eine Optimierung für kurze Längen sein kann.

    Bzw. die Implementierung eines std::string hat die Optimierungen. Denn der C++-String aus dem Standard hat keine. Nur vielleicht eine Implementierung von GCC/VC++ und Konsorten.



  • SeppJ schrieb:

    Er hat möglicherweise Optimierungen für Zeichenketten, welche möglicherweise auch eine Optimierung für kurze Längen sein kann.

    Welche andere Optimierung wäre denn noch denkbar?



  • Sone schrieb:

    PI war auch der Meinung, std::string habe zu viele Member

    Ja, und da ist er nicht ganz alleine. Das Interface von std::string ist eine Katastrophe.

    http://www.gotw.ca/gotw/084.htm



  • Nexus schrieb:

    Sone schrieb:

    PI war auch der Meinung, std::string habe zu viele Member

    Ja, und da ist er nicht ganz alleine. Das Interface von std::string ist eine Katastrophe.

    Dem habe ich nicht widersprochen. find_first_not_of muss aber für Strings in irgendeiner Weise spezialisiert werden, weil es möglich sein sollte es mit einem String-Literal o.ä. aufzurufen.

    Das es an std::string gekoppelt ist ist natürlich doof. Eigentlich sollte das für jeden Container gehen, und eine Überladung für char / char16_t /... als Wertetyp ist auch nicht schwer. Nur finde ich es wiederum praktisch, eine Variante in std::string zu haben die auch Bequem für Strings aufzurufen ist.

    Ein find_last_not_of wie dieses hier*

    template<typename BidirectionalIterator,
             typename ForwardIterator,
             typename ValueT>
    BidirectionalIterator find_last_not_of( BidirectionalIterator first,
    							         	BidirectionalIterator last,
    								        ForwardIterator first2,
    								        ForwardIterator last2 )
    {
    	auto iter = last;
    
    	for(;;)
    	{
    		if( iter == first )
    			break;
    
    		if( std::find(first2, last2, *--iter) != last2 )
    			return iter;
    	}
    
    	return last;
    }
    

    ist mit Strings umständlicher zu bedienen.

    std::string str("ABCDEFGHIJ");
    char const ptr[] = "AEIOU"; 
    auto iter = find_last_not_of( std::begin(str), std::end(str), std::begin(ptr), std::end(ptr) );
    

    (anstatt

    std::string str("ABCDEFGHIJ");
    auto pos = str.find_last_not_of("AEIOU");
    

    )

    Es stimmt jedoch, diese Bequemlichkeit kompensiert die Unflexibilität nicht.

    ~* Ich konnte nicht widerstehen. Ungetestet.~



  • Meine Güte, habe ich Sehstörungen? Ich dachte, knivil hätte den Post oben geschrieben...es war Nexus. habe ich irgendeine Leseschwäche?



  • Also wenn std::string schon eigene Memberfunktionen hat, dann sollen die gefälligst auch optimiert sein (KMP und so). Stattdessen sind sie die unnötigsten Brute-Force-Implementierungen (siehe Sone), die eigentlich besser über reverse_iterator + find_first_not_of implementiert wären.


  • Mod

    muh schrieb:

    SeppJ schrieb:

    Er hat möglicherweise Optimierungen für Zeichenketten, welche möglicherweise auch eine Optimierung für kurze Längen sein kann.

    Welche andere Optimierung wäre denn noch denkbar?

    Optimierung auf die Zeichenketten "Hello World!", "Foo", "Bar" und 252 weitere.



  • SeppJ schrieb:

    Optimierung auf die Zeichenketten "Hello World!", "Foo", "Bar" und 252 weitere.

    Wie willst du das machen?

    std::string s = "Foo";
    char *c = &s[1];
    *c = '!';
    


  • Youka schrieb:

    std::string hält Charaktere als char Array, womit diesen jeweils 1 Byte zur Verfügung steht. Wie dieser nun verwendet wird ist im Standard nicht definiert, wobei man für Windows ANSI und für Unix UTF-8 annehmen könnte anhand der Ausgabe durch OS API Funktionen.

    Und wie kann man dann ein File mit
    fstream (const string& filename, ios_base::openmode mode = ios_base::in | ios_base::out);
    richtig öffnen?
    Muss ich meine strings Betriebsystem spezifisch konvertieren?


  • Mod

    muh schrieb:

    SeppJ schrieb:

    Optimierung auf die Zeichenketten "Hello World!", "Foo", "Bar" und 252 weitere.

    Wie willst du das machen?

    std::string s = "Foo";
    char *c = &s[1];
    *c = '!';
    

    Die Optimierung merkt natürlich, sobald eine non-const Memberfunktion aufgerufen wird und schaltet dann in den normalen Modus.



  • SeppJ schrieb:

    Die Optimierung merkt natürlich, sobald eine non-const Memberfunktion aufgerufen wird und schaltet dann in den normalen Modus.

    Ohne bad_alloc?



  • kmp schrieb:

    die eigentlich besser über reverse_iterator + find_first_not_of implementiert wären.

    Weil du dann Sechs Zeilen Code weniger schreiben musst? Weil es flexibler ist? Aber ich sehe da nur den Umstand, das literarische Programmieren aufzugeben. Der Funktionsname sollte doch schon möglichst für sich sprechen.


  • Mod

    muh schrieb:

    SeppJ schrieb:

    Die Optimierung merkt natürlich, sobald eine non-const Memberfunktion aufgerufen wird und schaltet dann in den normalen Modus.

    Ohne bad_alloc?

    Klar doch! Es ist keine Platzoptimierung. Die Zeichenkette wird ganz normal gespeichert. Und noch ein weiteres Byte, das anzeigt, ob es sich um eine bekannte Sequenz handelt. Bloß die ganzen Suchmemberfunktionen sind optimiert und benutzen einen Lookup-Table für die optimierten Fälle.



  • SeppJ schrieb:

    muh schrieb:

    SeppJ schrieb:

    Die Optimierung merkt natürlich, sobald eine non-const Memberfunktion aufgerufen wird und schaltet dann in den normalen Modus.

    Ohne bad_alloc?

    Klar doch! Es ist keine Platzoptimierung. Die Zeichenkette wird ganz normal gespeichert. Und noch ein weiteres Byte, das anzeigt, ob es sich um eine bekannte Sequenz handelt. Bloß die ganzen Suchmemberfunktionen sind optimiert und benutzen einen Lookup-Table für die optimierten Fälle.

    std::string s = "Foo";
    std::string t = "Foo";
    char *c = &s[1]; // s welchselt in normalen Modus
    std::cout << (s == t) << '\n'; // s checkt sich selber erneut nach jedem der 255 Magic-Strings!?
    

  • Mod

    muh schrieb:

    [code="cpp"]
    std::cout << (s == t) << '\n'; // s checkt sich selber erneut nach jedem der 255 Magic-Strings!?[/cpp]

    Nein, s vergleicht sich einfach mit t. Jeder der Strings kennt zu jeder Zeit seinen genauen Wert.

    Selbst wenn dies nicht so wäre, so ist die Komplexität von == nicht vorgegeben. Das wäre dann eben ein Kompromiss bei der Optimierung, dass == langsamer wird, anderes dafür schneller.



  • Sone schrieb:

    Nexus schrieb:

    Sone schrieb:

    PI war auch der Meinung, std::string habe zu viele Member

    Ja, und da ist er nicht ganz alleine. Das Interface von std::string ist eine Katastrophe.

    Dem habe ich nicht widersprochen. find_first_not_of muss aber für Strings in irgendeiner Weise spezialisiert werden, weil es möglich sein sollte es mit einem String-Literal o.ä. aufzurufen.

    Das es an std::string gekoppelt ist ist natürlich doof. Eigentlich sollte das für jeden Container gehen, und eine Überladung für char / char16_t /... als Wertetyp ist auch nicht schwer. Nur finde ich es wiederum praktisch, eine Variante in std::string zu haben die auch Bequem für Strings aufzurufen ist.

    Ein find_last_not_of wie dieses hier*

    template<typename BidirectionalIterator,
             typename ForwardIterator,
             typename ValueT>
    BidirectionalIterator find_last_not_of( BidirectionalIterator first,
    							         	BidirectionalIterator last,
    								        ForwardIterator first2,
    								        ForwardIterator last2 )
    {
    	auto iter = last;
    
    	for(;;)
    	{
    		if( iter == first )
    			break;
    			
    		if( std::find(first2, last2, *--iter) != last2 )
    			return iter;
    	}
    	
    	return last;
    }
    

    ist mit Strings umständlicher zu bedienen.

    std::string str("ABCDEFGHIJ");
    char const ptr[] = "AEIOU"; 
    auto iter = find_last_not_of( std::begin(str), std::end(str), std::begin(ptr), std::end(ptr) );
    

    (anstatt

    std::string str("ABCDEFGHIJ");
    auto pos = str.find_last_not_of("AEIOU");
    

    )

    Es stimmt jedoch, diese Bequemlichkeit kompensiert die Unflexibilität nicht.

    ~* Ich konnte nicht widerstehen. Ungetestet.~

    Man kann natürlich einfach Überladungen anbieten.



  • Das meinte ich ja, Überladen. (Mit in "irgendeiner Weise spezialisiert", wobei der Begriff Spezialisierung in diesem Kontext natürlich völlig falsch gebraucht wurde)



  • supersonic schrieb:

    Und wie kann man dann ein File mit
    fstream (const string& filename, ios_base::openmode mode = ios_base::in | ios_base::out);
    richtig öffnen?
    Muss ich meine strings Betriebsystem spezifisch konvertieren?

    Das musst du doch wissen. Wer soll die geschriebenen Dateien denn später mal lesen? Wenn sie einem bestimmten Format folgen, gibt dieses oft auch die Zeichenkodierung vor. Viele Formate/Protokolle erlauben auch die Festlegung der Zeichenkodierung in einem Header o.ä.

    Wenn du das Format selbst bestimmst, kannst du dir natürlich was aussuchen.

    Wenn du fremde Dateien liest, musst du grundsätzlich die entsprechende Kodierung wissen. Wenn dies nicht gegeben ist, kannst du noch rumraten, was aber IMHO nicht empfehlenswert ist. Ich würde heute einfach von UTF-8 ausgehen und dies so dokumentieren. Füttert dich jemand mit ungültigem UTF-8 gibt es etablierte Vorgehensweisen damit umzugehen.

    Um die 234 Millionen verschiedenen Legacy-Encodings würde ich mir bei einem neuen Programm keinen Kopf mehr machen. Andernfalls ist mit Kopfschmerzen zu rechnen.

    Vorteil an UTF-8 ist auch, dass es eine Obermenge von ASCII ist (Codepoints unter 0x80 werden exakt wie in ASCII kodiert) und insbesondere, dass es keine Nullbytes enthalten kann. D.h. du bekommst keine Probleme mit APIs, denen die Bedeutung der ihnen übergebenen Bytes zwar völlig egal ist, aber keine Nullbytes mittendrin akzeptieren.


Anmelden zum Antworten