Zeichenketten: String
Willemers Informatik-Ecke
Die von C geerbten Zeichenketten haben den großen Nachteil, dass der Speicher nicht dynamisch nach Anforderung belegt wird, sondern dass immer so viel Speicher blockiert wird, wie im schlechtesten Fall erforderlich ist. Auch das Fehlerpotenzial, das durch das Fehlen der Abschluss-Null und der fehlenden Kontrolle der Speichergrenzen entsteht, hat viele Programmierer veranlasst, eine eigene Stringklasse zu schreiben. Inzwischen ist aber eine Klasse string in der Standardbibliothek verfügbar.

Standardklasse string

Verwendung

Um ein Objekt vom Typ string zu definieren, muss zunächst die Datei string eingebunden werden. Sie endet wie viele Header-Dateien der Standardbibliothek von C++ nicht auf .h. Die Klasse string liegt im Namensraum std. Sofern Sie nicht jedes Mal std::string angeben wollen, um einen String zu definieren, ziehen Sie zu Anfang den Namensraum std mit dem Befehl using hinzu:

#include <string>
using namespace std;

string meinName;

Kompatibilität

Ein Objekt vom Typ string verhält sich in vielen Dingen kompatibel zum char-Array. Der Zugriff auf die einzelnen Zeichen eines Strings erfolgt wie beim klassischen C-String durch die eckigen Klammern. Es ist möglich, Strings mit C-Strings zu initialisieren. Sie können sie ineinander umformen und miteinander vergleichen.

Unterschiede

Der Hauptunterschied besteht darin, dass Sie einen String nicht über einen char-Zeiger manipulieren können, wie Sie das mit einem char-Array gemacht haben. Ein Objekt der Klasse string besteht ja nicht nur aus den Zeichen selbst. Wenn Sie also einen Zeiger auf den String verwenden, zeigt dieser nicht auf den ersten Buchstaben, sondern auf das Objekt, das die Zeichen verwaltet. Da das Objekt in der Lage sein muss, die Daten an eine andere Stelle zu kopieren, wenn die Zeichenkette größer wird, ist es gar nicht ratsam, mit einem Zeiger auf die Zeichen direkt zuzugreifen. Stattdessen bietet die Klasse eigene Zeiger an, die »Iteratoren« genannt werden. Sie erfordern allerdings eine besondere Handhabung. Wir werden darauf noch zurück kommen. Das Fehlen der direkten Zugriffe schränkt den Programmierer zwar ein, erhöht aber die Sicherheit des Programms.

Vorteile

Die Klasse string bietet gegenüber den klassischen C-Strings einige Vorteile. So muss beim Anlegen eines Objekts nicht angegeben werden, wie viele Zeichen reserviert werden sollen. Wird mehr Platz benötigt, sorgt das Objekt selbst dafür, dass es den erforderlichen Speicherbereich bekommt. Der größte Vorteil ist vielleicht, dass Sie Strings einfach zuweisen und vergleichen können, wie Sie es von Integer-Variablen gewöhnt sind.

Zuweisung

Ein String kann durch eine Zuweisung direkt in eine andere Stringvariable kopiert werden. Sie brauchen also keine spezielle Funktion strcpy(), müssen auch keine Schleife über die Elemente machen, nicht darüber nachdenken, ob die Abschluss-Null gesetzt ist, und nicht kontrollieren, ob der Zielspeicher für eine Kopie überhaupt ausreicht. Sie können dem string-Objekt sogar ohne eine explizite Konvertierung einen klassischen C-String zuweisen.

Verbinden

Strings können aneinander gehängt werden, indem ein einfaches Pluszeichen zwischen sie gestellt wird.

#include <string>
using namespace std;

string meinName = "neu";
char oldName[25] = "alt";
string neuString;


    neuString = meinName;            // "neu"
    neuString = oldName;             // ält"
    neuString = meinName + oldName;  // "neualt"
    neuString += oldName;            // "neualtalt"

Vergleiche

