CStringW zu std:string



  • EOP schrieb:

    Viele Wege führen nach Rom.

    Ob das aber Sinn macht die ganze Konvertierung zu Fuß zu programmieren. Bin mir nicht so sicher.

    Naja, den** CStringW zuerst in einen CStringA zu kopieren, nur um ihn dann ein weiteres mal in einen std::string **zu kopieren, erscheint mir aber irgendwie wenig sinnvoll 😕

    Außerdem kann der Konstruktor von** CStringA , der einen CStringW als Argument annimmt, intern letztendlich auch nichts anderes tun, als den Eingabe-String Zeichen für Zeichen von wchar_t nach char **zu konvertieren.

    Da kann man diese Konvertierung auch direkt von** CStringW nach std::string **implementieren und sich mindestens eine überflüssige Deep-Copy sparen...



  • Ende der 80er hab ich auch den TurboC code als .asm ausgeben lassen und den code dann händisch optimiert.

    Braucht man das heutzutage noch? Eher nein wenn man nicht gerade ne Marsmission oder ne Herzpumpe programmiert.

    Also wieso sollte ich das zum 100000sten Mal selber programmieren wenn es auch einfacher geht?

    EDIT:
    Wenn du gleich nach ASCII umwandelst und base64 decodierst sparst du eine Menge an Speicher.
    50% gegenüber UNICODE und ungefähr nochmal 4/5 nach dem base64 decodieren.

    EDIT #2:
    Quatsch, du sparst natürlich 1/5 und nicht 4/5.



  • EOP schrieb:

    Ende der 80er hab ich auch den TurboC code als .asm ausgeben lassen und den code dann händisch optimiert.

    Braucht man das heutzutage noch? Eher nein wenn man nicht gerade ne Marsmission oder ne Herzpumpe programmiert.

    Also wieso sollte ich das zum 100000sten Mal selber programmieren wenn es auch einfacher geht?

    Wieso sollte man Daten nicht unnötig 2x hin und her kopieren, wenn es sich auch relativ leicht vermeiden lässt? Die Frage beantwortet sich ja von selbst.

    Zuerst immer schnellere Rechner und immer größeren Arbeitsspeicher entwickeln, nur um diese dann mit ineffizientem Code und durch Speicherverschwendung auszulasten, kann ja wohl nicht der Sinn der Übung sein.

    Außerdem war die ursprüngliche Frage hier ja gerade, wie man einen** CStringW möglichst effizient in einen std::string **"umwandeln" kann, insbesondere im Hinblick auf den Speicherverbrauch.

    EOP schrieb:

    EDIT:
    Wenn du gleich nach ASCII umwandelst und base64 decodierst sparst du eine Menge an Speicher.
    50% gegenüber UNICODE und ungefähr nochmal 4/5 nach dem base64 decodieren.

    EDIT #2:
    Quatsch, du sparst natürlich 1/5 und nicht 4/5.

    Deswegen hatte ich dem OP ja schon am Anfang empfohlen möglichst von vorne herein einen ANSI-String anstelle eines Wide-Strings für seine Base64-kodierten Daten zu verwenden, oder, falls möglich, die Base64-kodierten Daten direkt in Binär-Daten zu dekodieren.

    Es scheint aber so zu sein, dass er die Base64-Daten nun mal als** CStringW herein bekommt und als std::string **in eine Funktion weiter reichen muss...



  • DeathCubeK schrieb:

    Dass wir später nur das erste Byte aus dem Ausgabe-Array übernehmen sollte kein Problem sein, da praktisch alle Multibyte-Kodierungen zu ASCII abwärts-kompatibel sind, d.h. alle ASCII-Zeichen werden mit einem einzigen Byte und mit dem selben Bit-Mutser wie im ASCII-Standard dargestellt (Beispiel UTF-8: Jeder ASCII-kodierte Text ist automatisch auch ein 100% gültiger UTF-8 Text; In Umgekehrter Richtung gilt das natürlich nur, wenn der UTF-8 Text keine Zeichen enthält, die in ASCII nicht darstellbar sind).

    Ja, ich weiss dass fast alle MB-Codepages mit den 128 ASCII Codes anfangen. Gerade deswegen kann man das wctomb auch ganz weglassen, wenn garantiert ist dass man nur ASCII reinbekommt 😉



  • Danke für eure Antworten,

    ich habe jetzt von CStringW auf CStringA umgebaut.

    Wird hier nicht kopiert? Ich möchte nicht kopieren da ich dann soviel speicher brauche.

    std::string outputBuffer(cstrSource.GetBuffer());
    


  • lulu32_0 schrieb:

    Danke für eure Antworten,

    ich habe jetzt von CStringW auf CStringA umgebaut.

    Wird hier nicht kopiert? Ich möchte nicht kopieren da ich dann soviel speicher brauche.

    std::string outputBuffer(cstrSource.GetBuffer());
    

    Doch, hier wird kopiert (von CString nach std::string).
    cstrSource.ReleaseBuffer() nicht vergessen, gelle...



  • lulu32_0 schrieb:

    Danke für eure Antworten,

    ich habe jetzt von CStringW auf CStringA umgebaut.

    Wird hier nicht kopiert? Ich möchte nicht kopieren da ich dann soviel speicher brauche.

    std::string outputBuffer(cstrSource.GetBuffer());
    

    Es gibt, soweit ich weiß, keinen** std::string Konstruktor, dem man einen existierenden Puffer zur Weiterverwendung unterschieben kann (bei QByteArray **ginge das, siehe hier).

    ** std::string **allokiert stets seinen eignen internen Puffer und im Konstruktor wird der Eingabe-String in diesen Puffer kopiert:

    from c-string (4)
    ** string (const char* s); **
    Copies the null-terminated character sequence (C-string) pointed by s.

    from sequence (5)
    ** string (const char* s, size_t n); **
    Copies the first n characters from the array of characters pointed by s.

    Ich denke daher, es lässt sich nicht verhindern, dass die Daten zumindest 1x kopiert werden, wenn es ein** std::string **werden soll/muss.

    _________

    Indem Du zuvor bereits durchgängig mit** CStringA arbeitest, sparst Du Dir aber immerhin eine unnötige Kopie und der Overhead für die wchar_t nach char **Konverteirung entfällt.



  • Vielen Dank, ich glaube ich verstehe es jetzt.

    DeathCubeK schrieb:

    EOP schrieb:

    Irgendwie scheint es nur so zu funktionieren:

    CStringW hellocw ( _T("Hello World!") );
    CStringA helloca ( hellocw );
    string hellos ( helloca.GetString() );
    

    Nö, geht auch so:

    CStringW hellocw (L"Hello World!");
    const int len = hellocw.GetLength();
    std::string hellos;
    hellos.reserve(len);
    for(int i = 0; i < len; i++)
    {
    	hellos += (char) hellocw.GetAt(i);
    }
    printf("RESULT: \"%s\"\n", hellos.c_str());
    

    Immer vorausgesetzt, in "hellocw" kommen tatsächlich nur ASCII-Zeichen vor - sonst bekommst da Blödsinn raus 😉

    Hierzu aber noch eine Frage. Was meinst du mit nur ASCII - Zeichen? Gibt es in Unicode andere zeichen?



  • lulu32_o schrieb:

    Vielen Dank, ich glaube ich verstehe es jetzt.

    DeathCubeK schrieb:

    EOP schrieb:

    Irgendwie scheint es nur so zu funktionieren:

    CStringW hellocw ( _T("Hello World!") );
    CStringA helloca ( hellocw );
    string hellos ( helloca.GetString() );
    

    Nö, geht auch so:

    CStringW hellocw (L"Hello World!");
    const int len = hellocw.GetLength();
    std::string hellos;
    hellos.reserve(len);
    for(int i = 0; i < len; i++)
    {
    	hellos += (char) hellocw.GetAt(i);
    }
    printf("RESULT: \"%s\"\n", hellos.c_str());
    

    Immer vorausgesetzt, in "hellocw" kommen tatsächlich nur ASCII-Zeichen vor - sonst bekommst da Blödsinn raus 😉

    Hierzu aber noch eine Frage. Was meinst du mit nur ASCII - Zeichen? Gibt es in Unicode andere zeichen?

    ASCII war einer der ersten binären Kodierungen für Schriftzeichen überhaupt (noch aus der Fernschreiber-Ära). ASCII umfasst nur 128-Zeichen und ist eigentlich ein 7-Bit Code, wobei aber praktisch immer 8-Bit (d.h. ein char in C/C++) pro Zeichen gespeichert wird:
    http://www.asciitable.com/index/asciifull.gif

    Offensichtlich ist ASCII in seinem Umfang stark begrenzt und vor allem für die englische Sprache ausgelegt, weshalb später diverse Codepages für verschiedene Sprachen eingeführt wurden, z.B. Latin-1 (ISO 8859-1) oder Kyrillisch (ISO 8859-5). Diese Codepages ordnen die 256 möglichen Werte eines Bytes bzw. char's je nach Sprache unterschiedlich zu. Dabei sind die "unteren" 128 Zeichen in aller Regel mit ASCII identisch, die "oberen" 128 Zeichen hingegen werden Sprach-spezifisch zugeordnet.

    Der große Nachteil an Codepages ist, dass man je nach verwendeter Codepage immer nur Zeichen einer Sprache darstellen kann. Du könntest also nie lateinische, kyrillische und japanische Zeichen in einem Text mischen! Außerdem ergibt ein Text nur dann Sinn, wenn er mit der "korrekten" Codepage interpretiert wird. Die ist aber im Allgemeinen System-spezifisch. Eine Datei, die auf einem japanischen System erstellt wurde, würde auf Deinem deutschen System also nur Kauderwelsch ergeben.

    Unicode ist der Versuch einen Standard zu schaffen, der alle Schriftzeichen enthält, die auf der Welt vorkommen. Dabei definiert Unicode aber zunächst einmal nur eine (sehr großen) Zeichenvorrat, aber keine konkrete Kodierung! Unicode Zeichen können z.B. als UTF-16 oder als UTF-8 kodiert werden. Unter Windows enthält ein** wchar_t -String normalerweise Unicode-Daten in der UTF-16 Kodierung. Unter Linux hingegen, wird üblicherweise UTF-8 benutzt, so dass die Unicode-Daten in einen "normalen" char **-String passen.



  • lulu32_0 schrieb:

    Wird hier nicht kopiert? Ich möchte nicht kopieren da ich dann soviel speicher brauche.

    std::string outputBuffer(cstrSource.GetBuffer());
    
    1. Das GetBuffer ist Unsinn, nimm GetString statt dessen. Dann brauchst du auch kein ReleaseBuffer.
    2. Wenn du willst dass gar nicht kopiert wird, dann schreib die Funktion um dass sie mit nem const char* arbeitet.
    3. Was du beschrieben hast was davor schon passiert klingt abenteuerlich - ich denke da wird auch einiges an sinnlosem Speicherverbrauch zu finden sein.
    4. Kauft einfach mehr RAM für die Kiste auf der das laufen soll. 16 GB kosten heutzutage echt nicht mehr die Welt, und dann können einem ein paar hundert MB Ramverbraucht normalerweise auch egal sein. (Es sei denn das Programm muss zig- oder hundertfach gleichzeitig laufen oder andere Extremfälle.) Rechne dir mal durch was selbst ein billiger Programmierer pro Arbeitstag kostet (inklusive Nebenkosten etc.), und dann vergleich das mit dem was 16 GB RAM kosten...


  • Das ist keine option. Das ganze wird auf mehreren tausend Rechner installiert die teilweise nur 1gb ram haben. Das Programm selber braucht im Idle 4mb Ram. Wenn dann die Prozedur startet geht es bis auf 120mb hoch. es kommen immer Datenblocke von ca. 10-200mb rein. Das ganze kommt als const char* an.

    Das muss dann gefiltert werden. Das ganze ist wie eine XML aufgebaut wobei jeder Tag nur einmal vorkommt und es keine Baumstruktur ist. Vor dem Datenstrom steht ein Schlusselwort "<DataStream>" Danach muss dann erst mal gesucht werden. Das Ende ist mit </DataStream>" markiert. Deswegen wird das ganze erstmal in einen CString konvertiert und dann unbenötigte teile "deleted". Anschließend bekomme ich diesen Stream zum Base64 decodieren. Hierzu benutze ich eine Funktion die einen std::String erwartet.

    Hmm wie kann ich denn im const char* nach "DataStream" suchen? Da müsste ich eine Funktion schreiben die nach "D" sucht und dann das nächste zeichen mit dem "Eingabestring" vergleicht. Das an das ende würde ich dann vermutlich einen Pointer zeigen lassen um danach die zeichen zählen bis ich das ende gefunden habe.. das wird alles sehr abenteuerlich^^

    BTW eine weitere Frage. Ich schreibe den decodierten Stream in eine Datei mit CFile. Mir ist jetzt aufgefallen, das ich auf C:\\ keine Datei anlegen kann (vermutlich rechte aufm Firmenrechner). Allerdings spuckt mir CFile keinen Fehler aus. Das tut so als ob nix wäre. Aber es wird keine Datei angelegt. Hab ich hier was vergessen?



  • 🙄 oje.



  • lulu32_0 schrieb:

    Das ist keine option. Das ganze wird auf mehreren tausend Rechner installiert die teilweise nur 1gb ram haben.

    OK, dann ist RAM Upgrade wohl wirklich keine (gute) Option.

    lulu32_0 schrieb:

    Hmm wie kann ich denn im const char* nach "DataStream" suchen? Da müsste ich eine Funktion schreiben die nach "D" sucht und dann das nächste zeichen mit dem "Eingabestring" vergleicht. Das an das ende würde ich dann vermutlich einen Pointer zeigen lassen um danach die zeichen zählen bis ich das ende gefunden habe.. das wird alles sehr abenteuerlich^^

    Naja es gibt die fertige Funktion strstr die man verwenden könnte.

    lulu32_0 schrieb:

    BTW eine weitere Frage. Ich schreibe den decodierten Stream in eine Datei mit CFile. Mir ist jetzt aufgefallen, das ich auf C:\\ keine Datei anlegen kann (vermutlich rechte aufm Firmenrechner).

    Im Rootverzeichnis eines Volumes haben normale Benutzer normalerweise nicht das Recht Dateien zu erzeugen.

    lulu32_0 schrieb:

    Allerdings spuckt mir CFile keinen Fehler aus. Das tut so als ob nix wäre. Aber es wird keine Datei angelegt. Hab ich hier was vergessen?

    Möglicherweise werden die Files von Windows "umgeleitet". Bei Root-Verzeichnissen wäre mir diesbezüglich zwar nix bekannt, aber du kannst ja mal unter %LocalAppData%\VirtualStore nachgucken, ob dein File vielleicht da zu finden ist.

    Die Lösung ist aber auf jeden Fall einen anderen Pfad zu verwenden. Mach einfach ein eigenes Verzeichnis irgendwo, da darfst du dann reinschreiben.



  • ps:

    lulu32_0 schrieb:

    Vor dem Datenstrom steht ein Schlusselwort "<DataStream>" Danach muss dann erst mal gesucht werden. Das Ende ist mit </DataStream>" markiert. Deswegen wird das ganze erstmal in einen CString konvertiert und dann unbenötigte teile "deleted".

    Du musst gar nix löschen.
    Es reicht z.B. wenn du das "<" von "</DataStream>" mit einer binären Null ("\0") überschreibst.
    Dann kannst du den Zeiger auf das Zeichen hinter dem ">" von "<DataStream>" an jede Funktion übergeben die einen C-String (= const char* ) erwartet, und es wird funktionieren.

    lulu32_0 schrieb:

    Anschließend bekomme ich diesen Stream zum Base64 decodieren. Hierzu benutze ich eine Funktion die einen std::String erwartet.

    Mach die Base64 Dekodierung selbst, direkt mit dem const char* . Ist ja nicht so schwer.
    Dann kannst du an die Funktion auch gleich die Länge des (kodierten) Base64 Strings übergeben, und kannst dir das Überschreiben des "<" mit "\0" sparen.

    So kannst du das alles machen ohne jemals irgendwo grössere Speichermengen anzufordern. Abgesehen von dem einen grossen Puffer in dem die ganze Nachricht inklusive der pseudo-XML Tags drinsteht. Den auch noch zu eliminieren wäre dann aber einigermassen viel Aufwand, und wird vermutlich auch nicht nötig sein.



  • lulu32_0 schrieb:

    Hmm wie kann ich denn im const char* nach "DataStream" suchen? Da müsste ich eine Funktion schreiben die nach "D" sucht und dann das nächste zeichen mit dem "Eingabestring" vergleicht. Das an das ende würde ich dann vermutlich einen Pointer zeigen lassen um danach die zeichen zählen bis ich das ende gefunden habe.. das wird alles sehr abenteuerlich^^

    Du hast nie mit reinen C-Strings programmiert, oder? Die Funktion gibt es natürlich in der Standard-Bibliothek 😃

    Schau Dir mal die** strstr() **Funktion an:

    const char * strstr ( const char * str1, const char * str2 );

    Locate substring
    Returns a pointer to the first occurrence of str2 in str1, or a null pointer if str2 is not part of str1.
    The matching process does not include the terminating null-characters, but it stops there.

    Beachte, dass der Rückgabewert den Dir diese Funktion liefert ein Zeiger in den Eingabe-Puffer ist, es wir hier also nichts kopiert!

    Um Deinen Zeiger dann auf das erste Zeichen nach der gesuchten Zeichenkette zu verschieben, kannst Du einfach Zeiger-Arithmetik verwenden:

    static const char *const MARKER = "<DataStream>";
    
    void my_function(const char *const input)
    {
        const char *data = strstr(input, MARKER);
        if(data == NULL)
        {
            return; /*not found*/
        }
    
        data += strlen(MARKER); /*move pointer *behind* "<DataStream>" tag*/
        now_decode(data);
    }
    


  • Es wird immer kurioser.

    Eigentlich war der thread über CStringW zu std:string. Jetzt sind wir bei strstr gelandet.

    Für mich hört sich die ganze Geschichte inzwischen nach "hab nicht wirklich einen Plan" an. Sowohl von strings und deren Darstellungsformen über base64 (no rocket science) bis zu ich-weiß-nicht-was-noch-alles.



  • Ja ich habe bisher nur Java programmiert da habe ich solche Probleme nicht.

    Aber vielen Dank für eure Antworten. Durch den Hinweis der Standardbiobliothek habe ich mir das nötige Wissen angeignet und es gleich direkt auch const char* umgebaut. Einzige kopie die ich noch mache ist von const char* zu std:string weil ich keine Zeit mehr habe die Base64 funktion selber zu schreiben. Ich möchte hier lieber die Library verwenden um Fehler zu vermeiden.

    Vielen Dank an die anderen, Ihr habt mir sehr geholfen. Auch wenn es ein wenig weg vom Thema ging.

    Zu meinen CFile Problem:
    Hmm der Benutzer kann jeden beliebigen Ordner angeben. Ich prüfe jetzt nach dem Schreiben ob das File existiert. Wenn nicht gebe ich aus keine Schreibrechte. Das ist jetzt erst mal eine Notlösung.



  • lulu32_o schrieb:

    Zu meinen CFile Problem:
    Hmm der Benutzer kann jeden beliebigen Ordner angeben. Ich prüfe jetzt nach dem Schreiben ob das File existiert. Wenn nicht gebe ich aus keine Schreibrechte. Das ist jetzt erst mal eine Notlösung.

    Hat es einen speziellen Grund wieso Du mit der** CFile Klasse aus der MFC-Bibilothek und nicht einfach mit einem Standard C++ std::ofstream **arbeitest?

    Außerdem: Wie geanu öffnest Du denn die Datei? Wenn Du es wie folgt machst (Beispiel aus der MFC-Doku übernommen), sollte doch eine Exception fliegen, sofern etwas schief geht beim Öffnen...

    CFile f;
    CFileException e;
    TCHAR* pszFileName = _T("Open_File.dat");
    if(!f.Open(pszFileName, CFile::modeCreate | CFile::modeWrite, &e))
    {
       TRACE(_T("File could not be opened %d\n"), e.m_cause);
    }
    

    Oder als** std::ofstream **Variante:

    std::ofstream ofs;
    ofs.open ("test.txt", std::ofstream::out | std::ofstream::trunc);
    if(!ofs.good())
    {
        fprintf(stderr, "ERROR: Failed to open the file!\n");
        abort();
    }
    

    Und falls die Datei in einem "geschützten" Ordner angelegt wird, denk an die Hinweise zum VirtulaStore 😉



  • Wenn man ein CFile mit dem Konstruktor aufmacht fliegt eine Exception.
    Wenn man es über .Open aufmacht kommt bloss false zurück.
    Beim Versuch in das nicht-offene CFile zu schreiben sollte dann aber wieder ne Exception fliegen.



  • lulu32_o schrieb:

    Ja ich habe bisher nur Java programmiert da habe ich solche Probleme nicht.

    Du meinst weil Du bei Java keine Wahl hast und das Umkopieren nicht zu sehen ist ?


Anmelden zum Antworten