WEBCGI-Projekt | Verbesserungsvorschläge



  • Hallo zusammen,

    für das Fach Programmieren haben wir ein Projekt angefangen, welches sich mit WEB-CGI befasst. Wir sollen einen Online-Compiler entwickeln... Soweit so gut.

    Mittlerweile sind wir fast fertig, es fehlt eigentlich nurnoch mein letzter Part... Ich soll eine Funktion schreiben, die aus einem Text einen Teilstring findet und diesen ersetzt.

    Ich habe die Funktion mittlerweile fertig, brauche aber unbedingt noch Verbesserungsvorschläge. Was hab ich mir hier zu schwer / zu leicht gemacht, oder was kann man generell besser machen? Ist das überhaupt C++ und nicht ANSI C?

    #include <iostream>
    #include <string>
    using namespace std;
    
    char * suche(char * text, char * wort, char * ersetze)
    {
    	char * suchergebnis; // Hier wird spaeter nach (char * wort) gesucht
    	char * neuertext; // Der neue Text
    
    	int laenge = (strlen(text) - strlen(wort) + strlen(ersetze)) + 1; // Laenge von (char * neuertext)
    	int i, j; // Zaehler
    
    	neuertext = new char [laenge]; // dynamisch Speicher anfordern für (char * neuertext)
    
    	suchergebnis = strstr(text, wort); // Es wird nach (char * wort) gesucht
    
    	if (NULL == suchergebnis) return NULL; // Wort gefunden?
    
        for(i = 0; i < (strlen(text)-strlen(suchergebnis)); i++)
    	{
    		neuertext[i] = text[i]; 
    	}
    	/* In dieser ersten Schleife kopieren wir den Ursprungstext
           bis zum ersten Vorkommen von (char * wort) in den neuen Text */
    
        for(j = 0; j < strlen(ersetze); j++, i++)
    	{
    		neuertext[i] = ersetze[j];
    	} 
    	/* Die zweite Schleife kopiert das Ersatzwort ab dem ersten Vorkommen
           von (char * wort) in den neuen Text */
    
    	for(j = strlen(text) - (strlen(suchergebnis) - strlen(wort)); text[j] != '\0'; i++, j++)
    	{
    		neuertext[i] = text[j];
    	}
    	/* Zu guter letzt wird der Rest des Ursprungstextes nach (char * wort) in
           den neuen Text kopiert */
    
    	return neuertext; // Rueckgabe
    }
    
    int main()
    {
    	char text[] = "<title>((TITLE))</title>"; // Ursprungstext
    	cout << suche(text, "((TITLE))", "Der Online-Compiler!") << endl << endl; // Funktionsaufruf
    
        system("PAUSE");
    	return 0;
    }
    

    Vielen Dank schonmal
    Kevin


  • Administrator

    Ob es C++ oder C ist?
    Also wenn man new und das nicht vorhandene delete[] (-> Speicherleck) durch malloc und free, cout und endl durch printf, <string> durch <string.h> und <iostream> durch <stdio.h> ersetzt, dann hast du ein vollständiges C Programm.
    Anders gesagt, du hast hier so gut wie kein C++ benutzt und erzeugst zudem noch ein Speicherleck. 🙂

    Verbesserungsvorschläge?
    Verwende die Klasse std::string :
    http://www.cplusplus.com/reference/string/string/

    Da gibt es eine Funktion replace :
    http://www.cplusplus.com/reference/string/string/replace.html

    Auf system sollte man verzichten, aber ich nehme mal an, dass du es nur drin hast, um zu testen. Hier andere Möglichkeiten um das automatische schliessen zu verhindern und da drin ist auch ein Link, wieso man auf system verzichten sollte:
    http://www.c-plusplus.net/forum/viewtopic-var-t-is-111042.html

    Ansonsten, wenn du dann den C++ Code hast, können wir ja mal weiterschauen. 🙂

    Grüssli



  • Vielen Dank erstmal für deine Antwort,

    also das sieht ja schonmal viel besser, unkomplizierter und kompakter aus mit Strings.

    #include <iostream>
    #include <string>
    using namespace std;
    
    string ersetze(string ursprungstext, string suchwort, string ersatzwort)
    {
    	string neuertext;
    
        neuertext = ursprungstext.replace(ursprungstext.find(suchwort), suchwort.length(), ersatzwort);
    
        return neuertext;
    }
    

    Funktionsaufruf in der Main folgendermaßen:

    ersetze("Foo Bar", "Foo", "Ich zahle gern in"
    

    Ausgabe:

    Ich zahle gern in Bar

    Jetzt gibt es allerdings ein Problem...
    cin.getline funktioniert nicht mit Strings sondern nur mit char-Arrays, und cin liest keine Whitespace-Zeichen (Leerzeichen)

    int main()
    {
    	string text, suchwort, ersatzwort;
    
    	cout << "Text:\t";
    	cin.getline(text, 200000);
    
    	cout << "Suchwort:\t";
    	cin.getline(suchwort, 50);
    
    	cout << "Ersatzwort:\t";
    	cin.getline(ersatzwort, 50);
    
      cout << ersetze(text, suchwort, ersatzwort) << endl << endl;
    
    	return 0;
    }
    

    Hier wird ein Fehler ausgespuckt...

    21 E:\Programme\Dev-Cpp\main.cpp no matching function for call to `std::basic_istream<char, std::char_traits<char> >::getline(std::string&, int)'

    Wie kann ich also jetzt einen Text einlesen?

    Oder soll ich von char in String konvertieren? (wie das geht, weiß ich leider auch noch nicht, googlen wäre aber kein Problem)

    MfG,
    Kevin


  • Administrator

    Im Header <string> wird eine freie Funktion mit dem Namen std::getline definiert:
    http://www.cplusplus.com/reference/string/getline.html

    Die könnte hilfreich sein 😉

    Grüssli



  • Habe ich auch gerade gefunden,
    trotzdem vielen Dank, warst sehr hilfreich! 🙂

    Ist mein Programm denn jetzt in C++, oder immernoch C?

    Mit freundlichen Grüßen
    Kevin


  • Administrator

    root2k schrieb:

    Ist mein Programm denn jetzt in C++, oder immernoch C?

    Jetzt ist glaub ich alles C++, soweit ich das erkennen kann. Und nun gebe ich auch gerne ein paar Ratschläge 🙂

    #include <string>
    #include <iostream>
    
    // using namespace std;
    // Ich würde mit dem hier vorsichtig umgehen.
    // Hier kein Problem, aber nie damit in einen Header,
    // und daran denken, dass man damit Namenskonflikte
    // hervorrufen kann.
    
    // Ich nehme nun die Variablen in englisch, klingt irgendwie
    // besser mit all den englischen Schlüsselwörter :)
    
    // Deklaration (auch mehr einfach aus Stilgründen):
    std::string replace(
      std::string const& text,
      std::string const& searchTerm,
      std::string const& replacement);
    
    // Die konstanten Referenzen sind aber empfehlenswert.
    // So wird nicht eine Kopie gemacht, sondern mit dem gleichen
    // Objekt gearbeitet, welches von Aussen kommt. Es ist allerdings
    // garantiert, dass dieses Objekt nicht verändert wird.
    
    int main()
    {
      std::cout << "Text:\t";
    
      std::string text; // In C++ kann man eine Variable auch erst dann
                        // deklarieren, wenn man sie braucht.
      std::getline(std::cin, text);
    
      std::cout << "Suchwort:\t";
    
      std::string searchTerm;
      std::getline(std::cin, searchTerm);
    
      std::cout << "Ersatzwort:\t";
    
      std::string replacement;
      std::getline(std::cin, replacement);
    
      std::cout << replace(text, searchTerm, replacement)
                << '\n'
                << std::endl;
    
      // std::endl fügt nicht nur eine neue Linie ein,
      // sondern führt auch noch ein flush durch.
      // Deshalb wurde das erste std::endl durch ein '\n',
      // bzw. Linefeed, ersetzt.
    
      return 0;
      // return 0 ist bei der main Funktion nicht unbedingt nötig.
    }
    
    std::string replace(
      std::string const& text,
      std::string const& searchTerm,
      std::string const& replacement)
    {
      // Kopie von text erstellen, da wir auf diesem nicht arbeiten können.
      // Es ist ja eine konstante Referenz.
      std::string result = text;
    
      // Wir verändern result und geben es danach zurück.
      return result.replace(
                      text.find(searchTerm),
                      searchTerm.length(),
                      replacement);
    }
    

    So wird übrigens nur das erste Vorkommen von searchTerm ersetzt. Was dein ursprünglicher Code allerdings auch gemacht hat. Nur ist das auch gewollt? Sonst müsstest du es halt in einer Schlaufe durchlaufen lassen.

    Grüssli



  • Wooooow!

    Danke für deine Tipps, ich denke die stilvolle Programmierung ist ein neuer Weg bzw. eine neue Herausforderung für mich, die ich mir auf jeden Fall aneignen sollte. Wir haben es leider nicht anders beigebracht bekommen, auch in meinem C++ Buch steht es nicht anders 😞 Kannst du mir eventuell ein gutes Buch empfehlen, welches ein wenig Stil in die Sache bringt?

    Vielen Dank!

    Kevin


  • Administrator

    root2k schrieb:

    ...

    Stil ist eine ganz eigene Sache, deswegen habe ich es auch gekennzeichnet, da es eher Nebensächlich ist. Es gibt nicht DEN Stil, im Gegenteil, alle sind anderer Meinung, was den richtigen Stil betrifft. Und wenn man darüber anfängt zu diskutieren, werfen sich die Leute am Ende Granaten an die Köpfe 🙂

    Grundsätzlich denke ich, kann man ein paar Grundpunkte nennen:
    1. Dein Stil sollte einheitlich sein.
    2. Den Code nach Scopes einrücken.
    3. Eine Zeilenbreite zwischen 80 - 120 Zeichen anvisieren.
    4. Der Stil muss für dich oder dein Team stimmen. Er sollte die Lesbarkeit unterstützen. Ein guter Stil hast du meiner Meinung nach dann, wenn du ein Projekt während einem Jahr auf die Seite legst und danach dich leicht wieder in den Code einarbeiten kannst, da dein Code gut strukturiert ist.
    5. Ein Stil entwickelt sich über die Zeit und man sollte sich nicht scheuen, seinen Stil mal etwas anzupassen und/oder verändern.

    Ein Buch zu C++ Stil gibt es in diesem Sinne glaube ich nicht. Aber ich glaube dir würde so oder so ein C++ Buch für die Grundlagen gut tun. Für Grundlagen werden hier oft die folgenden zwei empfohlen:
    - C++ Primer
    - Thinking in C++ 1 & 2

    Grüssli



  • Hallo nochmal und danke,

    eine Frage habe ich da noch - wie schaffe ich es, eine Fehlermeldung auszugeben, wenn das Suchwort nicht gefunden wurde? Im Moment beendet er das Programm dann einfach...

    Probiert habe ich schon folgendes...

    std::string replace(
      std::string const& text,
      std::string const& searchTerm,
      std::string const& replacement)
    {
      std::string result = text;
      std::string errormsg = "Das gesuchte Zeichenfragment wurde nicht gefunden.";
    
      if ( text.find(searchTerm) == NULL ) return errormsg;
    
      return result.replace(
                      text.find(searchTerm),
                      searchTerm.length(),
                      replacement);
    }
    

    Doch auch hier wird das Programm einfach beendet. Was gibt text.find(searchTerm) == NULL aus, wenn nichts gefunden wurde?

    MfG, Kevin



  • Du solltest dich mit den Funktionen, die du verwendest, ein wenig vertraut machen (z.B. hier). 😉

    Du musst nämlich nicht auf NULL prüfen... Ausserdem solltest du find() besser nicht zwei Mal aufrufen, da es dann nochmals von vorne mit O(n) im String sucht.

    Um einen Fehler kennzuzeichnen, gibt es in C++ verschiedene Möglichkeiten. Entweder du handhabst ihn gleich lokal, d.h. gibst eine Fehlermeldung aus und erwartest nochmals eine Eingabe (durch eine Schleife, die du erst bei gefundenem Eintrag beendest). Oder du verschachtelst das Ganze in eine Funktion und prüfst den Rückgabewert. Oder du verwendest Exceptions, die du dann irgendwo fangen musst.



  • std::string replace(
      std::string const& text,
      std::string const& searchTerm,
      std::string const& replacement)
    {
      std::string result = text;
    
      std::size_t errorchk = text.find(searchTerm);
    
      if ( errorchk == std::string::npos )
         return "Das gesuchte Zeichenfragment wurde nicht gefunden.";
    
      return result.replace(
                      errorchk,
                      searchTerm.length(),
                      replacement);
    }
    

    Habs jetzt so gelöst, denkst du das geht in Ordnung?


  • Administrator

    root2k schrieb:

    Habs jetzt so gelöst, denkst du das geht in Ordnung?

    Die Lösung ist eher schlecht. Man erwartet im Rückgabewert nicht eine Fehlermeldung, sondern das Resultat. Zudem ist es schwer diesen Rückgabewert zu prüfen, ob es nun einen Fehler gab. Ich persönlich würde gleich die Funktionalität von std::string::replace ausnutzen. Wenn std::string::find ein std::string::npos zurückliefert und man dieses an std::string::replace übergibt, dann wird eine std::out_of_range Exception geworfen. Dies kannst du in der main abfangen und entsprechend darauf reagieren.

    // replace wie ich es aufgezeigt hatte
    
    int main()
    {
      // ... bekannter Code
    
      try
      {
        std::cout << replace(text, searchTerm, replacement)
                  << '\n'
                  << std::endl;
      }
      catch(std::out_of_range&)
      {
        // Sinnvolle Fehlermeldung ...
        std::cerr << "!!!FEHLER; FEHLER; FEHLER!!!" << std::endl;
    
        // SINNVOLLE! :)
      }
    
      return 0;
    }
    

    std::cerr ist im übrigen die Ausgabe für Fehlermeldungen. Dies geht an einen eigenen Puffer, welcher zum Teil mit dem von std::cout übereinstimmt. Wenn man die Ausgabe allerdings auf der Konsole umleitet, dann ist es gut möglich, dass das Zeug von std::cerr auf der Konsole erscheint, std::cout allerdings umgeleitet wird:
    http://www.cplusplus.com/reference/iostream/cerr.html
    http://www.cplusplus.com/reference/iostream/cout.html

    Grüssli



  • Schönen Dank für eure schnellen Antworten,

    habe das jetzt mal so gemacht, jedoch funktioniert das Kompilieren nicht...

    #include <string>
    #include <iostream>
    
    std::string replace(
      std::string const& text,
      std::string const& searchTerm,
      std::string const& replacement);
    
    int main()
    {
      std::cout << "Text:\t";
      std::string text;
      std::getline(std::cin, text);
    
      std::cout << "Suchwort:\t";
      std::string searchTerm;
      std::getline(std::cin, searchTerm);
    
      std::cout << "Ersatzwort:\t";
      std::string replacement;
      std::getline(std::cin, replacement);
    
      try
      {
        std::cout << replace(text, searchTerm, replacement)
                  << '\n'
                  << std::endl;
      }
      catch(std::out_of_range&)
      {
        std::cerr << searchTerm
                  << " wurde leider nicht im Text gefunden."
                  << std::endl;
      }
    
      system("PAUSE");
      return 0;
    }
    
    std::string replace(
      std::string const& text,
      std::string const& searchTerm,
      std::string const& replacement)
    {
      std::string result = text;
    
      return result.replace(
                      text.find(searchTerm),
                      searchTerm.length(),
                      replacement);
    }
    

    Compiler: Bloodshed Dev-C++

    E:\Programme\Dev-Cpp\main.cpp In function int main()': 30 E:\\Programme\\Dev-Cpp\\main.cppstd::out_of_range' has not been declared
    30 E:\Programme\Dev-Cpp\main.cpp invalid catch parameter
    E:\Programme\Dev-Cpp\Makefile.win [Build Error] [main.o] Error 1



  • Okay, das hat sich erledigt, in der Referenz steht, dass folgende Bibliothek eingebunden werden muss:

    #include <stdexcept>
    

    out_of_range - C++ Reference

    Danach hat es funktioniert... Vielen Dank nochmal! Sieht so sehr ordentlich aus


  • Administrator

    root2k schrieb:

    Compiler: Bloodshed Dev-C++

    Wenn ich noch einen Tipp geben darf. Wirf dieses Ding weg. Es gilt als veraltet und wird zudem nicht mehr weiterentwickelt. MAn sollte sich also davon lösen. Gratis C++ IDEs gibt es auch aktuellere:
    - Microsoft Visual Studio 2008 Express (ja, ist wirklich gratis! Und auf Windows sehr zu empfehlen.)
    - Code::Blocks - Platformunabhängig, kombinierbar mit gcc (g++ & gdb)

    root2k schrieb:

    Okay, das hat sich erledigt, in der Referenz steht, dass folgende Bibliothek eingebunden werden muss:

    Das nennt man übrigens nicht eine Bibliothek, sondern einen Header. Der Header gehört zur Standardbibliothek. Also eine Bibliothek ist immer eine Sammlung von Funktionen, Klassen usw., während ein Header Teil einer Bibliothek ist.

    Grüssli



  • Header, nicht Bibliothek. Okay, das kann ich mir merken. 🕶 😉

    Alles klar, hab Dev-C++ mal deinstalliert und im Gegenzug Microsoft Visual Studio 2008 Express heruntergeladen. Danke für den Tipp 👍

    MfG, Kevin



  • Dravere schrieb:

    Also eine Bibliothek ist immer eine Sammlung von Funktionen, Klassen usw., während ein Header Teil einer Bibliothek ist.

    Muss nicht sein. 😉
    Ein/mehrere Header kann/können sehr wohl eine ganze Bibliothek sein. (ist es meistens natürlich nicht so, dass sonst nichts migeliefert wird, aber so endgültige würde ich das nicht sagen)



  • Vielen Dank euch nochmal... Die Ersetzen-Funktion klappt einwandfrei. Leider darf ich momentan keine String-Klasse benutzen, da wir noch keine OOP gemacht haben und da erst noch zu kommen.

    Funktioniert aber trotzdem und ich bin den anderen ein wenig voraus 🙂

    Jetzt kommt das nächste Problem... Ich soll einen Parser schreiben, der Hexcode in Sonderzeichen rückwandelt... Der vom Benutzer eingegebene Quelltext hat ja Sonderzeichen. Diese werden aber bei der Übertragung in Hexcode umgewandelt ... z.B. das Ausrufezeichen "!" wird zu "%21" usw...

    Folgendes habe ich mal probiert (entschuldigt den unglaublich schlecht strukturierten Code, das mache ich immer nachdem alles funktioniert^^)

    #include <iostream>
    #include <string>
    #include <iomanip>
    #include <stdio.h>
    #include <stdlib.h>
    
    char hexToAscii(const char* code);
    char* changeText (char* text);
    
    int main()
    {
    	char* text = "Aber hallo%3f Das ist ja gut zu wissen%21";
    
    	std::cout		<< changeText(text)
    					<< "\n"
    					<< std::endl;
    }
    
    char * changeText (char * text)
    {
    	int i = 0, j = 0;
    	int searchResLen = 0;
    	int textLen = strlen(text);
    	char * searchRes;
    	char * newText = new char [textLen];
    
    	searchRes = strstr(text, "%");
    	searchResLen = strlen(searchRes);
    
    	for (; text[j] != NULL; i++, j++)
    	{
    		if (j == textLen-searchResLen)
    		{
    			newText[i] = hexToAscii(searchRes);
    			j += 2;
    		}
    		else
    		{
    			newText[i] = text[j];
    		}
    	}
    
    	newText[i] = '\0';
    
    	return newText;
    }
    
    char hexToAscii(const char* code)
    {
    	char hex[5], *stop;
    	hex[0] = '0';
    	hex[1] = 'x';
    	hex[2] = code[1];
    	hex[3] = code[2];
    	hex[4] = 0;
    
    	return strtol(hex, &stop, 16);
    }
    

    Ausgabe: Aber hallo? Das ist ja gut zu wissen%21

    Die Funktion hexToAscii bekommt von der Funktion changeText das Suchergebnis von strstr(text, "%") und gibt das hex-Array an die Funktion strtol weiter, welche dann das Sonderzeichen rückliefert.

    Das Programm funktioniert nun aber nur bis zum ersten Sonderzeichen. Danach nicht mehr... ich weiß warum es nicht funktioniert (ich müsste ja mehrmals Suchergebnisse haben), aber ich habe keine Ahnung wie ich es machen soll? Kann mir vielleicht jemand einleuchten?

    Danke schonmal
    der Kev


  • Administrator

    1. Neues Problem -> Neuer Thread
    Das ist auch für dich sinnvoll. Gibt viele Leute, welche ab einem gewissen Zeitpunkt in einem grössen Thread nicht mehr unbedingt reinschauen. Zudem ist es eine besser Ordnung, auch wenn jemand nach einem Problem sucht.
    2. Keine String-Klasse benutzen, kein OOP? Ok, ich versteh schon. Ein Lehrer, welcher seinen Schülern C with Classes als C++ verkauft. In C++ kann man völlig unabhängig von C Programmieren. Um C++ zu beherrschen, muss man nicht C verstehen. Grundsätzlich sind es unabhängig Sprachen. Aber naja, wir sind uns in dem Forum ja daran gewöhnt, dass wir immer wieder von solchen Lehrern hören. Die sind leider Standard ... Fortbildung ist halt immer so eine Sache bei den Leuten.
    3. Zu deinem Problem. Alles was du eigentlich machen müsstest ist, dass du changeText mehrmals aufrufst, bis alle Zeichen ersetzt sind. Du könntest auch sowas intern machen, also in changeText selber, in dem du immer wieder nach dem Prozentzeichen suchst, die Umwandlung machst usw., bis es kein Prozentzeichen mehr findet. Du solltest zudem noch dein Speicherleck schliessen. In der Funktion wird Speicher über new reserviert und nirgends per delete freigegeben.
    4. Die Leute im Forum werden mich womöglich lynchen. Ich finde aber du hast schon Initiative gezeigt und ich hatte gerade Lust, dazu was zu schreiben oder zusammenzustellen. Es ist nicht gerade ausserordentlich toll, da ich nicht so der C Programmierer bin, aber ich denke es dürfte interessant für dich sein:

    //#include <string>   -> Du benutzt ja keine C++ Strings.
    #include <iostream>
    //#include <iomanip>  -> brauchst du doch gar nicht.
    //                       std::endl ist in <iostream> drin
    //#include <stdio.h>  -> falsch, in C++ ist es <cstdio>
    //                       du benötigst es aber gar nicht.
    //#include <stdlib.h> -> falsch, in C++ ist es <cstdlib>
    #include <cstdlib>
    #include <cstring> // -> Um mit C-Strings zu arbeiten.
    #include <cassert> // -> Im Debugmodus kann man damit
    //                       Sicherheitsprüfungen durchführen.
    
    // Kopf bleibt gleich.
    char hexToAscii(char const* code);
    
    // Den Text übergibt man als konstant, er soll in der Funktion
    // schliesslich nicht verändert werden, sondern wird in einen
    // neuen Puffer kopiert.
    // Um Speicherlecks zu verhindern, muss der Anwender der
    // Funktion einen genügend grossen Speicherblock übergeben.
    // Dieser Puffer wird an dest übergeben.
    void changeText(char* dest, char const* text);
    
    int main()
    { 
    	// Man sollte ein Zeiger auf ein String-Literal
    	// als ein Zeiger auf konstanten Inhalt deklarieren.
    	// Es ist nämlich auch der Fall.
    	// Dass es anders geht, liegt nur an einer rückwärts-
    	// kompatibilität zu C.
    	char const* text = "Aber hallo%3f Das ist ja gut zu wissen%21";
    
    	// Wir erstellen uns nun ausserhalb Speicher,
    	// damit wir ihn auch wieder sauber aufräumen können.
    	// Nicht vergessen, zu dem new kommt ein delete.
    	// Der neue Text wird sicher kürzer oder gleichlang sein
    	// wie der alte Text. Also definieren wir einen
    	// Puffer, mit gleicher Grösse.
    	// strlen zählt die Null-Terminierung nicht mit,
    	// deshalb zählen wir noch eins dazu.
    	// Dies ist nötig, falls es keine zu ersetzenden
    	// Hexadezimalzahlen hat. Es gäbe es einen Überlauf
    	// mit undefiniertem Verhalten.
    	char* buffer = new char[strlen(text) + 1];
    
    	// Text wird geändert und in buffer geschrieben.
    	changeText(buffer, text);
    
    	// Der Puffer wird ausgegeben. changeText hat eine
    	// terminierende 0 reingeschrieben, wodurch wir
    	// den Puffer als C-String behandeln können.
    	std::cout << buffer << std::endl;
    
    	delete[] buffer; // <- Das wichtige Delete, welches
    	                 // vorhin fehlte.
    }
    
    // Zu den Funktionen :)
    
    void changeText(char* dest, char const* text)
    {
    	// <cassert> wird hier z.B. verwendet.
    	// Man hat ein Makro assert, welches etwas prüft
    	// und wenn es den Wert false ergibt, bricht das Programm ab.
    	// Diese Prüfung wird nur im Debugmodus durchgeführt.
    	assert(0 != dest && 0 != text);
    
    	// Es wird also geprüft,
    	// ob beide Zeiger keine Nullzeiger sind.
    	// Man könnte hier durchaus noch mehr Prüfungen machen,
    	// bzw. auch im Verlauf der Funktion.
    
    	// Wir müssen nun also kopieren und wenn wir
    	// ein '%' finden, müssen wir entsprechend dieses
    	// Zeichen ignorieren und die nächsten zwei Buchstaben
    	// als Hexadezimalziffern interpretieren. Richtig?
    
    	// Dies machen wir so lange, bis wir am Ende von text
    	// angelangt sind. Also das null terminierende Zeichen
    	// erreichen.
    
    	// Ich gebe dies nun explizit in der Bedingung an,
    	// damit man es gut sieht. Gründsätzlich würde aber auch
    	// ein *text reichen. '\0' entspricht dem Wert 0, was
    	// als false interpretiert würde. Alles andere als 0
    	// ist in C++ true.
    	while('\0' != *text)
    	{
    		// Hexadezimalziffer?
    		if('%' == *text)
    		{
    			// Ja, dann müssen wir übersetzen.
    
    			// Zuerst gehen wir eine Stelle weiter.
    			++text;
    
    			// Hier ist nun die erste Hexadezimalziffer.
    			// Wir übergeben es an deine Funktion und
    			// speichern das Resultat in die aktuelle
    			// Stelle von dest.
    			*dest = hexToAscii(text);
    
    			// text muss nun um eine weitere Stelle
    			// nach vorne geschoben werden.
    			// Die letzte Hexadezimalziffer, wird am
    			// Ende der Schleife noch übersprungen.
    			++text;
    		}
    		else
    		{
    			// Nein, also kopieren wir nur.
    			*dest = *text;
    		}
    
    		// Die beiden Zeiger um eine Stelle weiterschieben.
    		++text;
    		++dest;
    	}
    
    	// Wir fügen noch eine terminierende 0 beim Puffer ein.
    	*dest = '\0';
    
    	// Fertig.
    	// Gemerkt? Wir haben keine zusätzlichen Variablen benötigt.
    	// Nicht mal Funktionen aus der C-Bibliothek. Oder nur indirekt.
    }
    
    char hexToAscii(const char* code)
    { 
    	// Nur aus Sicherheit ein assert.
    	assert(0 != code);
    
    	// Nun, diese Funktion ändere ich ein wenig.
    	// Am liebsten würde ich die C++ Konvertierung verwenden,
    	// aber dann wird sicher wieder gemotzt, dass es OOP ist.
    	// Dann verwende ich eben die C Möglichkeiten.
    
    	// Dazu ist dein Code eigentlich schon ganz gut.
    	// Allerdings ist das 0x zu begin optional, da über die
    	// Basis 16 schon angegeben wird, dass Hexadezimalziffern
    	// gemeint sind. Daher können wir das weglassen.
    	// Auch ist der zweite Parameter von strtol optional.
    	// Ein NULL reicht dort völlig. Steht übrigens alles
    	// in der Referenz, welche du auch schon benutzt hast ;)
    
    	// Wir benutzen zudem die sofortige Initialisierung.
    	char hex[3] = { code[0], code[1], 0 };
    
    	// Den Rückgabewert konvertieren wir explizit mit
    	// dem C++ static_cast. Dies soll nur die Sache
    	// verdeutlichen, also was hier eigentlich passiert.
    	return static_cast<char>(strtol(hex, NULL, 16));
    }
    
    // Übrigens wird in C und teilweise C++ NULL nur für einen
    // Nullzeiger verwendet. Man sollte also NULL nicht für 0
    // oder '\0' verwenden. In C++ gibt es zudem auch noch die
    // Möglichkeit ein nullptr Objekt zu bauen. Näher steht 
    // in der FAQ. Aber da ihr ja nicht richtiges C++ macht,
    // dürft ihr das sicher auch nicht verwenden. Kommt übrigens
    // in den nächsten Standard sogar als Schlüsselwort rein.
    

    5. Ich hoffe ich habe keine groben Fehler gemacht.
    6. Zwei Links, zu Einträgen in der FAQ, welche ich erwähnt habe:
    FAQ - C++ - NULL oder 0 (eher veraltet)
    FAQ - C++ - nullptr (neu)
    7. Erwarte nicht, dass sowas zum Standard bei der Hilfestellung wird. Sonst dürftest du von diesem Forum enttäuscht werden. Ich glaube, ich habe gerade ein wenig übertrieben 😃

    Grüssli



  • Daumen hoch für den Beitrag! Hoffentlich macht er was draus, erklären kannst du jedenfalls sehr gut 👍


  • Administrator

    Noch eine kurze Bemerkung. Habe meinen Code noch korrigiert. Mir ist aufgefallen, dass ich vergessen habe, dass strlen die Länge des C-Strings ohne die Null-Terminierung zurückgibt. Wenn also nichts ersetzt hätte werden müssen, wäre es zu einem Überlauf mit undefiniertem Verhalten gekommen, da die Null-Terminierung des Puffers in Speicher geschrieben worden wäre, welcher nicht reserviert wurde. Deshalb hat es nun ein +1 drin und deshalb wurde mein Post editiert.

    @Badestrand,
    Ehm, danke.
    *froh ist dem Galgen entkommen zu sein* 😃

    Grüssli


Anmelden zum Antworten