Im Gegensatz zu den C-Strings können für Abfragen jetzt die einfachen Operatoren verwendet werden, die bereits von den Zahlenvergleichen bekannt sind. Es sind also keine speziellen Funktionen wie strcmp() mehr erforderlich. [Vergleichsoperatoren auf Strings]
Operator Bedeutung
== gleich
!= ungleich
< in lexikalischer Reihenfolge vorher
> in lexikalischer Reihenfolge nachher
<= in lexikalischer Reihenfolge vorher oder gleich
>= in lexikalischer Reihenfolge nachher oder gleich
Für den Vergleich reicht es, dass einer der beiden Operanden vom Typ string ist. Er kann sowohl mit einem klassischen C-String als auch mit einem char verglichen werden. Die Reihenfolge spielt keine Rolle.

string s;
char cstring[256];
    ...
    if (s < cstring)
    ...
    if (cstring < s)

Elementfunktionen

Darüber hinaus bringt die Klasse string eine Reihe anderer Funktionen mit, die in Tabelle (tabstringfunction) aufgeführt sind.

[String-Funktionen]
Funktion Aufgabe
length() liefert die Länge des Strings
insert(n, s) fügt den String s an Position n ein
erase(p, n) Entfernt ab Position p n Zeichen
find(s) Liefert die Position, an der sich der String s befindet

Beispiel

Im folgenden Code-Ausschnitt sind die Funktionen noch einmal in einem Programm dargestellt. In den Kommentaren stehen die Ergebnisse der Funktion.

#include <string>
using namespace std;

int main()
{
    string s("123");    // Konstruktor fuer C-String
    len = s.length();   // liefert 3, die Laenge des Strings
    s.insert(2, "xy");  // s ist nun "12xy3"
    s.erase(2,2);       // s ist nun "123"
    pos = s.find("23"); // liefert die Position 1
}

Iterator statt Zeiger

Auf die einzelnen Buchstaben einer Zeichenkette können Sie über den Indexoperator, also die eckigen Klammern, genau wie bei den klassischen C-Strings zugreifen. Die Verwendung eines Zeigers in der altbekannten Weise ist allerdings nicht möglich, da der Ort der Zeichenkette nicht mit dem Ort des Objekts übereinstimmt. Darüber hinaus kann die Speicheradresse sogar wechseln, wenn die Zeichenkette größer wird.

Die Klasse string bietet als Ersatz eine Art Zeiger an, die Iterator genannt wird. Den Typ des Iterators bringt die string-Klasse gleich mit. Er lautet string::iterator. In den allermeisten Fällen werden Sie den Iterator zunächst auf den Anfangs des Strings setzen wollen. Dazu bietet die Klasse string die Elementfunktion begin() an. Sie hat keine Parameter und liefert einen Iterator zurück. Der Iterator kann inkrementiert werden wie ein gewöhnlicher Zeiger. Um das Ende des Strings zu erkennen, gibt es die Elementfunktion end(), die ebenfalls einen Iterator zurückgibt. Dieser Iterator zeigt hinter den letzten gültigen Buchstaben des Strings. Sobald der Iterator gleich dem von end() gelieferten ist, hat er das Ende des Strings überschritten. Über den Iterator kann wie beim Zugriff über einen Zeiger auf das jeweilige Element zugegriffen werden, indem dem Iterator ein Stern vorangestellt wird.

Beispiel

In der folgenden Schleife wird der String mit Hilfe eines Iterators durchlaufen und Buchstabe für Buchstabe auf dem Bildschirm ausgegeben.

[Auslesen eines Strings über einen Iterator (stringit.cpp)]

#include <iostream>
#include <string>
using namespace std;

int main()
{
    string s = "Testlauf";
    string::iterator i;
    for (i=s.begin(); i!=s.end(); i++)
    {
        cout << *i ;
    }
    cout << endl;
}

Gültigkeit

Die Haltbarkeit eines Iterators ist begrenzt. Sobald der String in der Größe verändert wird, kann der Iterator seine Gültigkeit verlieren. Aus diesem Grund sollte der Iterator bei jedem Durchlauf erneut mit Hilfe der Funktion begin() bestimmt werden.

Revers

Neben dem Iterator gibt es den Revers-Iterator. Der Revers-Iterator hat einen anderen Typ als der normale Iterator. Auch dieser Typ wird von der Klasse string mitgeliefert. Er wird als string::reverse_iterator definiert und läuft rückwärts durch den String, obwohl er mit ++ erhöht wird. Um diesen Iterator auf das Ende des Strings zu setzen, verwenden Sie die Elementfunktion rbegin(). Der Abschluss wird durch rend() bestimmt. Durch das folgende Programm wird der String rückwärts ausgegeben:

