communityWir suchen ständig neue Tutorials und Artikel! Habt ihr selbst schonmal einen Artikel verfasst und seid bereit dieses Wissen mit der Community zu teilen? Oder würdet ihr gerne einmal über ein Thema schreiben das euch besonders auf dem Herzen liegt? Dann habt ihr nun die Gelegenheit eure Arbeit zu veröffentlichen und den Ruhm dafür zu ernten. Schreibt uns einfach eine Nachricht mit dem Betreff „Community Articles“ und helft mit das Angebot an guten Artikeln zu vergrößern. Als Autor werdet ihr für den internen Bereich freigeschaltet und könnt dort eurer literarischen Ader freien Lauf lassen.

CString Management Drucken E-Mail
Benutzerbewertung: / 9
SchwachPerfekt 
Geschrieben von: StarShaper   
Dienstag, den 20. September 2005 um 18:30 Uhr
Beitragsseiten
CString Management
CString in char *
VARIANT, STRINGTABLE und temporäre Objekte
std::string und Effizienzbetrachtungen
Alle Seiten

header

CStrings sind nützliche Datentypen. Sie vereinfachen eine Reihe von Arbeiten mit den MFC, indem sie die string Manipulation auf sehr viel angenehmere Art und Weise ermöglichen. Wie auch immer, es gibt eine Reihe von Techniken die es beim Umgang mit CStrings zu erlernen gibt. Diese können oft besonders schwer von Leuten mit reinen C-Hintergrund nachvollzogen werden, da in C strings immer nur als reine char Array's behandelt werden und der Umgang mit diesen etwas anders aussieht. Aber auch C++ Programmierer die bisher nur die string-Klasse kennen werden ein wenig umdenken müssen. Dieses Tutorial soll versuchen einige der Techniken im Umgang mit CStrings zu beschreiben.

Ein Teil von dem Tutorial richtet sich bereits an fortgeschrittene Programmierer. Dies ist zudem keine komplette Anleitung für die Arbeit mit CStrings, liefert dennoch Antworten zu den wesentlichen und grundlegenden Fragen.

  • String Verkettungen
  • Formatierung (inklusive Integer-nach-CString)
  • Konvertierung von CStrings in Integer
  • Konvertierung zwischen char * und CStrings
  • char * in CString
  • CString in char * I: Cast in LPCTSTR
  • CString in char * II: Unter Benutzung von GetBuffer
  • CString in char * III: Schnittstelle zu einer Control
  • CString in BSTR
  • BSTR in CString
  • VARIANT in CString
  • Laden von STRINGTABLE Werten
  • CStrings und temporäre Objekte
  • Konvertierungen zwischen std::string und CString
  • CString Effizienz

Allgemeines

Die MFC-Klasse CString stellt einige nützliche Methoden zur Verfügung. Eine vollständige Übersicht der CString Klassen Member findet man wie zu erwarten auf den MSDN Seiten unter CString Class Members. Das folgende Beispiel zeigt wie sich mit Hilfe der Methoden der Dateiname aus einer URI extrahieren lässt.

CString s (_T("C:\\Programme\\XYZ\\file.jpg"));
// Nun enthält s den string "file.jpg"
s = s.Right(s.GetLength() - (s.ReverseFind('\\') + 1 ));

String Verkettungen

Eines der angenehmen Eigenschaften von CStrings ist die Fähigkeit zwei strings auf einfache Art und Weise miteinander zu verknüpfen. Zum Beispiel auf die folgende Art und Weise:

CString gray("Gray");
CString cat("Cat");
CString graycat = gray + cat;

In C müsste man zuerst jeweils zwei char Array's definieren, anschließend Speicher für ein größeres char Pointer Array allokieren und letztenendes über die C Funktionen strcpy(); diesen in das neue array hineinkopieren. Wie das folgende Beispiel zeigt ist soetwas natürlich im Gegensatz zum obigen Code sehr umständlich und bietet zudem einige Fehlermöglichkeiten, wie z.B. das Weglassen des notwendigen Speichers für das string-Ende Zeichen.

char gray[] = "Gray";
char cat[] = "Cat";
char *graycat = malloc(strlen(gray) + strlen(cat) + 1);
strcpy(graycat, gray);
strcat(graycat, cat);

Formatierung (inklusive Integer-nach-CString)

Besser als die Benutzung von sprintf oder wsprintf ist die Nutzung der Methode Format zur Formatierung der CStrings.

