Merkwürdigkeiten bei STL::MAP -Erklärung und Hilfe benötigt :)



  • ich hab ein Vokabeltrainer geschrieben und hab das mit stl::map gemacht- weils schön iss 😉 . allerdings hab ich da nee merkwürdigkeit
    hab zwei maps erstellt und wenn ich erst englische listing ausführe also "1" (zeigt er englische listing an) aber wenn ich dann "2" ausführe zeigt er mir das deutsche listing nicht mehr an. warum 😕
    komisch oder??

    [b]coding wegen Übersichtlichkeit nach unten verschoben..wurde editiert[/b]
    

    siehe auch kommentare ... hilfe erbeten und wäre dankbar mit einen mentalen dankeschön 😃
    gruss sten

    ps. wenn ich grad dabei bin ... gibt es auch nee möglichkeit lexikographisch zu sortieren, dh. nicht nur rein nach ASCII weil bei der map-sortierung geht es nur ASCII- mässig ..



  • Erstmal solltest du die map(s) nur einmal einlesen und dann im Hauptprogramm lagern - an die statischen Variablen kommt nämlich niemand als die jeweiligen Funktionen heran.

    Zweitens steht der Lesezeiger deiner Wörterbuch-Datei am Ende der Print_Eng() auf deren Dateiende. Das heißt, bevor du irgendwas sinnvolles mit der Datei machen kannst, mußt du deren Fehlerflag aufräumen (clear()) und sie wieder zurückdrehen auf Anfang (seekg(0) oder rewind()).



  • CStoll schrieb:

    Erstmal solltest du die map(s) nur einmal einlesen und dann im Hauptprogramm lagern - an die statischen Variablen kommt nämlich niemand als die jeweiligen Funktionen heran.

    okdoki da haste recht.. hab ich gemacht

    #pragma warning( disable : 4244 )
    
    #include "stdafx.h"
    
    #include <cstdlib> // contains prototypes for functions srand and rand
    using std::rand;
    using std::srand;
    #include <ctime> // contains prototype for function time
    using std::time;
    
    #include <string>
    using std::string;
    #include <iostream>
    using std::cout;
    using std::cin;
    using std::endl;
    using std::ios;
    using std::cerr;
    
    #include <iomanip>
    using std::setw;
    using std::left;
    using std::right;
    
    #include <map>
    typedef std::map<string, string, std::less<string>> StringMap;
    
    #include <algorithm>
    #include <iterator>
    
    #include <fstream>
    using std::ifstream;
    using std::ostream;
    
    enum Choices{PRINT_ENG='1', PRINT_GER='2', ENG_WORD='3', GER_WORD='4', END='5'};
    
    char menu();
    void Print_Eng(StringMap&);
    void Print_Ger(StringMap&);
    void Eng_Word(StringMap&,int);
    void Ger_Word(StringMap&,int);
    int random(int count);
    
    int _tmain(int argc, _TCHAR* argv[])
    {	
    
    	ifstream infile("data.txt", ios::in);
    	 if(!infile)
    		{
    			cerr<<"File could not be opened"<<endl;
    			exit(1);
    		}
    	 StringMap Eng_wordPairs;
    	 StringMap Ger_wordPairs;
    	 string english, german;
        static int counter=-1; //zählt eins zuviel ?
    	while(infile)
    	 {
    		 getline(infile, english, ',');
    		 getline(infile, german, '\n');
    		 Eng_wordPairs.insert(StringMap::value_type(english,german)); 
    		 Ger_wordPairs.insert(StringMap::value_type(german,english));
    		 ++counter;	
    	}
    	int whichword=random(counter);
    
    	char choice;
    	while((choice=menu())!=END)
    	{
    		switch(choice)
    		{
    		case PRINT_ENG:
    			Print_Eng(Eng_wordPairs);
    			break;
    
    		case PRINT_GER:
    			Print_Ger(Ger_wordPairs);
    			break;
    
    		case ENG_WORD:
    			Eng_Word(Eng_wordPairs, whichword);
    			break;
    
    		case  GER_WORD:
    			Ger_Word(Ger_wordPairs, whichword);
    			break;
    
    		default:
    			cerr<<"Incorrect choice, try again!"<<endl;
    			menu();
    		}//end switch
    	}//endwhile
    
    	infile.close();
    	return 0;
    }
    int random(int count)
    {
    	srand( time( 0 ) );
    	int r = 1 + rand() % count;
    	return r;
    }
    
    char menu()
    {
    	cout<<"****Vocable-Trainee(GER<->ENG)****"<<"\n";
    	cout<<"**************Menu****************"<<endl;
    	cout<<"1 english listing"<<"\n";
    	cout<<"2 german listing"<<"\n";
    	cout<<"3 random english vocable"<<"\n";
    	cout<<"4 random german vocable"<<"\n";
        cout<<"5 End Programm"<<"\n";
    	char menuChoice;
    	cout<<"your choice: ";
    	cin>>menuChoice;
    	return menuChoice;
    }
    
    void Print_Eng(StringMap& Eng_wordPairs)
    {
      cout<<"\nEnglish:"<<endl; 
    	for(StringMap::const_iterator iter=Eng_wordPairs.begin();
    		 iter!=Eng_wordPairs.end(); ++iter)
    		 cout<<left<<setw(25)<<iter->first<<":"<<setw(25)<<left<<iter->second<<"\n";
    }
    
    void Print_Ger(StringMap& Ger_wordPairs)
    {
       cout<<"\nDeutsch:"<<endl;
    	 for(StringMap::const_iterator iter=Ger_wordPairs.begin();
    		 iter!=Ger_wordPairs.end(); ++iter)
    		 cout<<left<<setw(25)<<iter->first<<":"<<setw(25)<<left<<iter->second<<"\n";	
    }
    
    void Eng_Word(StringMap& Eng_wordPairs,int count)
    {}
    
    void Ger_Word(StringMap& Ger_wordPairs,int)
    {}
    

    jetzt bleibt noch das problem mit dem lexikalischen ordnen hmmm ? hab da bisher in meiner literatur nichts besseres gefunden (zur zeit sortiert es nur nach ASCII zeichen, d.h. erst werden kleinbuchstaben sortiert dann grossbuchstaben)
    achja gibt es nee möglichkeit wie ich zugriff auf ein beliebiges element bekomme? (möchte nämlich ein zufallswort generieren)
    oder muss ich tatsächlich vorwärts iterieren :(.. wollte mit ner zufallsfunktion
    ein index generieren und zugreifen aber leider gibt es bei map anscheinend keine indizierung 😞 (naja hätte auch vector nehmen können aber will das mit map machen 😡 )

    gruss sten
    ps. danke schonmal für eure hilfe 🙂



  • jetzt bleibt noch das problem mit dem lexikalischen ordnen hmmm ?

    das ist net ganz so einfach, dafür musst du die vergleichsfunktion separat angeben

    das geht in etwa so:

    #include <locale>//das muss zusätzlich rein
    
    struct lexicalCompare
    {
      bool operator()(const string& s1, const string& s2) const
      {
        //jetzt wirds ätzend
        const std::collate<char>& cmp=std::use_facet<std::collate<char> >(std::locale());
        return cmp.compare(&*s1.begin(),&*s1.end(),&*s2.begin(),&*s2.end())<0;
      }
    };
    //die map
    typedef std::map<string, string, lexicalCompare> StringMap;
    

    achja gibt es nee möglichkeit wie ich zugriff auf ein beliebiges element bekomme? (möchte nämlich ein zufallswort generieren)
    oder muss ich tatsächlich vorwärts iterieren

    jep, musst du. in einer map ist sowas was du willst nicht schnell zu implementieren, und bei der stl geht es um geschwindigkeit.



  • @otze: leider funzt das nicht 😞 , gibt ein runtimefehler aus:
    zitat:

    Debug Assertion Failed!

    Progamm: blala pfadangabe...

    Expression:string iterator not deferencable

    For information on how your programm can cause assertion
    falure, see... docublabla

    void Eng_Word(StringMap& Eng_wordPairs, int& counter)
    {
    	int whichword=random(counter);
    	cout<<"\nwhichword:"<<whichword<<endl;
    
    	StringMap::const_iterator iter=Eng_wordPairs.begin();
    	for(int randomcounter=0; randomcounter<whichword; randomcounter++)
    	{
    		++iter;		
    	}cout<<"\nZufallswort:"<<iter->first;
    
    }
    

    so ich hab die zufallsfunktion implementiert , was meint ihr iss das ok oder is das spagetticode 😕 falls ja ich bin für nee performantere lösung dankbar...
    das mit dem Lexikalisch ordnen iss kompliziert ich hab die vermutung das otze da ein funktionsobjekt programmiert hat , allerdings sind mir die stl-funktionen unbekannt bzw ich steig da net durch was die macht naja iss auch wurst wenn der fehler noch bereinigt werden könnte oder wenn jemand nee andere lösung hat, nicht zögern hier rein zu schreiben. ein mentaler dank ist demjenigen gewiss 😃



  • ohne zu wissen, welche stl implementation du nutzt, kann ich nur raten.

    ich vermute, dass das dereferenzieren von end() nicht erlaubt ist.

    dann halt bissl anders:

    struct lexicalCompare
    {
      bool operator()(const string& s1, const string& s2) const
      {
        //jetzt wirds ätzend
        const std::collate<char>& cmp=std::use_facet<std::collate<char> >(std::locale());
        return cmp.compare(s1.data(),s1.data()+s1.size(),s2.data(),s2.data()+s2.size())<0;
      }
    };
    

    was genau passiert:

    ich bastel hier nen funktor der folgendes macht:
    zuerst hol ich mir das collate objekt aus dem globalen locale. in diesem locale sind die informationen darüber gespeichert, wie in der eingestellten umgebung strings sortiert werden(neben noch vielem mehr). Collate ist dabei das Objekt,d ass sich nur ums sortieren kümmert.
    von diesem objekt ruf ich dann compare auf, die eigentliche vergleichsfunktion.



  • Nur so nebenbei: wenn du "std::locale" und "std::collate<char>&" zu Membern machst sollte das einiges an Geschwindigkeit bringen 😉



  • otze schrieb:

    struct lexicalCompare
    {
      bool operator()(const string& s1, const string& s2) const
      {
        //jetzt wirds ätzend
        const std::collate<char>& cmp=std::use_facet<std::collate<char> >(std::locale());
        return cmp.compare(s1.data(),s1.data()+s1.size(),s2.data(),s2.data()+s2.size())<0;
      }
    };
    

    danke aber deine funktionsobjekt macht das gleiche wie mein

    typedef std::map<string, string, std::less<string>> StringMap;
    

    von der stl.
    ich möchte das das grosse A und das kleine a genauso behandelt werden.
    als beispiel:
    ich habe diese wörter in der txt-datei:

    Arbeit
    Besen
    Leben
    argwöhnisch
    bösartig
    Leben

    nach Ascii wird es so sortiert, also nach meiner genauso wie nach deiner variante:

    Arbeit
    Besen
    Leben
    argwöhnisch
    bösartig
    lieblich
    

    d.h. erst die grossen buchstaben dann die kleinen buchstaben:
    ich möchte aber:
    Arbeit
    argwöhnisch
    Besen
    bösartig
    Leben
    lieblich

    garnet so einfach 😕 hmmm irgendwie muss man wohl, eventuell die operatoren > < überladen . vielleicht gehts dann frag mich nur wie? hmmm.

    ps. und das problem geht weiter ! "umlaute äöü...!" die muüssen auch richtig sortiert werden (meine umschalttaste klemmt (bin nur zu faul *gg)also nicht wundern warum alles klein geschrieben ist )



  • hmm ... std::transform(string.begin(), string.end(), string.begin(), std::tolower); ... dann sortieren ...



  • Sortieren von "transformierten" Strings mit std::less sollte dasselbe Ergebnis liefern wie Sortieren der "nicht transformierten" Strings mit std::collate::compare.
    Zumindest wenn ich das richtig verstanden habe.



  • (D)Evil schrieb:

    hmm ... std::transform(string.begin(), string.end(), string.begin(), std::tolower); ... dann sortieren ...

    ich glaube std::transform kann man nicht anwenden bei maps 😞
    ich hab die codezeile zwischen 65 und 66 eingefügt

    std::transform(Ger_wordPairs.begin(), Ger_wordPairs.end(), Ger_wordPairs.begin(), std::tolower);
    

    gibt 12 fehlermeldungen u.a
    fehlermeldung:

    error C2780: '`global namespace'::std::_Enable_if<!std::_Is_checked_iterator<_OutIt>::_Result||!std::_Is_checked_iterator<_OutIt>::_Result,_OutIt>::_Result std::transform(_InIt1,_InIt1,_InIt2,_OutIt,_Fn2)': Erwartet 5 Argumente - 4 unterstützt
            c:\programme\microsoft visual studio 8\vc\include\algorithm(867): Siehe Deklaration von 'std::transform'
    

    ich glaube ausserdem würde dann alles kleingeschrieben sein oder?. is ja auch nicht Sinnvoll... hmmm



  • okay ich habs gefunden : das grösste problem gelöst hippie , danke ohne euch wär ich wohl net drauf gekommen .. jetzt gibt es nur noch ein problem´chen siehe unten

    struct ignorecase
    {
        std::locale loc;
        bool operator()(const string& s1, const string& s2) const
        {
            string::const_iterator iter1 = s1.begin();
            string::const_iterator iter2 = s2.begin();
            while ((iter1!=s1.end()) && (iter2!=s2.end()))
            {
                // tolower aus locale. Beruecksichtigt die
                // landesspezifischen Umwandlungen
                char cs1 = std::tolower(*iter1,loc);
                char cs2 = std::tolower(*iter2,loc);
                if (cs1<cs2)
                    return true;
                if (cs1>cs2)
                    return false;
                ++iter1;
                ++iter2;
            }
            return false;
        }
    };
    

    und dann mit

    typedef std::map<string, string, ignorecase> StringMap;
    

    so jetzt gibt es nur noch das problem mit dem Umlauten die soll er auch locker floggig sortieren nach lexikon. Im moment sortiert er erst die ä und dann erst a
    beispiel:

    ärgern 
    Arbeit
    

    normalerweise sollte erst Arbeit dann ärgern kommen.
    wenn jemand ne idee hat ..

    wobei auf console wird das zeichen wg. zeicheninkomp. nicht richtig dargestellt
    gibs wohl keine lösung dafür



  • Vermutlich mußt du das tolower() noch mit dem collate<>-Ansatz von oben zusammenfassen, um die Sonderzeichen mit in die Suchfolge einzubinden.



  • jep, nun brauchst du collate. allerdings musst du beachten, dass collate sich nach der region richtet, in der du lebst, bzw die bei deinem betriebssystem angegeben ist. Es kann sein, dass du je nach pc unterschiedliche Ergebnisse bekommst, aber es ist dann immer für die region richtig(unterschiedliche Länder, unterschiedliche Arten zu sortieren)



  • CStoll schrieb:

    Vermutlich mußt du das tolower() noch mit dem collate<>-Ansatz von oben zusammenfassen, um die Sonderzeichen mit in die Suchfolge einzubinden.

    😕
    wie das ?
    std::collate<char> cs1 = std::tolower(*iter1,loc); oder dergleichen .. ?? geht nicht..kenn die grammatik und wirkung von std::collate nicht 😞



  • collate() wandelt den String in etwas um, was nach den gängigen Regeln deiner Sprache sortiert werden kann (was insbesondere bedeutet, daß Umlaute ins Alphabet einsortiert werden). tolower() verwandelt alle Buchstaben in Kleinbuchstaben. Also mußt du die Strings erst per tolower() ver"kleiner"n (eventuell brauchst du dafür auch eine locale-spezifische Variante, die die Umlaute mit behandelt und anschließend durch das collate<> jagen, um die Sortierung richtigzustellen.



  • 😞 aber wie sieht der quellcode dann aus ..ich weiss nicht wie ich das implementieren, definieren 😕 soll .. muss ich das kaskadieren oder wie ?

    string sten;
    sten="ich bin doch ein BAP..(BAP = Blödster Anzunehmenster Programmierer)";



  • Achtung - nicht getestet:

    bool lexical_compare::operator()(string s1,string s2)//Übergabe per Wert ist beabsichtigt
    {
      //1) Umwandlung in Kleinbuchstaben:
      transform(s1.begin(),s1.end(),s1.begin(),bind2nd(std::tolower,std::locale());
      transform(s2.begin(),s2.end(),s2.begin(),bind2nd(std::tolower,std::locale());
      //Notiz an mich: Ich bin nicht ganz sicher, ob der bind2nd so klappt
    
      //2) Locale-abhängig vergleichen:
      const std::collate<char>& cmp=std::use_facet<std::collate<char> >(std::locale());
        return cmp.compare(s1.data(),s1.data()+s1.size(),s2.data(),s2.data()+s2.size())<0;
    }
    


  • nur so nebenbei: locale besitzt selbst einen operator(), der genau dafür gedacht ist.

    map<string,string,locale> localized_map(locale("de_DE.UTF-8"));
    

    falls das locale de_DE.UTF-8 vorhanden ist, kümmert es sich automatisch um die richtige lexikographische ordnung. (dafür reicht z.b. unter linux im CLI localedef -f UTF-8 -i de_DE de_DE.UTF-8)

    alternativ kannst du dir dein eigenes facet ableiten:

    class CollateIgnoreCase : public collate<char>
    {
       int do_compare (const char *A, const char* B, const char* X, const char* Y) const
       {
          /* leave it to you */
       }
    
       std::string do_transform (const char* A, const char* B) { /* ... */ }
    
       ~CollateIgnoreCase () {} //dtor privat machen. s.u.
    };
    
    void foo();
    
    int main ()
    {
       locale(locale(""), new CollateIgnoreCase); //locale kümmert sich um das delete
       //edit: das speichermanagement von locales und facets ist sowieso ein eigenes kapitel und imo nicht sehr intuitiv geregelt. wenn du das facet stand-alone verwenden willst, musst du dem basisklassenkonstruktor z.b. eine "1" übergeben. andernfalls schadet es sicherlich auch nicht, den destruktor privat (oder protected zu halten)
       foo();
    }
    
    void foo ()
    {
     map<string,string,locale> localized_map (locale(""));
     //hier musst du die "" angeben, weil locale keine funktion, sondern ein konstruktor ist und der ausdruck sonst eine funktionsdeklaration wäre.
     //...
    }
    


  • ein problem, dass bis jetzt übrigens mit jeder vorgeschlagenen lösung beachtenswert ist, die auf strings zuerst tolower anwedet, ist außerdem folgendes:

    CStoll schrieb:

    Achtung - nicht getestet:

    bool lexical_compare::operator()(string s1,string s2)//Übergabe per Wert ist beabsichtigt
    {
      //1) Umwandlung in Kleinbuchstaben:
      transform(s1.begin(),s1.end(),s1.begin(),bind2nd(std::tolower,std::locale());
      transform(s2.begin(),s2.end(),s2.begin(),bind2nd(std::tolower,std::locale());
      //Notiz an mich: Ich bin nicht ganz sicher, ob der bind2nd so klappt
    
      //2) Locale-abhängig vergleichen:
      const std::collate<char>& cmp=std::use_facet<std::collate<char> >(std::locale());
        return cmp.compare(s1.data(),s1.data()+s1.size(),s2.data(),s2.data()+s2.size())<0;
    }
    

    selbst, wenn das funktionieren würde, wäre es eine schlechte idee.
    gegeben seien zwei strings, a und b, die sich jeweils nur in der groß/kleinschreibung unterscheiden. mit dieser implementation passiert es, dass lexical_compare::operator()(a,b) und lexical_compare::operator()(b, a) true liefern, was gleichbedeutend ist mit a == b. folglich wird ein element nicht einfügt, obwohl es das eigentlich sollte. vorstellbar z.b. bei "angeln - v, i, regulär" vs "Angeln - n, pl von Angel".

    das müsste man sowohl bei einer eigenen funktion/funktor korrigieren (a < A < b < B ...) als auch bei einer eigenen implementation von collate - wenn du das passende locale allerdings auf deinem pc hast (de_DE oder whatever), macht es das automatisch - das verhalten von solchen locales liegt aber außerhalb der definitionsmacht des C++ standards. eine lösung mit multimap wäre nicht zu empfehlen.


Anmelden zum Antworten