[Rückwärtslaufen über Iterator (stringrev.cpp)]

#include <iostream>
#include <string>
using namespace std;

int main()
{
    string s = "Testlauf";
    string::reverse_iterator i;
    for (i=s.rbegin(); i!=s.rend(); i++)
    {
        cout << *i ;
    }
    cout << endl;
}

Umwandlung zwischen Zahlen und Zeichenketten

Sehr häufig begegnet dem Programmierer die Situation, dass Zeichenketten in Zahlen oder Zahlen in Zeichenketten umgewandelt werden müssen. Wenn ein Benutzer eine Zahl eingibt, tippt er natürlich eine Folge von Buchstaben ein, also einen String. Zur Weiterverarbeitung muss dieser dann in Zahlen umgewandelt werden. Bei der Verwendung von cin erledigt das die Klasse iostream. In anderen Fällen wie beispielsweise bei grafischen Oberflächen übergeben die Kontrollelemente Zeichenketten an das Programm und das Programm muss sie selbst in Zahlen umwandeln. Ebenso erwarten die Kontrollelemente der grafischen Oberflächen eine Zeichenkette, wenn etwas angezeigt werden soll. Sollen dort Zahlen erscheinen, müssen Sie sie zunächst in Zeichenketten umwandeln.

String-Streams

Für solche Aufgaben bieten in den Standardbibliotheken von C++ die Klassen istringstream und ostringstream die Möglichkeit an, Daten mit den Ein- und Ausgabeoperatoren >> und << in Zeichenketten umzuleiten.

Zeichenkette nach Zahl

Zur Umwandlung einer Zeichenkette in eine Zahlenvariable verwenden Sie ein Objekt der Klasse istringstream. Dieses kann bei der Definition direkt mit der Zeichenkette initialisiert werden. Anschließend muss nur der Eingabeoperator von dem Objekt auf eine Zahlenvariable gelenkt werden.

#include <sstream>
using namespace std;

int meineZahl;
string s("123");
istringstream inStream(s);
inStream >> meineZahl;

Zahl nach Zeichenkette

Soll der umgekehrte Weg beschritten werden, also aus einer Zahlenvariablen eine Zeichenkette generiert werden, so verwenden Sie ein Objekt der Klasse ostringstream. Mit dem Ausgabeoperator << wird der Inhalt der Zahlenvariablen an das Stream-Objekt umgeleitet. Mit der Elementfunktion str() kann man die Zeichenkette direkt auslesen.

#include <sstream>
using namespace std;

int meineZahl = 976;
string s;
ostringstream outStream;
outStream << meineZahl;
s = outStream.str();

Um die Stringvariable Ihren Ausgabebedürfnissen anzupassen, können Sie auch Manipulatoren verwenden.

Ältere Versionen von C++ bieten mit strstream eine Klasse mit ähnlicher Funktionalität. Allerdings empfiehlt der Standard ausdrücklich, diese Klasse strstream nicht mehr zu verwenden. (vgl.: Kaiser, Richard: C++ mit dem Borland C++ Builder. Springer, Berlin-Heidelberg. 2002. S. 375.)

Andere String-Bibliotheken

Leider gibt es neben der oben ausgeführten Standardbibliothek noch einige Sonderimplementationen. In den ersten C++-Versionen war die Bibliothek string noch nicht enthalten. Das führte zu diversen privaten Implementationen verschiedenster Herkünfte. Da ein Standard nicht existierte, führten die großen Klassenbibliotheken jeweils ihre eigenen String-Klassen ein. Diese sind auch nach der Einführung der Klasse string nicht verändert worden. Der erste Grund ist, dass die eigenen Klassen wesentlich besser an die Klassenbibliothek angepasst waren. Der andere Grund ist, dass mittlerweile eine Reihe Programme existierte, die die String-Klassen verwendeten. Die Hersteller konnten ihre Kunden nicht einfach im Regen stehen lassen, nur weil es endlich einen Standard gab. Die Microsoft Foundation Classes (MFC) verwenden eine Klasse namens CString und der Borland C++-Builder eine Klasse namens AnsiString. Diese Klassen bieten allerdings einen Konvertierungskonstruktor, so dass es möglich ist, mit Standard-Strings zu arbeiten und die Strings erst beim Zugriff auf die Schnittstellen der Klassenbibliotheken zu konvertieren. Dieses Vorgehen hat den Vorteil, dass man einen großen Teil der Module eines Projekts portabel halten kann.