CString s;
s.Format(_T("The total is %d"), total);

Der Vorteil hier liegt darin, das man sich nicht um die Größe des zur Speicherung der Daten notwendigen Buffers Sorgen machen muss. Dies wird automatisch für Sie von den Formatierungs-Routinen gemanaged.

Der Gebrauch der Formatierung ist der gebräuchlichste Weg nicht-string Datentypen in einen CString zu konvertieren. Zum Beispiel die Konvertierung von einem Integer in ein CString:

CString s;
s.Format(_T("%d"), total);

Ich benutze immer das _T( ) Makro, weil ich meine Programme so gestalte das diese zumindest Unicode "wissend" sind. Aber das ist für sich ein Thema und bietet genug Stoff für ein anderes Tutorial. Der Sinn des _T ( ) Makros besteht darin den string für eine 8-Bit-Character Anwendung folgendermaßen zu kompilieren,

#define _T(x) x // non-Unicode version

während für Unicode Anwendungen dieser hingegen so kompiliert wird.

#define _T(x) L##x // Unicode version

So hat das den Effekt als hätte ich für eine Unicode-Version den Code geschrieben:

s.Format(L"%d", total);

Wenn Sie der Meinung sind eventuell Codeteile mit Unicode zu benutzen, sollten Sie in einer Unicode erkennende Weise ihre Programme schreiben. Zum Beispiel sollten sie niemals den Operator sizeof() benutzen um die Größe eines Character (Zeichens) festzustellen. Dies würde in einer Unicode Anwendung um den Faktor 2 von der tatsächlichen Größe abweichen. Wenn ich die Größe eines Objektes erfahren möchte, muss ich ein Makro namens DIM aufrufen, welches in der Header-Datei dim.h definiert wurde. Dieses inkludiere ich entsprechend vorher.

#define DIM(x) ( sizeof((x)) / sizeof((x)[0]) )

Das ist nicht nur für den Umgang mit Unicode Buffern deren Größe zur Komilierungszeit noch nicht bekannt ist nützlich, sondern auch für jede in der Tabelle definierte Kompilierungszeit.

class Whatever { ... };
Whatever data[] = {
   { ... },
    ...
   { ... },
};
 
for(int i = 0; i < DIM(data); i++) // durchsuche die Tabelle nach einem Treffer 

Hüten sie sich vor diesen nachfolgend dargestellten API Aufrufen welche nach echten Bytezahlen verlangen. Die Benutzung eines Character Zählers wird nicht funktionieren.

TCHAR data[20];
lstrcpyn(data, longstring, sizeof(data) - 1); // FALSCH!
lstrcpyn(data, longstring, DIM(data) - 1); // RICHTIG
WriteFile(f, data, DIM(data), &bytesWritten, NULL); // FALSCH!
WriteFile(f, data, sizeof(data), &bytesWritten, NULL); // RICHTIG

Dies liegt daran, das lstrcpyn nach der Character Anzahl verlangt, aber WriteFile einen Byte Anzahl möchte.

Die Benutzung von _T( ) erzeugt noch keine Unicode Anwendung! Es generiert nur eine Unicode erkennende Anwendung. Wenn sie den Code im 8-Bit Modus kompilieren, erhalten sie ein "normales" 8-Bit-Character Programm. Wenn sie es im Unicode Modus kompilieren erhalten sie hingegen ein 16-Bit-Character Programm. Dieser Modus kann in ihrem Compiler unter Chracter Set definiert werden. Beachten sie, das ein CString in einer Unicode Anwendung ein string ist, welcher 16-Bit Character enthält.

Konvertierung von CStrings in Integer

Der einfachste Weg einen CString in einen Integer Wert zu konvertieren, ist eines der Standard string-nach-integer Umwandlungsroutinen zu verwenden.

Während sie normalerweise erwarten würden das _atoi eine gute Wahl wäre, ist es selten die richtige Wahl. Wenn sie Unicode gerechte Anwendungen programmieren wollen sollten sie die Funktion _ttoi aufrufen, welche im MultiByte Character Modus mit _atoi und im Unicode Modus mit _wtoi kompiliert werden. Sie sollten ebenfalls in Betracht ziehen _tcstoul (für unsigned Umwandlungen zu jeder 2-er Potenz, wie 2, 4, 8, 16...) oder _tcstol (für signed Umwandlungen zu jeder 2-er Potenz) zu benutzen. Hier ein paar Beispiele dazu:

CString hex = _T("FAB");
CString decimal = _T("4011");
ASSERT(_tcstoul(hex, 0, 16) == _ttoi(decimal));

Konvertierung zwischen char * und CStrings

Dies ist die am häufigsten gestellte Frage die von Anfängern zum CString Datentyp gestellt wird. Wegen den großen magischen C++ Klassen und Funktionen kommt der gewöhnliche objektorientierte C++ Programmierer nicht in Versuchung die computerinternen Mechanismen zu studieren, die hinter der Character Verwaltung stehen. Das ist oft auch einfach egal, weil die string-Klasse sich um Alles im Detail kümmert. Probleme tauchen aber spätestens dann auf wenn es um allgemeine Verständnisprobleme geht, welche auf das mangelnde Wissen über diese grundlegenden Mechanismen zurück zu führen sind. Dann stellt man sich oft die Frage warum etwas nicht funktioniert obwohl es so doch eigentlich funktionieren müsste.

Zum Beispiel warum man bei dem oben genannten Code nicht einfach dies schreiben kann:

CString graycat = "Gray" + "Cat";
oder
CString graycat("Gray" + "Cat");

Tatsächlich würde sich der Compiler bitter über diese Versuche beschweren. Warum? Weil der Operator + als ein überladener Operator zwischen verschiedenen Kombinationen der CString und LPCTSTR Datentypen definiert ist, aber nicht zwischen zwei LPCTSTR Datentypen welche zugrunde liegende Datentypen sind. Sie können nicht C++ Operatoren an Basis Typen, wie int und char oder char * überladen. Folgendes aber funktioniert:

CString graycat = CString("Gray") + CString("Cat");

oder sogar auch

CString graycat = CString("Gray") + "Cat";

Wenn sie diese Codeteile genauer betrachten stellen sie schnell fest, dass das + immer mindestens an einem CString und einem LPCSTR hängt.

char * in CString

Angenommen sie haben einen char * oder einen string. Wie machen sie daraus nun einen CString. Hier sind einige Beispiele:

char *p = "This is a test"

oder, in Unicode-erkennenden Anwendungen

TCHAR *p = _T("This is a test")

oder

LPTSTR p = _T("This is a test");

Sie können jede der folgenden Schreibweisen benutzen.

