PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : VC++/.NET: sscanf-Alternative (Konvertierung von Hex-Zahlen)


Vedek Bareil
2005-06-02, 16:12:03
Hi Leute,

letztens hatte ich, bzw. ein Kollege von mir, folgendes Problem.
Ich hatte einen Code verfaßt, mit dem in einem String stehende Hex-Zahlen in integers (bzw. unsigned chars, also 8-bit integers) konvertiert werden sollten, wobei von der Funktion sscanf Gebrauch gemacht wurde:

// Array zum aufnehmen der (hier 4) 8-bit integers
unsigned char ucByte[4];
// durchparsen des Strings mit den Hex-Zahlen
sscanf(sHexString, "0x%x 0x%x 0x%x 0x%x", &ucByte[0], &ucByte[1], &ucByte[2], &ucByte[3]);

sHexString ist ein String aus vier Hex-Zahlen, z.B. "0xa1 0x34 0x3c 0xfe", die in den vier Elementen des Arrays ucByte (als integer-Werte) gespeichert werden sollten, also ucByte[0] = 0xa1 = 161 usw.

Unter VC++ 6 lief das auch ganz gut, aber dann versuchte mein Kollege, das Programm, in dem dieser Code enthalten war, nach .NET zu portieren. Und da funktionierte es dann nicht mehr. Beim Verlassen der Funktion, in der der Code verwendet wurde, gab es immer eine Fehlermelung, irgendwas mit "Variable 'ucByte' korrupt" oder so. Ein Herumexperimentieren mit z.B. dynamischem Anlegen des Arrays ucByte oder unter Verwendung der std::vector-Klasse führte schließlich darauf, daß beim Löschen des Arrays irgendetwas schiefging.

Mein Kollege war schließlich der Ansicht, die Funktion sscanf sei die Quelle des Übels. Testweise haben wir das sscanf dann mal aus dem Code entfernt und die Konvertierung mittels einer selbstgeschriebenen (allerdings recht aufwändigen) Funktion vorgenommen. Und in der Tat war die Fehlermeldung dann verschwunden.
So absolut absurd und unlogisch das auch sein mag - man muß sich das mal vor Augen halten: weil die Elemente des Arrays ucByte ihre Werte mittels sscanf zugewiesen bekommen, funktioniert später das Löschen des Arrays nicht mehr richtig! "Die Logik ist ein kleiner zwitschernder Vogel der in der Wiese zirpt" fällt mir dazu nur ein :conf2: - es hat offenbar tatsächlich an sscanf gelegen.

Wie gesagt haben wir zur Konvertierung dann eine selbstgeschriebene Funktion verwendet. Diese ist aber nicht besonders komfortabel. Gibt es in C++ nicht irgendeine andere Funktion, die man statt sscanf benutzen kann? Vielleicht irgendwas mit stringstreams?

Asmodeus
2005-06-02, 16:47:19
Wenn man ein Array anlegt, es dann in irgend einer Form mit Werten füllt, z.B. über scanf, und beim Freigeben oder Löschen des Arrays später dann Probleme auftreten, dann liegt es in 99 Prozent der Fälle schon meist daran, dass beim Füllen des Arrays etwas über die Grenzen des Arrays hinausgeschrieben wurde. Lass Dir einfach mal den vom Array belegten Bereich im Speicher beim Debuggen anzeigen und achte einfach darauf, ob beim Füllen auch Teile des Speichers mit überschrieben werden, die nicht mehr zum Array gehören.

Das ganze muss dann nicht an scanf liegen, sondern vielleicht eher an einem falsch dimensionierten Array (in Bezug auf die Daten, die im Ausgangsstring stehen). Manchmal gibts auch Probleme, wenn ein String nicht richtig Nullterminiert ist.

Gruss, Carsten.