String als char-Array

Wenn man sich ganz naiv einen Text ansieht, erkennt man darin sofort eine Reihe von Buchstaben. Der Programmierer tippt sofort auf ein Array. Und genau das haben sich auch die Erfinder von C gesagt und eine Zeichenkette als ein Array von char definiert. Für eine Zeichenkette definiert man also ein char-Array und sammelt darin Buchstaben:
char name[20];
name[0] = 'K';
name[1] = 'a';
name[2] = 'i';
Das Programm will natürlich wissen, wann der Name zu Ende ist. Zu diesem Zweck wird eine 0 hinten angehängt. Vorsicht! nicht '0', sondern 0, was man als Zeichen mit '\0' kodiert. Für Kai brauchen wir also 4 char-Elemente, K, a, i und die 0.
char name[20];
name[0] = 'K';
name[1] = 'a';
name[2] = 'i';
name[3] = 0; // oder '\0', was das Gleiche ist
Das Einfüllen der einzelnen Buchstaben ist sehr mühsam, darum wurde gleich ein Literal definiert. Eine Zeichenkette erkennt man daran, dass sie in Anführungszeichen steht - im Gegensatz zu den Hochkommata von char.
char name[20] = "Kai";
Da C++ möglichst kompatibel zu C ist, werden auch die C-Strings unterstützt. Das geht auch so weit, dass cin und cout wunderbar mit dem char-Array zusammenspielen.
#include <iostream>
using namespace std;

int main()
{
    char name[20] = "Willemer";
    cout << "Geben Sie Ihren Namen ein: ";
    cin >> name;
    cout << "Ihr Name lautet " << name << endl;
    // Schauen wir auf jedes char im Array:
    for (int i=0; i<20; i++) {
         cout << name[i] << ' ';
    }
    cout << endl;
}
Geben Sie mal Kai als Name ein und Sie werden sehen, dass das "emer" von "Willemer" noch in der Variablen steht. Da aber die Ausgabe beim Auftreten von 0 aufhört, sehen wir das nicht, wenn wir die Variable name einfach ausgeben.

Speicheranforderung

Da der klassische C-String ein Array vom Typ char ist, muss der Programmierer abschätzen, wie groß der String einmal werden kann. Das Array muss auf den ungünstigsten Fall vorbereitet sein. Die Maximalgröße muss bei der Definition eines Arrays festgelegt werden. Sie können dieses Problem umgehen, indem Sie den Speicher mit Hilfe von new dynamisch anfordern. Wird das Array doch größer, müssen Sie einen neuen Speicher anfordern, den String umkopieren und den alten Speicher wieder freigeben. Anders ausgedrückt: Das Speichermanagement der Zeichenketten gehört damit zu den Aufgaben der Applikation.

Abschluss-Null

Das tatsächliche Ende der Zeichenkette wird unabhängig von der Größe des Arrays durch ein Nullzeichen bestimmt. Wenn nun das Nullzeichen vergessen wird und innerhalb des Arrays nicht zufällig eine 0 auftritt, werden Daten verwendet, die nichts mit der Zeichenkette zu tun haben. Solche Fehler sind schwer zu finden, weil das Überschreiben fremder Variablenbereiche nicht sofort zu einem Fehler führt. Damit ist die Ursache nicht erkennbar, wenn der Fehler sichtbar wird.

Klassische C-Funktionen

Immerhin gibt es für die klassischen C-Strings eine Vielzahl unterstützender Funktionen, so dass der Programmierer nicht darauf angewiesen ist, alle Stringoperationen selbst durchzuführen. Um diese Funktionen zu nutzen, muss die Header-Datei string.h eingebunden werden.

Selbstgestrickt

Die Funktionen sind meist gar nicht besonders kompliziert, und so finden Sie in manchem Programm direkt ausprogrammierte Schleifen wie das folgende Beispiel, das wie strcpy() eine Zeichenkette kopiert.

[strcpy() selbst geschrieben]

while(*Quelle)
{
    *Ziel++ = *Quelle++;
}
*Ziel = 0;

Stringkopie

