C-Programmierung: Zeichenkettenfunktionen
Für die Bearbeitung von Strings stellt C eine Reihe von Bibliotheksfunktionen zur Verfügung. Um sie verwenden zu können, muss mit der Präprozessor-Anweisung #include
die Headerdatei string.h eingebunden werden.
strcpy
Bearbeitenchar* strcpy(char* Ziel, const char* Quelle);
Kopiert einen String in einen anderen (Quelle nach Ziel) und liefert Zeiger auf Ziel als Funktionswert. Bitte beachten Sie, dass eine Anweisung text2 = text1
für ein Array nicht möglich ist. Für eine Kopie eines Strings in einen anderen ist immer die Anweisung strcpy
nötig, da eine Zeichenkette immer zeichenweise kopiert werden muss.
Beispiel:
#include <stdio.h>
#include <string.h>
int main(void)
{
char text[20];
strcpy(text, "Hallo!");
printf("%s\n", text);
strcpy(text, "Ja Du!");
printf("%s\n", text);
return 0;
}
Ausgabe:
Hallo! Ja Du!
strncpy
Bearbeitenchar* strncpy(char* Ziel, const char* Quelle, size_t num);
Kopiert num
-Zeichen von Quelle
zu Ziel
. Wenn das Ende des Quelle
C-String (welches ein null-Character ('\0'
) signalisiert) gefunden wird, bevor num
-Zeichen kopiert sind, wird Ziel mit '\0'
-Zeichen aufgefüllt bis die komplette Anzahl von num
-Zeichen in Ziel
geschrieben ist.
Wichtig: strncpy()
fügt selbst keinen null-Character ('\0'
) an das Ende von Ziel
. Soll heißen: Ziel
wird nur null-terminiert wenn die Länge des C-Strings Quelle
kleiner ist als num
.
Beispiel:
#include <stdio.h>
#include <string.h>
int main ()
{
char strA[] = "Hallo!";
char strB[6];
strncpy(strB, strA, 5);
/* Nimm die Anzahl von Bytes in strB (6), ziehe 1 ab (= 5) um auf den letzten index zu kommen,
dann füge dort einen null-Terminierer ein. */
strB[sizeof(strB)-1] = '\0';
puts(strB);
return 0;
}
Vorsicht: Benutzen Sie sizeof()
in diesem Zusammenhang nur bei Character-Arrays. sizeof()
gibt die Anzahl der reservierten Bytes zurück. In diesem Fall: 6
(Größe von strB) * 1 Byte
(Character) = 6
.
Ausgabe:
Hallo
strcat
Bearbeitenchar* strcat(char* s1, const char* s2);
Verbindet zwei Zeichenketten miteinander. Das Stringende-Zeichen '\0'
von s1
wird überschrieben. Voraussetzung ist, dass der für s1
reservierte Speicherbereich ausreichend groß zur Aufnahme von s2
ist. Andernfalls ergibt sich undefiniertes Verhalten.
Beispiel:
#include <stdio.h>
#include <string.h>
int main(void)
{
char text[20];
strcpy(text, "Hallo!");
printf("%s\n", text);
strcat(text, "Ja, du!");
printf("%s\n", text);
return 0;
}
Ausgabe:
Hallo! Hallo!Ja, du!
Wie Sie sehen wird der String in Zeile 9 diesmal nicht überschrieben, sondern am Ende angehängt.
strncat
Bearbeitenchar* strncat(char* s1, const char* s2, size_t n);
Verbindet – so wie strcat()
– zwei Zeichenketten miteinander, wobei aber nur n
Elemente von s2
an s1
angehängt werden. An das Ende der Resultat-Zeichenfolge wird in jedem Fall ein '\0'
-Zeichen angehängt. Für überlappende Bereiche ist das Ergebnis – soweit nicht anders angegeben – nicht definiert.
Mit dieser Funktion kann beispielsweise sichergestellt werden, dass nicht in einen undefinierten Speicherbereich geschrieben wird. Dafür wäre n so zu wählen, dass der für s1
reservierte Speicherbereich nicht überschritten wird.
Beispiel:
#include <stdio.h>
#include <string.h>
int main(void)
{
char text[40];
strcpy(text, "Es werden nur zehn Zeichen ");
printf("%s\n", text);
strncat(text, "angehaengt, der Rest nicht.", 10);
printf("%s\n", text);
return 0;
}
Ausgabe:
Es werden nur zehn Zeichen Es werden nur zehn Zeichen angehaengt
strtok
Bearbeitenchar *strtok( char *s1, const char *s2 );
Diese Funktion zerlegt einen String s1
mit Hilfe des bzw. der in s2
gegebenen Trennzeichen (token) in einzelne Teil-Strings. s2
kann also eines oder auch mehrere Trennzeichen enthalten, das heißt
char s2[] = " ,\n.";
würde beispielsweise auf eine Trennung bei Space, Komma, New-Line oder Punkt hinauslaufen.
Anmerkung: Durch strtok()
wird der ursprüngliche String zerstört, dieser darf demzufolge niemals konstant (const
) sein. Weiters ist die Funktion wegen der internen Verwendung von statischem Speicher nicht multithread-fähig und nicht wiedereintrittsfähig (nicht reentrant).
Beispiel:
#include <stdio.h>
#include <string.h>
int main(void) {
char text[] = "Das ist ein Beispiel!";
char trennzeichen[] = " ";
char *wort;
int i=1;
wort = strtok(text, trennzeichen);
while(wort != NULL) {
printf("Token %d: %s\n", i++, wort);
wort = strtok(NULL, trennzeichen);
//Jeder Aufruf gibt das Token zurück. Das Trennzeichen wird mit '\0' überschrieben.
//Die Schleife läuft durch bis strtok() den NULL-Zeiger zurückliefert.
}
return 0;
}
Ausgabe:
Token1: Das Token2: ist Token3: ein Token4: Beispiel!
strcspn
Bearbeitenint strcspn(const char *string1, const char *string2);
Diese Funktion gibt die Anzahl der Zeichen am Anfang von string1
zurück, die nicht in string2
enthalten sind.
Beispiel:
#include <stdio.h>
#include <string.h>
int main(void){
char s1[] = "Das ist ein Text";
char s2[] = "tbc";
int cnt = strcspn(s1,s2);
printf("Anzahl der Zeichen am Anfang von '%s', die nicht in '%s' vorkommen: %d\n", s1, s2, cnt);
return 0;
}
Ausgabe:
Anzahl der Zeichen am Anfang von 'Das ist ein Text', die nicht in 'tbc' vorkommen: 6
strpbrk
Bearbeitenchar *strpbrk(const char *string1, const char *string2);
Gibt einen Zeiger auf das erste Zeichen in string1
zurück, das auch in string2
enthalten ist. Es wird also – wie auch bei Funktion strcspn()
– nicht nach einer Zeichenkette, sondern nach einem einzelnen Zeichen aus einer Zeichenmenge gesucht. War die Suche erfolglos, wird NULL
zurückgegeben.
Beispiel:
#include <stdio.h>
#include <string.h>
int main()
{
char string1[]="Schwein gehabt!";
char string2[]="aeiou";
printf("%s\n", strpbrk(string1, string2));
return 0;
}
Ausgabe:
ein gehabt!
strchr
Bearbeitenchar* strchr(char *string, int zeichen);
Diese Funktion sucht nach dem ersten Auftreten eines Zeichens zeichen
in einer Zeichenkette string
und gibt einen Zeiger auf dieses zurück. War die Suche erfolglos, wird NULL
zurückgegeben.
Beispiel 1:
#include <stdio.h>
#include <string.h>
int main()
{
char string[] = "Ein Teststring mit Worten";
printf("%s\n",strchr(string, (int)'W'));
printf("%s\n",strchr(string, (int)'T'));
return 0;
}
Ausgabe:
Worten Teststring mit Worten
Beispiel 2:
#include <stdio.h>
#include <string.h>
int main()
{
char string[]="Dies ist wichtig. Dies nicht.";
char *stelle;
stelle=strchr(string, (int)'.');
*(stelle+1)='\0'; /*Durch *(stelle+1) wird nicht der Punkt,
sondern das Leerzeichen (das Zeichen danach)
durch das Determinierungszeichen ersetzt*/
printf("%s", string);
return 0;
}
Ausgabe:
Dies ist wichtig.
strrchr
Bearbeitenchar *strrchr(const char *s, int ch);
Diese Funktion sucht im Unterschied zu strchr()
nicht nach dem ersten, sondern nach dem letzten Auftreten eines Zeichens ch
in einer Zeichenkette s
und gibt einen Zeiger auf dieses Zeichen zurück. War die Suche erfolglos, wird NULL
zurückgegeben.
Beispiel 1:
Hier nutzen wir fgets()
, um eine Zeichenkette von der Standard-Eingabe (stdin
) einzulesen. Wenn die Eingabe nun aber weniger Zeichen umfasst als die angegebene maximale Anzahl der einzulesenden Zeichen (im Beispiel unten wären es 20), endet die resultierende Zeichenkette mit einem New-Line-Zeichen (\n
). Um einen nullterminierten String zu erhalten, suchen wir daher mit einem Zeiger nach diesem und ersetzen es gegebenenfalls durch \0
.
#include <stdio.h>
#include <string.h>
int main()
{
char string[20];
char *ptr;
printf("Eingabe machen:\n");
fgets(string, 20 , stdin);
/* man setzt den zeiger auf das New-Line-Zeichen */
ptr = strrchr(string, '\n');
if( ptr != NULL )
{
/* \n-Zeichen mit \0 überschreiben */
*ptr = '\0';
}
printf("%s\n",string);
return 0;
}
Beispiel 2:
#include <stdio.h>
#include <string.h>
int main()
{
char string[]="Dies ist wichtig. Dies ist nicht wichtig";
char *ptr;
// suche Trennzeichen '.' vom Ende der Zeichenkette aus
ptr = strrchr (string, '.');
// wenn Trennzeichen im Text nicht vorhanden,
// dann ist der Pointer NULL, d.h. NULL muss abgefangen werden.
if (ptr != NULL) {
*ptr = '\0';
}
printf ("%s\n", string);
return 0;
}
Der Pointer ptr
zeigt nach strrchr()
genau auf die Speicherstelle des Strings, in der das erste Trennzeichen von hinten steht. Wenn man nun an diese Speicherstelle das Zeichenketteendezeichen \0
schreibt, dann ist der String für alle Stringfunktionen an dieser Stelle beendet.
printf()
gibt den String string
nur bis zum Zeichenketteendezeichen aus.
Ausgabe:
Dies ist wichtig
strcmp
Bearbeitenint strcmp(char* s1, char* s2);
Diese Funktion vergleicht zwei Zeichenketten miteinander, wobei Zeichen für Zeichen deren jeweilige ASCII-Codes verglichen werden. Wenn die beiden Strings identisch sind, gibt die Funktion den Wert 0 zurück. Sind sie unterschiedlich, liefert die Funktion einen Rückgabewert entweder größer oder kleiner 0: Ein Rückgabewert größer / kleiner 0 bedeutet, dass der ASCII-Code des ersten ungleichen Zeichens in s1
größer / kleiner ist als der des entsprechenden Zeichens in s2
.
Beispiel:
#include <stdio.h>
#include <string.h>
int main()
{
const char string1[] = "Hello";
const char string2[] = "World";
const char string3[] = "Hello";
if (strcmp(string1,string2) == 0)
{
printf("Die beiden Zeichenketten %s und %s sind identisch.\n",string1,string2);
}
else
{
printf("Die beiden Zeichenketten %s und %s sind unterschiedlich.\n",string1,string2);
}
if (strcmp(string1,string3) == 0)
{
printf("Die beiden Zeichenketten %s und %s sind identisch.\n",string1,string3);
}
else
{
printf("Die beiden Zeichenketten %s und %s sind unterschiedlich.\n",string1,string3);
}
return 0;
}
Ausgabe:
Die beiden Zeichenketten Hello und World sind unterschiedlich. Die beiden Zeichenketten Hello und Hello sind identisch.
strncmp
Bearbeitenint strncmp(const char *x, const char *y, size_t n);
Diese Funktion arbeitet so wie strcmp()
– mit dem einzigen Unterschied, dass nur die ersten n
Zeichen der beiden Strings miteinander verglichen werden. Auch der Rückgabewert entspricht dem von strcmp()
.
Beispiel:
#include <stdio.h>
#include <string.h>
int main()
{
const char x[] = "aaaa";
const char y[] = "aabb";
int i;
for(i = strlen(x); i > 0; --i)
{
if(strncmp( x, y, i) != 0)
printf("Die ersten %d Zeichen der beiden Strings "\
"sind nicht gleich\n", i);
else
{
printf("Die ersten %d Zeichen der beiden Strings "\
"sind gleich\n", i);
break;
}
}
return 0;
}
Ausgabe:
Die ersten 4 Zeichen der beiden Strings sind nicht gleich Die ersten 3 Zeichen der beiden Strings sind nicht gleich Die ersten 2 Zeichen der beiden Strings sind gleich
strspn
Bearbeitenint strspn(const char *string1, const char *string2);
Diese Funktion gibt die Anzahl der Zeichen am Anfang von string1
zurück, die in string2
enthalten sind.
Beispiel:
#include <stdio.h>
#include <string.h>
int main()
{
const char string[] = "7501234-123";
int cnt = strspn(string, "0123456789");
printf("Anzahl der Ziffern am Anfang von '%s': %d\n", string, cnt);
return 0;
}
Ausgabe:
Anzahl der Ziffern am Anfang von '7501234-123': 7
strlen
Bearbeitensize_t strlen(const char *string1);
Diese Funktion gibt die Länge eines Strings string1
(ohne das abschließenden Nullzeichen) zurück.
Beispiel:
#include <stdio.h>
#include <string.h>
int main()
{
char string1[] = "Das ist ein Test";
size_t length;
length = strlen(string1);
printf("Der String \"%s\" hat %d Zeichen\n", string1, length);
return 0;
}
Ausgabe:
Der String "Das ist ein Test" hat 16 Zeichen
strstr
Bearbeitenchar *strstr(const char *s1, const char *s2);
Sucht nach dem ersten Vorkommen der Zeichenfolge s2
(ohne dem abschließenden Nullzeichen) in der Zeichenfolge s1
und gibt einen Zeiger auf die gefundene Zeichenfolge (innerhalb s1
) zurück. Ist die Länge der Zeichenfolge s2
0, so wird der Zeiger auf s1
geliefert; war die Suche erfolglos, wird NULL
zurückgegeben.
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] = "Dies ist ein simpler string";
char *ptr;
// setzt den Pointer ptr an die Textstelle "simpler"
ptr = strstr (str, "simpler");
// ersetzt den Text an der Stelle des Pointers mit "Beispiel"
strncpy (ptr, "Beispiel", 8);
puts (str);
return 0;
}
Ausgabe:
Dies ist ein Beispielstring
Gefahren
BearbeitenBei der Verarbeitung von Strings muss man aufpassen, nicht über das Ende eines Speicherbereiches hinauszuschreiben oder zu -lesen. Generell sind Funktionen wie strcpy()
und sprintf()
zu vermeiden und stattdessen strncpy()
und snprintf()
zu verwenden, weil dort die Größe des jeweiligen Speicherbereiches angegeben werden kann.
Beispiel:
#include <string.h>
#include <stdio.h>
int main(void)
{
char text[20];
strcpy(text, "Dies ist kein feiner Programmtest"); // Absturzgefahr, da Zeichenkette zu lang
strncpy(text, "Dies ist ein feiner Programmtest", sizeof(text));
printf("Die Laenge ist %u\n", strlen(text)); // Absturzgefahr, da Zeichenkette 'text' nicht terminiert
// also vorsichtshalber mit \0 abschliessen.
text[sizeof(text)-1] = '\0';
printf("Die Laenge von '%s' ist %u \n", text, strlen(text));
return 0;
}
Die beiden Zeilen 8 und 11 bringen das Programm möglicherweise zum Absturz:
- Zeile 8:
strcpy()
versucht mehr Zeichen zu schreiben, als in der Variable vorhanden sind, was möglicherweise zu einem Speicherzugriffsfehler führt. - Zeile 11: Falls das Programm in Zeile 8 noch nicht abstürzt, geschieht das eventuell jetzt. In Zeile 10 werden genau 20 Zeichen kopiert, was prinzipiell in Ordnung ist. Weil aber der Platz nicht ausreicht, wird die abschließende
\0
ausgespart, die Zeichenkette ist also nicht terminiert. Die Funktionstrlen()
benötigt aber genau diese\0
, um die Länge zu bestimmen. Tritt dieses Zeichen nicht auf, kann es zu einem Speicherzugriffsfehler kommen.
Entfernt man die beiden Zeilen 8 und 11 ergibt sich folgende Ausgabe:
Die Laenge von 'Dies ist ein feiner' ist 19
Es ist klar, dass sich hier als Länge 19 ergibt, denn ein Zeichen wird eben für das Nullzeichen verbraucht. Man muss also immer daran denken, ein zusätzliches Byte dafür einzurechnen.
Iterieren durch eine Zeichenkette
BearbeitenDie folgende Funktion replace_character()
ersetzt in einem String ein Zeichen durch ein anderes, ihr Rückgabewert ist die Anzahl der Ersetzungen.
#include <string.h>
#include <stdio.h>
unsigned replace_character(char* string, char from, char to)
{
unsigned result = 0;
if (!string) return 0;
while (*string != '\0')
{
if (*string == from)
{
*string = to;
result++;
}
string++;
}
return result;
}
int main(void)
{
char text[50] = "Dies ist ein feiner Programmtest";
unsigned result;
result = replace_character(text, 'e', ' ');
printf("%u Ersetzungen: %s\n", result, text);
result = replace_character(text, ' ', '#');
printf("%u Ersetzungen: %s\n", result, text);
return 0;
}
Der Vergleich der einzelnen Zeichen von char *string
mit char from
wird mit einer Schleife bewerkstelligt. Der Zeiger string
(diese Schreibweise entspricht ja &string[]
) verweist anfangs auf die Adresse des ersten Zeichens – durch die Dereferenzierung (*string
) erhält man also dieses Zeichen selbst. Am Ende jedes Schleifendurchlaufes wird dieser Zeiger um eins erhöht, also auf das nächste Zeichen gesetzt. Falls die beiden verglichenen Zeichen identisch sind, wird das jeweiligen Zeichen *string
durch to
ersetzt.
Ausgabe:
5 Ersetzungen: Di s ist in f in r Programmt st 9 Ersetzungen: Di#s#ist##in#f#in#r#Programmt#st
Die Bibliothek ctype.h
BearbeitenWie wir bereits im Kapitel Variablen und Konstanten gesehen haben, sagt der C-Standard nichts über den verwendeten Zeichensatz aus. Nehmen wir beispielsweise an, wir wollen testen, ob in der Variable c
ein Buchstabe gespeichert ist. Wir verwenden dazu die Bedingung
if ('A' <= c && c <= 'Z' || 'a' <= c && c <= 'z')
Unglücklicherweise funktioniert diese Bedingung zwar mit dem ASCII-, nicht aber dem EBCDIC-Zeichensatz. Der Grund dafür ist, dass die Buchstaben beim EBCDIC-Zeichensatz nicht hintereinander stehen.
Wer eine plattformunabhängige Lösung sucht, kann deshalb auf Funktionen der Standardbibliothek zurückgreifen, deren Prototypen alle in der Headerdatei ctype.h
definiert sind. Für den Test auf Buchstaben können wir beispielsweise die Funktion int isalpha(int c)
benutzen. Alle Funktionen, die in der Headerdatei ctype.h
deklariert sind, liefern einen Wert ungleich 0 zurück wenn die entsprechende Bedingung erfüllt ist, andernfalls liefern sie 0 zurück.
Weitere Funktionen von ctype.h
sind:
int isalnum(int c)
testet auf alphanumerisches Zeichen (a-z, A-Z, 0-9)int isalpha(int c)
testet auf Buchstabe (a-z, A-Z)int iscntrl(int c)
testet auf Steuerzeichen ('\f', '\n', '\t' ...)int isdigit(int c)
testet auf Dezimalziffer (0-9)int isgraph(int c)
testet auf druckbare Zeichenint islower(int c)
testet auf Kleinbuchstaben (a-z)int isprint(int c)
testet auf druckbare Zeichen ohne Leerzeichenint ispunct(int c)
testet auf druckbare Interpunktionszeichenint isspace(int c)
testet auf Zwischenraumzeichen (engl. whitespace) (' ','\f','\n','\r','\t','\v')int isupper(int c)
testet auf Großbuchstaben (A-Z)int isxdigit(int c)
testet auf hexadezimale Ziffern (0-9, a-f, A-F)
Zusätzlich sind noch zwei Funktionen für die Umwandlung in Groß- bzw. Kleinbuchstaben definiert:
int tolower(int c)
wandelt Groß- in Kleinbuchstaben umint toupper(int c)
wandelt Klein- in Großbuchstaben um