CString s = "This is a test";     // Nur 8-bit
CString s = _T("This is a test"); // Unicode-erkennend
CString s("This is a test");      // Nur 8-bit
CSTring s(_T("This is a test");   // Unicode-erkennend
CString s = p;
CString s(p);

Jede Schreibweise konvertiert bequem den konstanten string oder den Pointer in einen CString Wert. Achten sie darauf, das die zugehörigen Character immer in den CString kopiert werden, so dass sie auch sowetwas anschließend machen können:

TCHAR *p = _T("Gray");
CString s(p);
p = _T("Cat");
s += p;

Und sie können sicher sein, dass das Ergebnis der string "GrayCat" sein wird.

Es gibt zahlreiche andere Methoden für CString Konstruktoren, aber wir werden hier auf die meisten nicht näher eingehen. Sie können sich näher darüber z.B. in der MSDN informieren.

CString in char * I: Cast in LPCTSTR

Dies ist ein bißchen schwerer in der Durchführung, und es gibt eine Menge Verwirrung über den richtigen Weg es zu tun. Es gibt einige richtige Wege, aber wahrscheinlich genauso viele falsche Wege.

Die erste Sache die sie bei CStrings verstehen müssen ist das es sich um ein C++ Objekt handelt, welches drei Werte beinhaltet: ein Pointer auf ein Buffer, eine Zahl an gültigen Charactern in diesem Buffer und eine Bufferlänge. Die Zahl der vorhandenen Character kann jeden Wert angefangen bei 0 bis hin zu der maximalen Länge des Buffers minus 1 sein. Das letzte ist für das Nullbyte reserviert. Die Character Anzahl und die Buffer Länge sind elegant versteckt.

Solange sie nicht irgend etwas spezielles anstellen, wissen sie nichts über die Größe des Buffers, welcher mit dem CString assoziert ist. solange sie die Adresse des Buffers nicht kennen, können sie nicht dessen Inhalt verändern. Sie können den Inhalt nicht kürzen und sie dürfen die Länge des Inhalts nicht verändern. Dies führt zu einigen sehr merkwürdigen Workarounds.

Der Operator LPCTSTR (oder genauer gesagt, der Operator const TCHAR *), ist für CString überladen. Die Definition des Operators ist die Adresse des Buffers zurück zu geben. Infolgedessen, wenn sie einen string Pointer zum CString brauchen können sie etwas in dieser Art machen

CString s("GrayCat");
LPCTSTR p =  s;

und es wird korrekt funktionieren. Dies ist begründet in dem Vorgang wie in C Cast's (Typumwandlungen) vollzogen werden. Wenn ein Cast benötigt wird, erlaubt es C++ den Cast auszuwählen. Zum Beispiel können sie (float) als Cast an einer komplexen Zahl (in unserem Fall sind Real- als auch Imaginärteil vom Typ float) definieren und es so definieren das es nur die erste Zahl, den Real-Teil, dieser komplexen Zahl zurückgibt.

Complex c(1.2f, 4.8f);
float realpart = c;

Wenn der explizite Cast (float) richtig angewandt wurde, ist nun der Wert des realpart 1.2.

Dies funktioniert in jedem Fall. Zum Beispiel wird jede Funktion welche ein LPCTSTR Parameter annimmt, dieser Operation folgen, so dass sie eine Funktion haben können (vielleicht in einer DLL):

BOOL DoSomethingCool(LPCTSTR s);

und diese wie folgt aufrufen

CString file("c:\\myfiles\\coolstuff")
BOOL result = DoSomethingCool(file);

Das funktioniert einwandfrei, weil die Funktion BOOL DoSomethingCool() einen LPCTSTR als Parameter definiert hat und der LPCTSTR Operator auf das Argument angewendet wird, was in MFC bedeutet das die Adresse des Strings zurückgegeben wird.

Aber was ist zu tun wenn sie den CString formatieren wollen?

CString graycat("GrayCat");
CString s;
s.Format("Mew! I love %s", graycat);

Aufgrunddessen das der Wert in der Variablen-Argument Liste erscheint (die Argumenten-Liste dient neben dem Compiler zu Konstenzprüfung der Typen unter anderem der impliziten und Benutzer definierten Typumwandlung), gibt es keinen Operator der die Formatierung implizit erzwingt. Was also nun?

Tja, sie werden überrascht sein, aber wir erhalten den string

"Mew! I love GrayCat"

weil die MFC Implementierer den CString Datentypen sehr überlegt konstruiert haben, so dass ein Ausdruck des CString Typs auf den Pointer zum string zeigt. Wurde kein Cast angegeben bzw. fehlt dieser, so wie in einer Formatierung oder sprintf, werden sie trotzdem das richtige Ergebnis erhalten. Die zusätzlichen Daten die einen CString beschreiben, existieren in den Adressen unterhalb der nominalen CString Adresse.

Sie können aber nicht den string modifizieren. Wenn sie zum Beispiel "." durch "," (sie sollten aber die National Language Support Features für dezimale Umwandlungen benutzen, wenn sie auf internationale Standards achten wollen) ersetzen möchten, wie in diesem Beispiel:

CString v("1.00");  // 2 dezimal Stellen
LPCTSTR p = v;
p[lstrlen(p) - 3] = ',';

Wenn sie das versuchen, werden sie vom Compiler die Fehlermeldung erhalten das sie an einem konstanten string eine Zuweisung durchführen wollen. Der Compiler wird auch bei folgenden Versuch eine Fehlermeldung produzieren:

strcat(p, "each");

Die Funktion strcat() verlangt nach einem LPTSTR als erstem Argument aber sie haben dieser ein LPCTSTR gegeben.

Versuchen sie nicht diese Fehlermeldungen irgendwie zu umgehen. Sie werden sich nur in Schwierigkeiten bringen.

Der Grund liegt darin, das der Buffer einen Zähler hat, auf welchen sie nicht zugegreifen können (er befindet sich in einem verstecktem Bereich welcher unterhalb der CString Adresse sitzt) und wenn sie nun den string verändern, werden sie sehen das sich der Zähler des Buffers nicht verändert. Mehr noch! Wenn der string so lang ist, wie die phisikalische Buffer Grenze (mehr dazu später) wird jeder Versuch den String zu erweitern darin enden, das sie irgendwelche Daten im Speicher überschreiben, welche sich hinter dem Buffer befinden. Natürlich haben sie dazu keine Rechte, den diese Daten werden von anderen Programmen benutzt. Der sichere Tod einer jeden Anwendung!



Zuletzt aktualisiert am Mittwoch, den 22. August 2007 um 17:22 Uhr
 
AUSWAHLMENÜ