Die Funktion strcpy() der Standardbibliothek erwartet als Parameter zunächst das Ziel und dann die Quelle. Als Eselsbrücke können Sie sich merken, dass die Reihenfolge genau die ist, die Sie auch in einer Zuweisung verwenden würden. Weitere Parameter hat strcpy() nicht, und so kann die Funktion auch nicht feststellen, ob die Quelle eventuell größer als das Ziel ist. In einem solchen Fall würde über die Grenze des Arrays hinausgeschrieben. Darum gilt die Verwendung von strcpy() als Sicherheitsrisiko. Verwendet ein Angreifer dem Programm als Eingabe oder Parameter eine überlange Zeichenkette, wird die Funktion strcpy() über den Rand des Arrays hinwegschreiben und damit Variablen verändern, die für den Anwender eigentlich unerreichbar sein sollten. Dadurch können unerwünschte Funktionen oder ein Absturz des Programms erreicht werden. Wenn das Programm eine Schutzfunktion gegenüber dem Rechner hat (Passworteingabe, Firewall oder ähnliches), dann ist der Rechner anschließend für den Angreifer offen.

Um dies zu verhindern, gibt es die Funktion strncpy(), die als weiteren Parameter die maximale Länge des zu kopierenden Bereichs erwartet. Damit ist ein Überschreiten der Grenzen des Ziel-Arrays leicht vermeidbar. Leider schließt strncpy() den Zielstring nicht mit einer 0 ab, wenn die Länge des Quellstrings den dritten Parameter überschreitet. Es werden einfach exakt so viele Zeichen kopiert, wie der dritte Parameter angibt. Damit das Ziel-Array auf jeden Fall korrekt abgeschlossen ist, sollten Sie grundsätzlich nach dem Aufruf von strncpy() das letzte Element des Ziel-Arrays auf 0 setzen. Das folgende Beispiel zeigt, wie das geht:

char *Quelle;
char Ziel[MAX];
   ...
   strncpy(Ziel, Quelle, MAX);
   Ziel[MAX-1] = 0; // stört nie, ist aber sicherer

Strings verbinden

Zum Aneinanderhängen zweier Strings wird die Funktion strcat() verwendet. Der erste Parameter enthält den ersten Teil des Strings und ist gleichzeitig die Zielvariable. Es wird also an den existierenden String im Ziel der String des zweiten Parameters angehängt. Auch in diesem Fall gibt es eine Funktion, die als dritten Parameter eine Länge entgegennimmt. Diese Funktion heißt strncat(), und der dritte Parameter gibt an, wie viele Zeichen aus dem Quell-String maximal angehängt werden. Es handelt sich also nicht um die maximale Länge des Ziel-Strings. Das folgende Beispiel verwendet die Funktion strlen(), die die Länge eines Strings liefert:

char *Quelle;
char Ziel[MAX];
...
strncat(Ziel, Quelle, MAX-strlen(Ziel));
Ziel[MAX-1] = 0; // stört nie, ist aber sicherer

Stringlänge

Die Funktion strlen() liefert als Rückgabewert die Länge des Strings, der als Parameter übergeben wird.

Stringvergleich

Mit der Funktion strcmp() können zwei Strings miteinander verglichen werden. Der Rückgabewert ist 0, wenn beide Zeichenketten identisch sind. Ist der erste String lexikalisch größer als der zweite, ist der Rückgabewert eine positive ganze Zahl. Im umgekehrten Fall ist der Rückgabewert negativ. »Lexikalisch kleiner« bedeutet, dass die Zeichenkette im Lexikon oder im Telefonbuch eher einsortiert würde. Allerdings ist zu berücksichtigen, dass die deutschen Sonderzeichen hier ausscheren. So wird ein ü von C++ keineswegs als ue interpretiert, sondern als ein Wert jenseits der 128. Je nachdem, ob das Attribut unsigned für das char-Array verwendet wird, steht das ü also weit vor oder weit hinter den internationalen Buchstaben.

Teilstring suchen

Mit der Funktion strstr() können Sie einen Teilstring in einem größeren String suchen. Der erste Parameter ist der String, in dem gesucht wird. Der zweite Parameter ist der Such-String. strstr() gibt einen Zeiger auf die erste Stelle zurück, an der der Such-String gefunden wurde. Existiert der Such-String nicht, ist der Rückgabewert 0. Zum Abschluss sehen Sie die Prototypen der wichtigsten Stringfunktionen, wie sie in C verwendet werden:

#include <string.h>

char *strcat(char *Ziel, const char *Quelle);
int strcmp(const char *s1, const char *s2);
char *strcpy(char *Ziel, const char *Quelle);
size_t strlen(const char *s);
char *strncat(char *Ziel, const char *Quelle, size_t n);
int strncmp(const char *s1, const char *s2, size_t n);
char *strncpy(char *Ziel, const char *Quelle, size_t n);
char *strstr(const char *Heuhaufen, const char *Nadel);

Strings in Zahlen konvertieren

Es ist erforderlich, Zeichenketten in Zahlen umzuwandeln, wenn beispielsweise die Benutzereingaben in Dialogboxen als Zeichenketten an das Programm geliefert werden, das Programm aber eigentlich die eingegebene Zahl braucht.

atoi()

Die Funktion atoi() (atoi ist die Abkürzung für ascii to int) erwartet einen C-String als Parameter und liefert einen Wert vom Typ int zurück. Die Zeichenkette muss mit Ziffern oder Vorzeichen beginnen. Die Funktion bricht ab, sobald ein Zeichen auftritt, das nicht in eine Zahl umzuwandeln ist. Analog zu atoi() gibt es die Funktion atol(), die einen Rückgabewert vom Typ long liefert. Für Fließkommazahlen gibt es die Funktion atof(), deren Rückgabewert vom Typ double ist.

#include <stdlib.h>

int    atoi(const char *s);
long   atol(const char *s);
double atof(const char *s);

Die Funktion atof() hat den Nachteil, dass sie nur einen Punkt als Dezimalzeichen akzeptiert. Im deutschsprachigen Raum wird aber ein Anwender lieber ein Komma verwenden. Dies wiederum wird von atof() als Ende der Zahl interpretiert, so dass die Nachkommastellen fehlen. Um die Verwendung eines Kommas als Dezimalzeichen zu ermöglichen, können wir eine eigene Funktion atof schreiben, die als zweiten Parameter ein beliebiges Dezimalzeichen akzeptiert.

[atof überladen]

double atof(const char *ZahlenString, char DezimalZeichen)
{
double Wert = 0.0;
bool Negativ = false;

    // Vorzeichen bearbeiten
    if (*ZahlenString=='-')
    {
        Negativ = true;
        ZahlenString++;
    }
    else if (*ZahlenString=='+')
    {
        ZahlenString++;
    }
    // Vorkommastellen berechnen
    while (*ZahlenString>='0' && *ZahlenString<='9')
    {
        Wert *= 10;
        Wert += (*ZahlenString++ - '0');
    }

    if (*ZahlenString == DezimalZeichen )
    {
        // Nachkommastellen berechnen
        double NachkommaPotenz=1.0;
        ZahlenString++;
        while (*ZahlenString>='0' && *ZahlenString<='9')
        {
            NachkommaPotenz /= 10;
            Wert += (*ZahlenString++ - '0') * NachkommaPotenz;
        }
    }
    // Vorzeichen berücksichtigen
    if (Negativ)
    {
        Wert = -Wert;
    }
    return Wert;
}

Übung

Zahlen in Strings konvertieren

Für die Umwandlung einer Zahl in einen String wird gern die Funktion sprintf() verwendet. Das »printf« im Namen deutet auf ihre Herkunft hin. Die Funktion printf() (print formatted) wird in C dazu verwendet, formatierte Ausgaben zu erzeugen. Die Funktion sprintf() hat die gleiche Funktionalität, jedoch wird die Ausgabe nicht auf dem Bildschirm ausgegeben, sondern in einen C-String geschoben. Eine dritte Variante namens fprintf() schreibt die Ausgabe in eine Datei. Aufgrund ihrer Herkunft muss die Datei stdio.h eingebunden werden.

#include <stdio.h>

int printf(const char *Format, ...);
int fprintf(FILE *stream, const char *Format, ...);
int sprintf(char *String, const char *Format, ...);

Die Funktion sprintf() soll hier näher betrachtet werden, weil sie auch in C++-Programmen häufig benutzt wird, um Zahlen oder Tabellen zu formatieren. Die Funktion ist vor allem bei der Ausgabe von Festkommawerten flexibler als die Manipulatoren.

