Problem mit Stringstream und String
-
Hallo,
ich schreibe gerade an einem Programm, was eine map (std::map) verwenden soll. Als key verwende ich strings und als eingabewert zahlen (int).
Ziel des ganzen ist ein Verbindungsgewicht zu speichern, also z.B. soll 1:2=3 angeben dass die connection von 1 zu 2 gleich 3 ist.Um das ganze hinzubekommen habe ich nun also einen Stringstream benutzt, der z.B. "1:2" aus zwei ints generiert (habe aehnliches hier im Forum schon gesehen,..). Der stream wird ueber die Methode stream.str() in ein string objekt geschrieben und dieses dann per stringobjekt.c_str() in ein char array umgewandelt und in die map gefuettert.
Nun zu meinem Problem: Wenn ich direkt in die map schreibe, also z.B.
meineMap["1:2"]=3; funktioniert das ganze wunderbar.
wenn ich aber den string benutze, der aus dem stringstream kommt dann sieht die map das ganze nicht als der gleiche an (es werden z.B. werte nicht richtig eingetragen, etc) OBWOHL ausgegeben mit std::cout beide 1:2 ergeben.Kann es sein, dass ein String-Objekt, welches mit "hiereinString" erstellt wurde und ein string-objekt was aus einem Stringstream kommt NICHT identisch ist? Schreibt der stream evtl noch irgendwelche zeichen à la \0 oder aehnliches hinten an den string dran??
Hoffe das problem ist einigermassen klar geworden, ansonsten kann ich hier auch noch Code posten..
Danke
-
Ja, Code wäre nicht schlecht, insbesondere die Definition der Map. Ich vermute nämlich Du hast keine Map mit string als Key-Type, sondern eine mit const char* als Key-Type.
-
also mein ansatz ist einem anderen (von silversurfer) hier aehnlich, daher leihe ich mir eben mal den code von silversurfer zu praesentationszwecken..
wir haben also eine map und koennen per char array direkt elemente einfuegen
hash_map<const char*, float> connections; hm.connections["1:4"]=1.4;
Das ganze funktioniert soweit super. Ich habe nun eine Methode, die aus zwei ints einen string erzeugt (hier aehnlich dem code von silver...) wie diesen hier:
void meineKlasse::stringGenerator(int key1, int key2, std::string& strng) { std::stringstream stream; stream << key1 << ":" << key2; strng = stream.str(); //strng += '\0'; }
Wenn ich nun also z.B. 1 und 2 in diese methode gebe, wird strng gefuellt mit "1:2".
Soweit so gut. Nebeneinandergestellt ist die obige Version "1:2" und die die in der Methode entsteht also identisch.
Leider sieht das die map aber anders, weil wie gesagt die direkte version "1:2" funktioniert und die die den String aus der Methode benutzt leider nicht.
Methodenaufruf:std::string temp; makeStringFromKeysP(key1,key2,temp); return connections[temp.c_str()];
Ist also ein evtl nicht sichtbarer unterschied zwischen den beiden Varianten? Wenn ich mit strcmp(...) vergleiche gibt es mir auch immer 0 zurueck... es sollte also doch ein Unterschied sein
Um dem problem aus dem Weg zu gehen hatte ich die idee, als key fuer die map ein pair zu nehmen, also z.B.:
std::map<std::pair<int,int>*, float, hash<const char*>, eqstr> connections;
mit dieser Map koennte ich ueber first und second auf die connection-elemente zugreifen,.. ich schreibe eben nur eine art eqstr methode, die first und second vergleicht... leider scheint das ganze nicht zu funktionieren (nicht mal ansatzweise). Wenn hier also jemand einen Rat wuesste waere ich auch sehr dankbar!
viele Gruesse
-
Wie ich vermutete - Der Typ const char* ist ein Zeiger - die Map sortiert mit operator< - ein const char* ist kleiner als ein anderes wenn die Speicheradresse kleiner ist.
Mach die Map vom Typ std::string wenn Du lexikalische Vergleiche der Keys haben möchtest.
-
daran liegt es leider nicht, ich habe auch folgenden Code getestet der den vergleichsoperator implementiert.. diese methode vergleicht die Strings oder const char* arrays schon inhaltlich...
hash_map<const char*, float, hash<const char*>, eqstr> connections; struct eqstr { bool operator()(const char* s1, const char* s2) const{ return strcmp(s1, s2) == 0; } };
Hat evtl jamend eine andere Idee? Was ist mit dem pair als key fuer die map?
Danke!
-
Ja, nimm std::string als Key. Das wurde dir aber auch schon in der ersten Antwort gesagt. Ich kann mir ehrlich gesagt nicht vorstellen wie eine Map mit const char * als Key funktionieren soll.
Du sagst, du baust deine Key-Strings mit Stringstreams zusammen. Etwa so?
std::ostringstream Stream; Stream << irgendwas; Map[Stream.str().c_str()] = irgendwas_anderes;
Ist jetzt einfach mal so geraten, denn den relevanten Code hast du uns leider unterschlagen.
Der obige Code-Ausschnitt funktioniert nicht. Und das hat nichts mit dem Stringstream zu tun. In dem Moment, wo der []-Operator der Map abgearbeitet worden ist, existiert der von c_str() zurückgegebene String nicht mehr und dein Schlüssel in der Map zeigt ins Nirvana.
-
Ja, nimm std::string als Key. Das wurde dir aber auch schon in der ersten Antwort gesagt.
--> es waere bei mir evtl auch gut gewesen direkt ein pair als key zu nutzen, deshalb die frage!
Ist jetzt einfach mal so geraten, denn den relevanten Code hast du uns leider unterschlagen
--> Der code steht oben, ist also nicht unterschlagen
Die Methode stringGenerator erledigt das ganze fuer mich und schreibt den stream-inhalt ueber ein referenzobjekt in einen string. Dieser wird spaeter imt c_str() in die hash-map gepackt.. Hier nochmal die beiden Code-elemente:
Hier der Map-Hinzufuegen-Code
std::string temp; makeStringFromKeysP(key1,key2,temp); return connections[temp.c_str()];
und der string generator:
void meineKlasse::stringGenerator(int key1, int key2, std::string& strng) { std::stringstream stream; stream << key1 << ":" << key2; strng = stream.str(); //strng += '\0'; }
Der obige Code-Ausschnitt funktioniert nicht. Und das hat nichts mit dem Stringstream zu tun. In dem Moment, wo der []-Operator der Map abgearbeitet worden ist, existiert der von c_str() zurückgegebene String nicht mehr und dein Schlüssel in der Map zeigt ins Nirvana.
--> AHA... nun wird es hier interessant! Kann es sein dass bei meinem Ansatz (siehe hier oben) das selbe passiert dass ich ins nirvana verweise?
Danke fuer eure Hilfe
-
Sorry, hatte den relevanten Code-Abschnitt übersehen.
Falls du mit obigem Ansatz das hier meinst:
std::map<std::pair<int,int>*, float, hash<const char*>, eqstr> connections;
Ja, das geht hunderprozentig schief. Zeiger und STL-Container sind ein Thema für sich. Zeiger als Schlüssel für eine Map sind eigentlich nie ein Thema, weil das gewöhnlich nicht funktioniert.
-
ganz im Grunde meinte ich jenes:
hash_map<const char*, float> connections;
in kombination mit der stringgenerator-methodesorry wenn das falsch ruebergekommen ist...
-
Hm, hash_map ist nicht Teil des Standards und ich kenne mich nicht wirklich damit aus. Aber unter der Annahme, daß das Interface äquivalent zu einer gewöhnlichen std::map ist (man möge mich bitte berichtigen, wenn das nicht so ist), gilt das, was ich oben schon geschrieben habe. Zeiger als Schlüssel zu verwenden ist schlecht. Nimm den std::string.
-
Z2! hash_map ist evtl. die std::ext::hash_map und erlaubt halt nur eine schnellere Suche im Gegensatz zur std::map. Aber es gelten die gleichen Regeln für jeden anderen Container.
mefirst! Benutze doch einfach std::string anstatt const char*. Hilft doch nichts, immer wieder die Frage zu stellen. Denn es geht nunmal ums Prinzip.
-
ich habe mich nun entschieden einfach pair (int,int) als key zu benutzen, das erlaubt mir auch etwas effizienteren zugriff auf die beiden werte des keys.. Mit dem string habt ihr recht, die Frage schien eh nicht beantwortbar zu sein, auch wenn ich den unterschied gern gewusst haette ;P
Das problem bei der Nutzung von pair als key ist lediglich die hash_funktion die ich dann wohl selber schreiben muss.. aber das waere hier offtopic, gell? (falls aber doch jemand weiss wie,... bitte melden)
Danke fuer eure Hilfe soweit
-
std::string s1 = "Hallo"; std::string s2 = "Hallo"; std::cout << (s1 < s2) << std::endl; // ergibt false const char* p1 = s1.c_str(); const char* p2 = s2.c_str(); std::cout << (p1 < p2) << std::endl; // ergibt einen zufälligen Wert
Das ist genau das, was eine std::map<const char*, int> verwenden würde. Über den kleiner-als-Operator wird die Reihenfolge bestimmt und auch die Gleichheit ermittelt. Nämlich die Schlüssel sind gleich, wenn folgendes gilt: !(p1 < p2 || p2 < p1). Wenn Du als Schlüssel jetzt eine String-Konstante übergibst (also keinen std::string), dann erhälst Du wahrscheinlich immer eine Ungleichheit.
Du hattest den richtigen Ansatz schon geliefert, indem Du ein eigenes Prädikatobjekt angelegt hast. Allerdings hast Du diesen falsch implementiert. Richtig wäre zumindest für die std:
struct cmpstr { bool operator()(const char* s1, const char* s2) const{ return strcmp(s1, s2) < 0; } }; std::map<const char*, float, cmpstr> connections;
Ich nehme an, daß das mit der hash_map ähnlich ist.
Eine andere Sache ist auch noch falsch. Wenn Du mit stringstream einen String anlegst und über c_str() den Zeiger holst, wird der Zeiger ungültig, sobald der stringstream ungültig wird. Den als Schlüssel in einem Container zu verwenden, wäre ungünstig.
Und noch was: Du schreibst:
std::map<std::pair<int,int>*, float, hash<const char*>, eqstr> connections;
Warum willst Du immer Pointer verwenden? Nimm doch einfach:
std::map<std::pair<int,int>, float> > connections;
Das entspricht dem Standard (im Gegensatz zu hash_map) und ist effizienter als einen std::string zu verwenden.
-
Danke fuer die Hilfe, besonders die info ueber den StringStream hilft mir sehr...
Die standard Map laeuft jetzt (wenn auch es eine normale map ist und keine hash-map,..). Nun kann ich mich also mit den anderen neueren Problemen des Image-Processing widmen..