Vedek Bareil
2005-06-02, 16:50:26
Wenn man ein Array anlegt, es dann in irgend einer Form mit Werten füllt, z.B. über scanf, und beim Freigeben oder Löschen des Arrays später dann Probleme auftreten, dann liegt es in 99 Prozent der Fälle schon meist daran, dass beim Füllen des Arrays etwas über die Grenzen des Arrays hinausgeschrieben wurde. wurde aber nicht.
Lass Dir einfach mal den vom Array belegten Bereich im Speicher beim Debuggen anzeigen und achte einfach darauf, ob beim Füllen auch Teile des Speichers mit überschrieben werden, die nicht mehr zum Array gehören.hab ich. Ist nicht.
Das ganze muss dann nicht an scanf liegen, sondern vielleicht eher an einem falsch dimensionierten Array (in Bezug auf die Daten, die im Ausgangsstring stehen).tut's aber nicht.

Asmodeus
2005-06-02, 17:30:22
Also:

C-Referenz (http://www.cplusplus.com/ref/cstdio/scanf.html)

Dort wird angegeben, dass "%x" einen hexadezimalen Integerwert liefert. Das würde meiner Meinung nach schon bedeuten, dass die so übergebenen Werte nicht als Character-Werte gespeichert werden können, da sie "zu groß" sind und somit die Grenzen des Arrays überschreiben. Und wenn ich Deinen Code 1:1 übernehme, dann tritt bei mir dieses Überschreiben der Arraygrenze auch auf. Was dann natürlich beim Freigeben zum Fehler führt.

Gruss, Carsten.

Trap
2005-06-02, 17:50:06
Richtig, sscanf will bei %x sizeof(int) chars an die übergebene Adresse speichern. Was bei einem char-Array nicht die gewünschte Wirkung hat.

Hexeingabe geht auch mit C++-streams, dann musst du aber die "0x" am Anfang der Zahlen rauskicken. Oder einen Parser schreiben.
char inp[4];
cin >> hex >> inp[0];

Vedek Bareil
2005-06-02, 18:22:36
Dort wird angegeben, dass "%x" einen hexadezimalen Integerwert liefert. Das würde meiner Meinung nach schon bedeuten, dass die so übergebenen Werte nicht als Character-Werte gespeichert werden können, da sie "zu groß" sind hm, das könnte es sein.

Die sscanf-Alternative mit stringstreams hab ich auch schon gefunden:

for (int i=0; i < 4; i++)
{
// auslesen des aktuellen Bytes aus dem String
CString sByte;
AfxExtractSubString(sByte, sHexString, i, ' ');

// einlesen des aktuellen Bytes (z.B. "0xa1") in stream
std::stringstream ssByte;
ssByte << (const char*)sByte;

// Byte konvertieren
ssByte >> std::showbase >> std::hex >> ucByte[i];
}

Mit dem std::showbase klappt's auch mit dem 0x davor. Jetzt fehlt nur noch ne komfortable Methode zum Extrahieren von Substring aus der std::string-Klasse, dann kann man sich auch das Konvertieren zwischen std::string und CString sparen.

ScottManDeath
2005-06-02, 18:41:24
hm, das könnte es sein.

Die sscanf-Alternative mit stringstreams hab ich auch schon gefunden:

for (int i=0; i < 4; i++)
{
// auslesen des aktuellen Bytes aus dem String
CString sByte;
AfxExtractSubString(sByte, sHexString, i, ' ');

// einlesen des aktuellen Bytes (z.B. "0xa1") in stream
std::stringstream ssByte;
ssByte << (const char*)sByte;

// Byte konvertieren
ssByte >> std::showbase >> std::hex >> ucByte[i];
}

Mit dem std::showbase klappt's auch mit dem 0x davor. Jetzt fehlt nur noch ne komfortable Methode zum Extrahieren von Substring aus der std::string-Klasse, dann kann man sich auch das Konvertieren zwischen std::string und CString sparen.

Boost (http://www.boost.org/) hat eine menge an nützliche String Algorithmen (http://www.boost.org/doc/html/string_algo.html)
Darunter eine split (http://www.boost.org/doc/html/string_algo/reference.html#id477625) Funktion die einen string in dessen Teilstrings, durch Separatoren getrennt, zerlegt.


std::string input = "a b c d";
std::vector<string> sub_strings;
boost::split(sub_strings,input,is_any_of(" "));

//danach kanns du mit :

for(size_t i=0; i < sub_strings.size(); ++i)
sub_strings[i] = ....;

//auf die einzelnen elemente zugreifen