Der erste Parameter der Funktion sprintf() ist der C-String, der nach dem Aufruf das Ergebnis enthält. Er ist großzügig zu bemessen, da sprintf() nicht prüfen kann, ob die Kapazität überschritten wird. Der zweite Parameter ist der Format-String, der angibt, wie die nachfolgenden Parameter dargestellt werden sollen. Die weiteren Parameter enthalten die Werte, die aufbereitet werden sollen. Die drei Punkte sind kein Zeichen für Ratlosigkeit, sondern ein Syntaxelement der Sprache C für beliebig viele Parameter.

Format-String

Der Format-String ist ein gewöhnlicher C-String, der an bestimmten Stellen Platzhalter enthält, die angeben, welchen Typ die in den weiteren Parametern angegeben Werte haben und wie sie auszurichten sind. Die Platzhalter werden durch ein Prozentzeichen eingeleitet. Beispiel:

sprintf(OutStr, "%s %s hat Kundennr. %d und %7.2f EUR Guthaben",
       VornameStr, NameStr, KdNr, Guthaben);

Die Variable OutStr sollte ein char-Array sein und wird nach der Ausführung des Befehls die Ausgabe von sprintf() enthalten. Der Formatstring enthält zunächst zwei String-Platzhalter (%s). Die nächsten zwei Parameter - hier sind es VornameStr und NameStr - müssen C-Strings sein. Der Text im Format-String »hat Kundennr.« wird danach in OutStr zu finden sein. Als nächstes wird ein ganzzahlige, dezimal dargestellter Wert erwartet (%d). Der Paramter KdNr sollte also ein Integer sein. Der letzte Platzhalter (%7.2f) stellt einen Fließkommawert mit festen sieben Vorkomma- und zwei Nachkommastellen dar. Es wird also der Format-String in die Zielvariable kopiert und die Platzhalterstellen durch die nachfolgenden Parameter aufgefüllt. Ein Platzhalter beginnt immer mit dem Prozentzeichen und endet mit dem Typkennzeichnungsbuchstaben. Die folgende Tabelle zeigt eine Zusammenfassung. [Typkennzeichen im printf-Format-String]
Typkennzeichen Bedeutung
d Dezimale Darstellung eines Ganzzahlwertes
x Hexadezimale Darstellung eines Ganzzahlwertes
f Festkommadarstellung eines double-Wertes
e Exponentialdarstellung eines double-Wertes
c Ein Zeichen darstellen
s Einen C-String darstellen

Platzhalter

Für jeden dieser Platzhalter muss ein Wert als weiterer Parameter an die Funktion übergeben werden. Dabei muss dieser Wert auch typkompatibel zum Platzhalter sein. Die Funktion selbst kann das nicht feststellen, und der Compiler kann dies nicht kontrollieren. Wenn Sie also eine falsche Anzahl oder falsche Typen übergeben, führt das schlimmstenfalls zum Absturz des Programms. Hier ist also sehr viel Sorgfalt notwendig.

Flags

Zwischen dem Prozentzeichen und dem Typkennzeichner können optional noch ein Flag, eine Breiten- und Genauigkeitsangabe und eine Typspezifizierung eingefügt sein. Das Flag kontrolliert die Art, wie der Freiraum bei Zahlen aufgefüllt wird. [Flags]
Flag} Bedeutung
0 Mit Nullen auffüllen
- Linksbündige Ausrichtung
Leerzeichen Bei positiven Zahlen wird ein Leerzeichen anstelle des
Plus angezeigt
+ Vorzeichen auch bei positiven Zahlen anzeigen

Spaltenbreite

Darauf folgt eine Zahlenkonstante, die angibt, wie viele Stellen für die Spalte minimal zur Verfügung stehen. Bei Fließkommawerten kann durch einen Punkt und eine weitere Zahlenkonstante angegeben werden, wie viele Nachkommastellen angezeigt werden sollen.

Modifizierer

Danach können Modifizierer angegeben werden, die den Typ genauer beschreiben. Dabei steht ein »h« für short und ein »l« für long. Ein »L« wird für long double verwendet. Die einfachste Form einer Umwandlung ist die Darstellung einer ganzzahligen Variable als String. Der Aufruf dazu lautet:

sprintf(ZielString, "%d", ZahlenWert);