Unterschied der Methoden get und put bei Zeichensätzen?



  • Hallo Leute!

    Bin Wiedereinsteiger und Anfänger in c++, bitte Text ganz durchlesen!

    Bei der Portierung meiner Programme von c auf c++ sind mir einige gravierende
    Unterschiede beim Handling der ein- und ausgelesenen Zeichen aufgefallen. Ein
    simples Verschlüsselungsprogramm schluckt die Algorithmen nicht mehr.

    Der Grund liegt darin, das fgetc und fputc die Zeichen als int-Wert einlesen,
    bei c++ dagegen als char, also signed char-Wert.

    Zunächst der Abschnitt in c, danach der in c++:

    zeichen = fgetc(fquelle); /* slei=Zeichenenmenge beim einlesen */
           if (zeichen == EOF) break;
           // naunco = "Nachricht uncodiert"
           if (kodierart == 0)
            {
             dummy = (unsigned char)zeichen + schluessel[codze];
             if (dummy > 255) dummy -= 255;
            }
           if (kodierart == 1)
            {
             dummy = (unsigned char)zeichen - schluessel[codze];
             if (dummy < 0)   dummy += 255;
            }
           fputc(dummy, fziel);
           codze++;
           if (codze >= codelaenge - 1)
            codze = 0;
    

    Selbstverständlich werden in C und in c++ beiden Dateien im Binärmodus
    geöffnet.
    Hier nun der angepasste Abschnitt in c++:

    dat_ein.get(inpletter);
         if (dat_ein.eof()) break;
         if (kodiermodus == 0)
          {
           outletter = inpletter + schluessel[codezaehler];
           if (outletter > 127) outletter -= 127;
          }
         if (kodiermodus == 1)
          {
           outletter = inpletter - schluessel[codezaehler];
           if (outletter < -128)   outletter += 128;
          }
         dat_aus.put(outletter);
         codezaehler++;
         if (codezaehler >= codelaenge - 1)
          codezaehler = 0;
    

    Nun die Rück-codierten Dateien. Zunächst der zu verschlüsselnde Quelltext:

    1234567890ß´#
    qwertzuiopü+
    asdfghjklöä
    yxcvbnm,.-
    

    Hier sieht man, das der Text Umlaute enthält. zunächst das Ergebnis der
    Rückcodierung in c:

    1234567890ß´#
    qwertzuiopü+
    asdfghjklöä
    yxcvbnm,.-
    

    Wie man sieht, ist die Kodierung einwandfrei möglich. Mit und ohne
    Sonderzeichen, egal ob in der Quelldatei oder im Schüssel. So wie es
    eigentlich sein sollte.
    Nun das Ergebnis in c++:

    1234567890CB4#
    qwertzuiopC<+
    asdfghjklC6C$
    yxcvbnm,.-
    

    Geplant wird später zusätzlich eine Bittransposition. Dazu wird eine Datei, die 16384 Zahlen, je eine von 0 bis 16383 enthält, erstellt.
    Diese werden Zahlen durcheinander gewirbelt. Die Zahl der Möglichen Permutationen liegt bei 16384 faktorielle. Das dürfte in jedem Fall
    jeden Knackversuch Stand halten. Danach wird die zu codende Datei häppchenweise als eindimensionales Array geladen und die
    Schaltzustände der Bits mit Hilfe der Datei mit den 16384 Zahlenwerten vertauscht. Die Mögliche Verschlüsselungstiefe hängt von der
    Mengenverteilung zwischen Highbits und Lowbits ab. Siehe

    Formel von Euler: n = { (a+b)! /(a! *b!)}
    

    Bei 50-prozentiger Verteilung ergibt das folgende Tabelle:

    Bytezahl... Bitzahl .......mögliche Permutationen
    Nibbel........4............6
    1.............8............70
    2.............16...........12870
    4.............32...........6.0108E+8
    8.............64...........1.83262E+18
    16............128..........2.39511E+37
    32............256..........5.76866E+75
    64............512..........4.72553E+152
    128...........1024.........4.48125e+306
    256...........2048.........5.69709e+614
    512...........4096.........1.30195e+1231
    1024..........8192.........9.61516e+2436
    2048..........16384 .......7.41605e+4929

    Weitere Versuche ergaben:
    4096..........32768........?????? (ca. 45 Sekunden um die Zahlenfolge herzustellen)
    8192..........65536........?????? (Herstellungszeit des Codes: 75 Sekunden bis 3Minuten)
    16384.........131072.......?????? (Herstellungszeit des Codes: 15-25 Minuten!!!!!)

    Anzumerken ist, das Linux ja einen linearen Speicher hat, theoretisch könnte
    man auch sämtliche Bits verschlüsseln. Leider ergaben meine Versuche jedoch,
    das die Herstellungszeit der permutierten Zahlenfolgen exponentiell ansteigt.
    Siehe obere Liste.

    Mein altes c-Programm schluckt
    das alles einwandfrei und ist bereits fertig und getestet worden.
    Aber leider nur für 32 Bit-Windows-Systeme tauglich. Den Code dazu
    möchte ich hier aus allen sicherlich verständlichen Gründen nicht veröffentlichen. Dazu muss aber die Handhabung der einzelnen Bytes
    einer Datei einwandfrei funktionieren. Dies geht nur, wenn die Bytes
    als unsigned char vorliegen. Bei signed char funktioniert das leider
    nicht, mußte ich feststellen. Dieses Programm ist nur der Testfall zum
    üben.
    Das ganze soll dann unter GPL ins Netz gestellt werden.

    Meine Frage:
    Gibt es eine Möglichkeit in c++ die Bytes von vorn herein als unsigned char
    einzulesen und abzuspeichern(siehe obrige Ausgabefehler), oder muß ich in den
    sauren Apfel beißen und zumindest alles trotzdem in c programmieren?
    Wer kann mir da helfen?
    Ansonsten: Frohes Fest!



  • Nimm drei gute C++-Bücher und lerne von vorne neu. Schau in der FAQ dieses Forums nach guten Titeln und nicht bei Amazon-Rezensionen.

    Lass das Portieren! Diese kleinen Programme sind bloß Fingerübungen, wovon keins länger als ne viertel Stunde zum Proggen braucht. Portieren dauert viel länger und es kommt kacke raus, wenn Du Dich am alten Stil festhältst.



  • Welchen Typ haben zeichen/dummy bzw. inpletter/outletter? Die fstream::get Funktion gibts auch mit int als Rückgabewert und auch als Variante mit Parameter für signed und unsigned char. Wenn du einfach unsigned char für alles benutzt, dann kann man sich deine Anpassungen wie if (dummy > 255) dummy -= 255; sparen, da sowieso alles Modulo 256 gerechnet wird.



  • Ach und mir fällt auch gerade dein Fehler auf. Du addierst die falschen Werte, in beiden Fällen! In der C Version möchtest du eigentlich 256 addieren/subtrahieren und in der C++ Version auch! Wenn du 127/128 addierst dann wird der Bereich [-128,-1] nach [0,127] verschoben. Dieser Bereich ist aber bereits belegt. Du möchtest eigentlich nach [128,255] verschieben.



  • Bei mir streikt der compiler wenn ich ios::get nicht mit char, sondern mit
    unsigned char betreibe. Jetzt wandle ich die Typen einfach mit Addition,
    bzw. mit Substraktion in die jeweilig gewünsten Tyen um.
    Ich dachte, es geht auch für schreibfaule wie mich.In meinen C++ Büchern
    werden die Methoden get und put (nur mit char) ganz kurz angerissen. Der Rest
    wird für Strings verbraucht. Auf Bitoperationen zur Verschlüsselung
    wir in keinen Buch eingegangen, das ich kenne.

    Es wurmt mich nur, das die Sonderzeichen so schwierig in c++ gehandhabt
    werden müssen.

    Alle Beispiele die ich im Netz fand, waren mit char (signed char), keines
    mit int oder unsigned char geschrieben. Beispiele mit unsigned char oder int
    für get und put bei c++ habe ich noch nicht gesehen. Ist das Abhängig vom Compiler?



  • Sorry hab in der Doku ein 'nicht' überlesen. Die get Funktion wo man das Zeichen als Parameter kriegt ist doch nicht überladen für unsigned/signed char. Aber die Variante ohne Parameter hat einen int als Rückgabewert. Damit könntest du sowas schreiben:

    unsigned char c = dat_ein.get();
    


  • Shit happens, said Forest Gump. ******* Kann passieren. Ich habs gelöst:

    Ich habe mir zwei kleine Funktionen geschrieben:

    //Umwandlung von signed char zu int
    int chartouint(char letter)
    {
     int rewer;
     if ( letter < 0)
      rewer = letter + 256;
       else rewer = (int)letter;
     return rewer;
    }
    
    //Umwandlung von signed char zu int
    char uinttochar(int letter)
    {
     char rewer;
     if ( letter > 127)
      rewer = letter - 256;
       else rewer = (int)letter;
     return rewer;
    }
    

    die Umwandlung findet nun wie folgt statt:

    void TestDatei::codedecode(void)
    {
       int codze, slei, intkey;
    
        QMessageBox *msgBox = new QMessageBox(this);
    
       std::fstream dat_ein;  // std::fstream
       std::fstream dat_aus;  // std::fstream
    
        /* Initialisiert die Dateilänge, einser, und Nuller mit Null, um richtig zäehlen zu können*/
        dateilaenge = 0;
        einser = 0;
        nuller = 0;
        codze  = 0;
    
        char *slussel = schluesselcode.toAscii().data();  // kopiert einen QString in einen ASCII-String
    
        char *finam = datnam.toAscii().data();  // kopiert einen QString in einen ASCII-String
        dat_ein.open(finam, std::fstream::in | std::fstream::binary);
        char *zielfinam = zieldateiname.toAscii().data();  // kopiert einen QString in einen ASCII-String
        dat_aus.open(zielfinam, std::fstream::out | std::fstream::binary);
    
        dat_ein.seekg(0, std::fstream::end);
        dateilaenge = dat_ein.tellg();
        dat_ein.seekg(0, std::fstream::beg);
    
       if (!dat_ein )
        {
         msgBox->setText("Datei konnte nicht geoeffnet werden!");
         msgBox->show();
        }
    
        for (slei = 0; slei < dateilaenge; slei++)
        {
         dat_ein.get(inpletter);
         if (dat_ein.eof()) break;
         einletter = atouint(inpletter);
         dum = atouint(slussel[codze]);
         if (kodiermodus == 0)
          erge = einletter + dum;
         if (kodiermodus == 1)
          erge = einletter - dum;
         if (erge > 255) erge -= 255;
         if (erge < 0)   erge += 255;
         outletter = uinttochar(erge);
         dat_aus.put(outletter);
         codze++;
         if (codze >= codelaenge)
          codze = 0;
        }
    
       dat_ein.close();
       dat_aus.close();
    
       msgBox->setText("Datei wurde gecodet und abgespeichert!");
       msgBox->show();
    
    }
    

    Ist zwar mit QT geschrieben, aber für c++ durchaus brauchbar.
    Ich schreibe wenn es geht zuerst die c++ Konsolenanwendung,
    danach erst wird (wenn ich Lust dazu habe) eine QT-Version
    geschrieben. Soll ja mit 50 Jahren auf dem Buckel kein neuer
    Beruf werden (Was Gott verhüten möge, nach seinem Willen) es
    soll ein Hobby bleiben.



  • Gibt es irgendwo eine online-Dokumentation, für die Methode ios:get()?
    bei mir sind nur Beispiele und Angaben mit dem Typ char gegeben:

    Autor: Stanley B. Lippman
    Titel: C++ Einführung und Leitfaden
    Verlag: Addison Wesley
    ISBN: 0-201-54848-8

    Autor: Andre Willms
    Titel: C++ Programmierung
    Verlag: Addison Wesley
    ISBN: 3-8273-1152-7

    Autor: Dirk Louis
    Titel: C/C++ Kompendium
    Verlag: Markt&Technik
    ISBN: 3-8272-5386-1

    Autor: Ulla Kirch-Prinz / Peter Prinz
    Titel: C++ Lernen und professionell anwenden
    Verlag: mitp
    ISBN: 3-8266-1534-4

    Autor: Helmut Erlenkötter
    Titel: C++ Objektorientiertes Programmieren von Anfang an
    Verlag: rororo
    ISBN: 3-499-60077-3

    Autor: Kaare Christian
    Titel: Programmierung mit C++
    Verlag: Microsoft Press
    ISBN: 3-86063-310-4

    Autor: Stephen Randy Davis
    Titel: C++
    Verlag: mitp
    ISBN: 3-8266-3151-X

    Autor: Burkard Lehner
    Titel: KDE- und QT-Programmierung GUI-Entwicklung für Linux
    Verlag: Addison-Wesley
    ISBN: 3-8273-1753-3

    Tip:
    Bei Wohnungsauflösungen/Wertstoffhöfen/Flohmärkten
    lassen sich solche Sachen hamstern.



  • rustyoldguy schrieb:

    if (erge > 255) erge -= 255;
         if (erge < 0)   erge += 255;
    

    Hier muss es auch 256 sein.

    rustyoldguy schrieb:

    Gibt es irgendwo eine online-Dokumentation, für die Methode ios:get()?

    Ja es gibt zwei Online Referenzen die man bei Google ständig findet:
    http://www.cplusplus.com/reference/istream/istream/get/
    http://en.cppreference.com/w/cpp/io/basic_istream/get



  • Der Typ get() mit Rückgabewert int ist für C-Programmierung. Die anderen Datentypen sind für char. Der "Notnagel" hat geholfen. Mal schaun, wie es
    Weitergeht.

    Danke!


Log in to reply