9.1. Ein- und Ausgabe von Zeichenketten
Die uns mittlerweile hinreichend bekannten Ein- bzw. Ausgabe-Funktionen scanf() bzw. printf() kommen auch in Betracht, um Zeichenketten einzulesen oder auszugeben. Beide kennen die Format-Zeichenkette %s, um Argumente als Strings zu behandeln. Unter Berücksichtigung der zusätzlich zur Verfügung stehenden Modifizierer bieten sie erhebliche Möglichkeiten der Ein- und Ausgabe-Kontrolle.
Folgendes Beispiel demonstriert die Anwendung von scanf() bzw. printf() auf Strings:
#include <stdio.h>
int main(void){
char szHello[21]; /* Platz für 20 Zeichen + '\0' */
printf("\nGeben Sie eine originelle Begrüßung ein! "
"(max. 20 Zeichen)\n");
/* Für szHello ist kein Adreßoperator erforderlich ! */
scanf("%20s", szHello);
printf("szHello: %s\n", szHello);
printf("Sagen Sie zum Abschied ");
fflush(stdin);
scanf("%20['a-z']s", szHello);
/* formatiert die Ausgabe rechtsbündig und
* füllt links bei Bedarf mit Leerzeichen auf
*/
printf("szHello: %20s\n", szHello);
return 0;
}
Die Feldbreitenangabe mit 20 bei scanf() stellt sicher, daß nicht mehr Zeichen eingelesen werden, als in szHello Platz finden. Auf diese Weise steht dem Programmierer ein Kontrollmechanismus zur Verfügung, mit dessen Hilfe ein Überlauf von char-Arrays vermieden werden kann. Der zweite Aufruf von scanf() zeigt zudem den Einsatz sogenannter Scansets: Die Angabe ['a-z'] läßt nur die Eingabe von Kleinbuchstaben zu (unter Ausschluß von Umlauten!) und bewirkt beim Auftreten eines anderen Zeichens den Abbruch des Einlesevorgangs.
Wenn Sie der Meinung sind, daß "Hello world" ein origineller Gruß ist, dann werden Sie bei der Ausführung dieses Beispielprogramms schnell feststellen, daß printf() immer nur "Hello" ausgibt. Das liegt daran, daß scanf(), sobald es ein sogenanntes "Whitespace"-Zeichen erkennt, den Eingabestrom auf die nächste Variable richtet oder - falls eine solche nicht vorhanden ist — abbricht.
Sobald Sie also längere Zeichenketten einlesen wollen, in denen auch Leerzeichen vorkommen sollen, dann ist scanf() keine gute Wahl. Als Alternative bietet sich gets() an (steht für "get string"). Sein Pendant auf der Ausgabeseite ist puts() (steht für "put string").
#include <stdio.h>
int main(void){
char szHello[21];
printf("\nGeben Sie eine originelle Begrüßung ein! "
"(max. 20 Zeichen)\n");
gets(szHello);
puts("szHello:");
puts(szHello);
return 0;
}
Zwei Dinge fallen im Vergleich zum vorigen Beispiel sofort auf: Die Funktion gets() liest bis zum Auftreten eines (␍) nach szHello ein. Zur Ausgabe der Information, die voher printf() erledigte, benötigt puts() zwei Anläufe. Letzteres liegt daran, daß puts() nur ein Argument — einen Pointer auf eine Zeichenkette — akzeptiert. Für die konstante Zeichenkette und den Zeiger szHello sind daher zwei seperate Aufrufe nötig. Im Gegensatz zu printf() hängt puts() immer einen Zeilenvorschub ans Ende der Zeichenkette an. Sofern es bloß um eine einfache Ausgabe von Zeichenketten geht, ist puts() gut geeignet, da es den "Overhead" einer so komplexen Funktion wie printf() vermeidet.
Die Funktion gets() akzeptiert ebenfalls nur ein Argument, nämlich einen char-Pointer auf einen Speicherbereich, an dem die eingelesenen Zeichen gespeichert werden sollen. Das abschließende (␍) wird nicht Teil der Zeichenkette, stattdessen fügt gets() den Wert '\0' an. Das größte Manko von gets() besteht allerdings darin, daß es keinerlei Schutz gegen das Hinausschreiben über die Array-Grenzen bietet. Deshalb sollte sein Einsatz vermieden und stattdessen die Funktion fgets() verwendet werden. Diese Funktion ist in stdio.h deklariert als
char *fgets(char *Ziel,
int MaxZeichen,
FILE *Eingabestrom)
Wie Ihnen vielleicht auffällt, ist fgets() nicht in erste Linie für die Eingabe von der Tastatur konzipiert, sondern kommt mit allen möglichen Eingabequellen zurecht. Wenn fgets() von der Standardeingabe lesen soll, muß das dritte Argument stdin lauten. Das erste Argument ist wieder ein Pointer auf einen Puffer, an dem die eingegebenen Daten abgelegt werden sollen, das zweite Argument hingegen begrenzt die Anzahl der akzeptierten Zeichen. fgets() bricht entweder ab, sobald es ein (␍) entdeckt oder MaxZeichen-1 Zeichen eingelesen wurden; wie gets() hängt fgets() noch '\0' ans Ende der Zeichenkette. Im Unterschied zu gets() verwirft es jedoch nicht den abschließenden Zeilenvorschub: Um dessen Enfernung muß sich der Programmierer selbst kümmern (siehe dazu Beispiel fgets.c in Abschnitt 9.3.4).