C Language
Zeichenketten
Suche…
Einführung
In C ist eine Zeichenfolge kein intrinsischer Typ. Ein C-String ist die Konvention, um ein eindimensionales Array von Zeichen zu haben, das durch ein Nullzeichen und ein '\0'
.
Dies bedeutet, dass ein C-String mit dem Inhalt "abc"
die vier Zeichen 'a'
, 'b'
, 'c'
und '\0'
.
Siehe die grundlegende Einführung zu Strings .
Syntax
- char str1 [] = "Hallo, Welt!"; / * Modifizierbar * /
- char str2 [14] = "Hallo, Welt!"; / * Modifizierbar * /
- char * str3 = "Hallo, Welt!"; /* Nicht veränderbar*/
Länge berechnen: strlen ()
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char **argv)
{
/* Exit if no second argument is found. */
if (argc != 2)
{
puts("Argument missing.");
return EXIT_FAILURE;
}
size_t len = strlen(argv[1]);
printf("The length of the second argument is %zu.\n", len);
return EXIT_SUCCESS;
}
Dieses Programm berechnet die Länge seines zweiten Eingabearrements und speichert das Ergebnis in len
. Diese Länge wird dann an das Terminal ausgegeben. Wenn Sie zum Beispiel mit den Parametern program_name "Hello, world!"
, Das Programm gibt aus. The length of the second argument is 13.
Weil die Zeichenfolge Hello, world!
ist 13 Zeichen lang.
strlen
zählt alle Bytes vom Anfang des Strings bis zum abschließenden NUL-Zeichen '\0'
, schließt dieses jedoch nicht ein. Daher kann es nur verwendet werden, wenn garantiert ist , dass der String NUL-terminiert ist.
strlen
Sie außerdem, dass strlen
Ihnen nicht sagt, wie viele Zeichen die Zeichenfolge enthält, wenn die Zeichenfolge Unicode-Zeichen enthält (da einige Zeichen möglicherweise mehrere Byte lang sind). In solchen Fällen müssen Sie die Zeichen ( dh Codeeinheiten) selbst zählen. Betrachten Sie die Ausgabe des folgenden Beispiels:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char asciiString[50] = "Hello world!";
char utf8String[50] = "Γειά σου Κόσμε!"; /* "Hello World!" in Greek */
printf("asciiString has %zu bytes in the array\n", sizeof(asciiString));
printf("utf8String has %zu bytes in the array\n", sizeof(utf8String));
printf("\"%s\" is %zu bytes\n", asciiString, strlen(asciiString));
printf("\"%s\" is %zu bytes\n", utf8String, strlen(utf8String));
}
Ausgabe:
asciiString has 50 bytes in the array
utf8String has 50 bytes in the array
"Hello world!" is 12 bytes
"Γειά σου Κόσμε!" is 27 bytes
Kopieren und Verketten: strcpy (), strcat ()
#include <stdio.h>
#include <string.h>
int main(void)
{
/* Always ensure that your string is large enough to contain the characters
* and a terminating NUL character ('\0')!
*/
char mystring[10];
/* Copy "foo" into `mystring`, until a NUL character is encountered. */
strcpy(mystring, "foo");
printf("%s\n", mystring);
/* At this point, we used 4 chars of `mystring`, the 3 characters of "foo",
* and the NUL terminating byte.
*/
/* Append "bar" to `mystring`. */
strcat(mystring, "bar");
printf("%s\n", mystring);
/* We now use 7 characters of `mystring`: "foo" requires 3, "bar" requires 3
* and there is a terminating NUL character ('\0') at the end.
*/
/* Copy "bar" into `mystring`, overwriting the former contents. */
strcpy(mystring, "bar");
printf("%s\n", mystring);
return 0;
}
Ausgänge:
foo
foobar
bar
Wenn Sie an eine vorhandene Zeichenfolge anhängen oder von dieser kopieren oder kopieren, stellen Sie sicher, dass sie NUL-terminiert ist!
String-Literale (zB "foo"
) werden vom Compiler immer NUL-terminiert.
Vergleich: strcmp (), strncmp (), strcasecmp (), strncasecmp ()
Die strcase*
-Funktionen sind nicht Standard C, sondern eine POSIX-Erweiterung.
Die Funktion strcmp
vergleicht lexikographisch zwei nullterminierte Zeichenarrays. Die Funktionen geben einen negativen Wert zurück, wenn das erste Argument vor dem zweiten in lexikografischer Reihenfolge erscheint, Null, wenn sie gleich sind, oder positiv, wenn das erste Argument nach dem zweiten in lexikografischer Reihenfolge erscheint.
#include <stdio.h>
#include <string.h>
void compare(char const *lhs, char const *rhs)
{
int result = strcmp(lhs, rhs); // compute comparison once
if (result < 0) {
printf("%s comes before %s\n", lhs, rhs);
} else if (result == 0) {
printf("%s equals %s\n", lhs, rhs);
} else { // last case: result > 0
printf("%s comes after %s\n", lhs, rhs);
}
}
int main(void)
{
compare("BBB", "BBB");
compare("BBB", "CCCCC");
compare("BBB", "AAAAAA");
return 0;
}
Ausgänge:
BBB equals BBB
BBB comes before CCCCC
BBB comes after AAAAAA
Als strcmp
vergleicht die strcasecmp
Funktion auch lexikographisch ihre Argumente, nachdem sie jedes Zeichen in einen Kleinbuchstaben übersetzt haben:
#include <stdio.h>
#include <string.h>
void compare(char const *lhs, char const *rhs)
{
int result = strcasecmp(lhs, rhs); // compute case-insensitive comparison once
if (result < 0) {
printf("%s comes before %s\n", lhs, rhs);
} else if (result == 0) {
printf("%s equals %s\n", lhs, rhs);
} else { // last case: result > 0
printf("%s comes after %s\n", lhs, rhs);
}
}
int main(void)
{
compare("BBB", "bBB");
compare("BBB", "ccCCC");
compare("BBB", "aaaaaa");
return 0;
}
Ausgänge:
BBB equals bBB
BBB comes before ccCCC
BBB comes after aaaaaa
strncmp
und strncasecmp
vergleichen höchstens n Zeichen:
#include <stdio.h>
#include <string.h>
void compare(char const *lhs, char const *rhs, int n)
{
int result = strncmp(lhs, rhs, n); // compute comparison once
if (result < 0) {
printf("%s comes before %s\n", lhs, rhs);
} else if (result == 0) {
printf("%s equals %s\n", lhs, rhs);
} else { // last case: result > 0
printf("%s comes after %s\n", lhs, rhs);
}
}
int main(void)
{
compare("BBB", "Bb", 1);
compare("BBB", "Bb", 2);
compare("BBB", "Bb", 3);
return 0;
}
Ausgänge:
BBB equals Bb
BBB comes before Bb
BBB comes before Bb
Tokenisierung: strtok (), strtok_r () und strtok_s ()
Die Funktion strtok
zerlegt einen String in kleinere Strings oder Token, wobei ein Satz von Trennzeichen verwendet wird.
#include <stdio.h>
#include <string.h>
int main(void)
{
int toknum = 0;
char src[] = "Hello,, world!";
const char delimiters[] = ", !";
char *token = strtok(src, delimiters);
while (token != NULL)
{
printf("%d: [%s]\n", ++toknum, token);
token = strtok(NULL, delimiters);
}
/* source is now "Hello\0, world\0\0" */
}
Ausgabe:
1: [Hello]
2: [world]
Die Begrenzungszeichenfolge kann ein oder mehrere Begrenzungszeichen enthalten, und bei jedem Aufruf von strtok
können unterschiedliche Begrenzungszeichenfolgen verwendet werden.
Aufrufe von strtok
, um die Tokenisierung derselben strtok
fortzusetzen, sollten die Quellzeichenfolge nicht erneut übergeben, sondern NULL
als erstes Argument. Wenn die gleiche Quelle String übergeben wird , dann wird das erste Token stattdessen wieder in Token aufgeteilt werden. Das heißt, mit denselben Trennzeichen würde strtok
das erste Token einfach wieder zurückgeben.
Beachten Sie, dass als strtok
keine neue Speicher zuteilt für die Token, die Quellzeichenfolge ändert. Das heißt, im obigen Beispiel wird der String src
so manipuliert, dass er die Token erzeugt, auf die der Zeiger verweist, der von den Aufrufen von strtok
. Das bedeutet, dass die Quellzeichenfolge nicht const
(also kein String-Literal sein kann). Dies bedeutet auch, dass die Identität des Begrenzungsbytes verloren geht (dh in dem Beispiel werden die Zeichen "," und "!" Effektiv aus dem Quellstring gelöscht und Sie können nicht erkennen, welches Trennzeichen übereinstimmt).
Beachten Sie auch, dass mehrere aufeinanderfolgende Trennzeichen in der Quellzeichenfolge als eins behandelt werden. In diesem Beispiel wird das zweite Komma ignoriert.
strtok
ist weder threadsicher noch neu strtok
da beim Parsing ein statischer Puffer verwendet wird. Das heißt, wenn eine Funktion strtok
, kann keine Funktion, die sie während der Verwendung von strtok
strtok
, auch strtok
, und sie kann nicht von einer Funktion aufgerufen werden, die selbst strtok
.
Ein Beispiel, das die Probleme strtok
die durch die Tatsache verursacht werden, dass strtok
nicht erneut strtok
lautet wie folgt:
char src[] = "1.2,3.5,4.2";
char *first = strtok(src, ",");
do
{
char *part;
/* Nested calls to strtok do not work as desired */
printf("[%s]\n", first);
part = strtok(first, ".");
while (part != NULL)
{
printf(" [%s]\n", part);
part = strtok(NULL, ".");
}
} while ((first = strtok(NULL, ",")) != NULL);
Ausgabe:
[1.2]
[1]
[2]
Die erwartete Operation besteht darin, dass die äußere do while
Schleife drei Token erstellen sollte, die aus jeder Dezimalzahl-Zeichenfolge ( "1.2"
, "3.5"
, "4.2"
) bestehen, für die jeder strtok
die innere Schleife strtok
Ziffernfolgen ( "1"
, "2"
, "3"
, "5"
, "4"
, "2"
).
Da strtok
jedoch nicht erneut strtok
, tritt dies nicht auf. Stattdessen erstellt der erste strtok
das Token "1.2 \ 0" richtig und die innere Schleife die Token "1"
und "2"
. Dann befindet sich der strtok
in der äußeren Schleife am Ende der Zeichenfolge, die von der inneren Schleife verwendet wird, und gibt NULL sofort zurück. Der zweite und dritte Teilstring des src
Arrays werden überhaupt nicht analysiert.
Die Standard-C-Bibliotheken enthalten keine Thread-sichere oder wiedereintretende Version, andere jedoch, wie beispielsweise POSIX ' strtok_r
. Beachten Sie, dass auf MSVC das strtok
Äquivalent strtok_s
Thread-sicher ist.
C11 hat einen optionalen Teil, Annex K, der eine thread-sichere und wiedereintretende Version namens strtok_s
. Sie können mit __STDC_LIB_EXT1__
auf die Funktion __STDC_LIB_EXT1__
. Dieser optionale Teil wird nicht allgemein unterstützt.
Die Funktion strtok_s
unterscheidet sich von der POSIX-Funktion strtok_r
durch das strtok_r
vor dem Speichern außerhalb der Zeichenkette, die mit Token versehen wird, und durch Überprüfung der Laufzeiteinschränkungen. Bei korrekt geschriebenen Programmen verhalten sich strtok_s
und strtok_r
gleich.
Wenn Sie strtok_s
mit dem Beispiel verwenden, erhalten Sie jetzt die richtige Antwort:
/* you have to announce that you want to use Annex K */
#define __STDC_WANT_LIB_EXT1__ 1
#include <string.h>
#ifndef __STDC_LIB_EXT1__
# error "we need strtok_s from Annex K"
#endif
char src[] = "1.2,3.5,4.2";
char *next = NULL;
char *first = strtok_s(src, ",", &next);
do
{
char *part;
char *posn;
printf("[%s]\n", first);
part = strtok_s(first, ".", &posn);
while (part != NULL)
{
printf(" [%s]\n", part);
part = strtok_s(NULL, ".", &posn);
}
}
while ((first = strtok_s(NULL, ",", &next)) != NULL);
Und die Ausgabe wird sein:
[1.2]
[1]
[2]
[3.5]
[3]
[5]
[4.2]
[4]
[2]
Finden Sie das erste / letzte Vorkommen eines bestimmten Zeichens: strchr (), strrchr ()
Die Funktionen strchr
und strrchr
finden ein Zeichen in einer Zeichenfolge, das sich in einem NUL-terminierten Zeichenarray befindet. strchr
einen Zeiger auf das erste Vorkommen zurück und strrchr
auf das letzte Vorkommen.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void)
{
char toSearchFor = 'A';
/* Exit if no second argument is found. */
if (argc != 2)
{
printf("Argument missing.\n");
return EXIT_FAILURE;
}
{
char *firstOcc = strchr(argv[1], toSearchFor);
if (firstOcc != NULL)
{
printf("First position of %c in %s is %td.\n",
toSearchFor, argv[1], firstOcc-argv[1]); /* A pointer difference's result
is a signed integer and uses the length modifier 't'. */
}
else
{
printf("%c is not in %s.\n", toSearchFor, argv[1]);
}
}
{
char *lastOcc = strrchr(argv[1], toSearchFor);
if (lastOcc != NULL)
{
printf("Last position of %c in %s is %td.\n",
toSearchFor, argv[1], lastOcc-argv[1]);
}
}
return EXIT_SUCCESS;
}
Ausgaben (nachdem eine ausführbare Datei mit dem Namen pos
generiert wurde):
$ ./pos AAAAAAA
First position of A in AAAAAAA is 0.
Last position of A in AAAAAAA is 6.
$ ./pos BAbbbbbAccccAAAAzzz
First position of A in BAbbbbbAccccAAAAzzz is 1.
Last position of A in BAbbbbbAccccAAAAzzz is 15.
$ ./pos qwerty
A is not in qwerty.
strrchr
wird häufig verwendet, um einen Dateinamen aus einem Pfad zu extrahieren. Zum Beispiel zum Extrahieren von myfile.txt
aus C:\Users\eak\myfile.txt
:
char *getFileName(const char *path)
{
char *pend;
if ((pend = strrchr(path, '\')) != NULL)
return pend + 1;
return NULL;
}
Iteration über die Zeichen in einer Zeichenfolge
Wenn wir die Länge der Zeichenfolge kennen, können wir eine for-Schleife verwenden, um ihre Zeichen zu durchlaufen:
char * string = "hello world"; /* This 11 chars long, excluding the 0-terminator. */
size_t i = 0;
for (; i < 11; i++) {
printf("%c\n", string[i]); /* Print each character of the string. */
}
Alternativ können wir die Standardfunktion strlen()
, um die Länge einer Zeichenfolge zu ermitteln, wenn wir nicht wissen, was die Zeichenfolge ist:
size_t length = strlen(string);
size_t i = 0;
for (; i < length; i++) {
printf("%c\n", string[i]); /* Print each character of the string. */
}
Schließlich können wir die Tatsache ausnutzen, dass die Zeichenfolgen in C garantiert null-terminiert sind (was wir bereits bei der Übergabe an strlen()
im vorherigen Beispiel strlen()
). Wir können das Array unabhängig von seiner Größe durchlaufen und die Iteration abbrechen, sobald wir ein Null-Zeichen erreicht haben:
size_t i = 0;
while (string[i] != '\0') { /* Stop looping when we reach the null-character. */
printf("%c\n", string[i]); /* Print each character of the string. */
i++;
}
Grundlegende Einführung in Strings
In C ist eine Zeichenfolge eine Folge von Zeichen, die durch ein Nullzeichen ('\ 0') abgeschlossen wird.
Wir können Strings mit String-Literalen erstellen, bei denen es sich um Zeichenfolgen handelt, die von doppelten Anführungszeichen umgeben sind. Nehmen Sie zum Beispiel den String-Literal "hello world"
. String-Literale werden automatisch mit Nullen abgeschlossen.
Wir können Strings mit verschiedenen Methoden erstellen. Zum Beispiel können wir ein char *
deklarieren und es so initialisieren, dass es auf das erste Zeichen einer Zeichenfolge zeigt:
char * string = "hello world";
Beim Initialisieren eines char *
mit einer String-Konstante wie oben wird der String selbst normalerweise in schreibgeschützten Daten zugewiesen. string
ist ein Zeiger auf das erste Element des Arrays, das das Zeichen 'h'
.
Da das String-Literal im Nur-Lese-Speicher zugewiesen wird, kann es nicht geändert werden 1 . Jeder Versuch, es zu ändern, führt zu undefiniertem Verhalten. Es ist daher besser, const
hinzuzufügen, um einen Kompilierungsfehler wie diesen zu erhalten
char const * string = "hello world";
Es hat einen ähnlichen Effekt 2 wie
char const string_arr[] = "hello world";
Um eine modifizierbare Zeichenfolge zu erstellen, können Sie ein Zeichenarray deklarieren und dessen Inhalt mit einem Zeichenfolgenliteral wie folgt initialisieren:
char modifiable_string[] = "hello world";
Dies entspricht dem Folgenden:
char modifiable_string[] = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '\0'};
Da die zweite Version einen geschweiften Klammerinitialisierer verwendet, wird die Zeichenfolge nicht automatisch mit einem Nullwert abgeschlossen, es sei denn, ein '\0'
Zeichen ist explizit im Zeichenarray enthalten, normalerweise als letztes Element.
1 Nicht modifizierbar bedeutet , dass die Zeichen im String können nicht geändert werden, aber denken Sie daran , dass der Zeiger string
geändert werden kann (kann woanders Punkt oder kann erhöht oder verringert werden).
2 Beide Zeichenfolgen haben den gleichen Effekt, dass Zeichen beider Zeichenfolgen nicht geändert werden können. Es ist zu beachten, dass string
ein Zeiger auf char
und ein modifizierbarer l-Wert ist. Er kann also inkrementiert werden oder auf eine andere Position zeigen, während das Array string_arr
ein nicht modifizierbarer l-Wert ist.
Erstellen von Arrays von Strings
Ein Array von Strings kann einige Dinge bedeuten:
- Ein Array, dessen Elemente
char *
s sind - Ein Array, dessen Elemente Arrays von
char
Wir können ein Array von Zeichenzeigern so erstellen:
char * string_array[] = {
"foo",
"bar",
"baz"
};
Denken Sie daran: Wenn Sie char *
Zeichenfolgenliterale zuweisen, werden die Zeichenfolgen selbst im Nur-Lese-Speicher zugeordnet. Das Array string_array
wird jedoch im Lese- / Schreibspeicher zugewiesen. Dies bedeutet, dass wir die Zeiger im Array ändern können, nicht aber die Zeichenfolgen, auf die sie zeigen.
In C ist der Parameter für main argv
(das Array von Befehlszeilenargumenten, das bei Ausführung des Programms übergeben wurde) ein Array von char *
: char * argv[]
.
Wir können auch Arrays von Zeichenarrays erstellen. Da Zeichenfolgen Arrays von Zeichen sind, handelt es sich bei einem Array von Zeichenfolgen einfach um ein Array, dessen Elemente Zeichenarrays sind:
char modifiable_string_array_literals[][4] = {
"foo",
"bar",
"baz"
};
Das ist äquivalent zu:
char modifiable_string_array[][4] = {
{'f', 'o', 'o', '\0'},
{'b', 'a', 'r', '\0'},
{'b', 'a', 'z', '\0'}
};
Beachten Sie, dass wir als Größe der zweiten Dimension des Arrays 4
angeben. Jede der Zeichenfolgen in unserem Array besteht aus 4 Bytes, da das nullterminierende Zeichen eingefügt werden muss.
strstr
/* finds the next instance of needle in haystack
zbpos: the zero-based position to begin searching from
haystack: the string to search in
needle: the string that must be found
returns the next match of `needle` in `haystack`, or -1 if not found
*/
int findnext(int zbpos, const char *haystack, const char *needle)
{
char *p;
if (((p = strstr(haystack + zbpos, needle)) != NULL)
return p - haystack;
return -1;
}
strstr
sucht der haystack
(ersten) Argument für den String , auf die needle
. Wenn gefunden, gibt strstr
die Adresse des Vorkommens zurück. Wenn die needle
nicht gefunden werden konnte, wird NULL zurückgegeben. Wir verwenden zbpos
damit wir nicht immer und immer wieder dieselbe Nadel finden. Um die erste Instanz zu überspringen, fügen wir einen Versatz von zbpos
. Ein Notepad-Klon könnte findnext
so aufrufen, um den Dialog " findnext
" zu implementieren:
/*
Called when the user clicks "Find Next"
doc: The text of the document to search
findwhat: The string to find
*/
void onfindnext(const char *doc, const char *findwhat)
{
static int i;
if ((i = findnext(i, doc, findwhat)) != -1)
/* select the text starting from i and ending at i + strlen(findwhat) */
else
/* display a message box saying "end of search" */
}
String-Literale
String-Literale stellen nullterminierte Arrays von char
statischer Dauer dar . Da sie über eine statische Speicherdauer verfügen, kann ein String-Literal oder ein Zeiger auf dasselbe zugrunde liegende Array auf verschiedene Weise verwendet werden, die ein Zeiger auf ein automatisches Array nicht bietet. Die Rückgabe eines String-Literal aus einer Funktion hat beispielsweise ein genau definiertes Verhalten:
const char *get_hello() {
return "Hello, World!"; /* safe */
}
Aus historischen Gründen sind die Elemente des Arrays, die einem String-Literal entsprechen, nicht formal const
. Jeder Versuch, sie zu ändern, hat jedoch ein undefiniertes Verhalten . Normalerweise stürzt ein Programm ab, das versucht, das Array, das einem String-Literal entspricht, zu ändern, oder es kommt zu einer Fehlfunktion.
char *foo = "hello";
foo[0] = 'y'; /* Undefined behavior - BAD! */
Wenn ein Zeiger auf ein Zeichenfolgenliteral zeigt - oder wo dies manchmal möglich ist - empfiehlt es sich, den referent const
dieses Zeigers zu deklarieren, um zu vermeiden, dass ein solches undefiniertes Verhalten versehentlich aktiviert wird.
const char *foo = "hello";
/* GOOD: can't modify the string pointed to by foo */
Andererseits ist ein Zeiger auf oder in das zugrunde liegende Array eines Zeichenfolgenlitals nicht inhärent speziell. Sein Wert kann frei geändert werden, um auf etwas anderes hinzuweisen:
char *foo = "hello";
foo = "World!"; /* OK - we're just changing what foo points to */
Obwohl Initialisierer für char
Arrays dieselbe Form wie String-Literale haben können, werden bei Verwendung eines solchen Initialisierers dem initialisierten Array nicht die Merkmale eines String-Literalwerts verliehen. Der Initialisierer legt einfach die Länge und den anfänglichen Inhalt des Arrays fest. Insbesondere sind die Elemente modifizierbar, wenn sie nicht explizit als const
deklariert werden:
char foo[] = "hello";
foo[0] = 'y'; /* OK! */
Einen String auf Null setzen
Sie können memset
, um einen String (oder einen anderen Speicherblock) auf Null zu setzen.
Dabei ist str
die zu str
Zeichenfolge und n
die Anzahl der Bytes in der Zeichenfolge.
#include <stdlib.h> /* For EXIT_SUCCESS */
#include <stdio.h>
#include <string.h>
int main(void)
{
char str[42] = "fortytwo";
size_t n = sizeof str; /* Take the size not the length. */
printf("'%s'\n", str);
memset(str, '\0', n);
printf("'%s'\n", str);
return EXIT_SUCCESS;
}
Drucke:
'fortytwo'
''
Ein anderes Beispiel:
#include <stdlib.h> /* For EXIT_SUCCESS */
#include <stdio.h>
#include <string.h>
#define FORTY_STR "forty"
#define TWO_STR "two"
int main(void)
{
char str[42] = FORTY_STR TWO_STR;
size_t n = sizeof str; /* Take the size not the length. */
char * point_to_two = strstr(str, TWO_STR);
printf("'%s'\n", str);
memset(point_to_two, '\0', n);
printf("'%s'\n", str);
memset(str, '\0', n);
printf("'%s'\n", str);
return EXIT_SUCCESS;
}
Drucke:
'fortytwo'
'forty'
''
strspn und strcspn
Bei strspn
einer Zeichenfolge berechnet strspn
die Länge der ursprünglichen Teilzeichenfolge (Spanne), die ausschließlich aus einer bestimmten Liste von Zeichen besteht. strcspn
ist ähnlich, außer es berechnet die Länge der ursprünglichen Teilzeichenfolge, die aus beliebigen Zeichen besteht, mit Ausnahme der aufgelisteten Zeichen:
/*
Provided a string of "tokens" delimited by "separators", print the tokens along
with the token separators that get skipped.
*/
#include <stdio.h>
#include <string.h>
int main(void)
{
const char sepchars[] = ",.;!?";
char foo[] = ";ball call,.fall gall hall!?.,";
char *s;
int n;
for (s = foo; *s != 0; /*empty*/) {
/* Get the number of token separator characters. */
n = (int)strspn(s, sepchars);
if (n > 0)
printf("skipping separators: << %.*s >> (length=%d)\n", n, s, n);
/* Actually skip the separators now. */
s += n;
/* Get the number of token (non-separator) characters. */
n = (int)strcspn(s, sepchars);
if (n > 0)
printf("token found: << %.*s >> (length=%d)\n", n, s, n);
/* Skip the token now. */
s += n;
}
printf("== token list exhausted ==\n");
return 0;
}
Analoge Funktionen, die wcsspn
verwenden, sind wcsspn
und wcscspn
. Sie werden auf die gleiche Weise verwendet.
Zeichenketten kopieren
Zeigerzuweisungen kopieren keine Zeichenfolgen
Sie können die Verwendung =
Operator ganzen Zahlen zu kopieren, aber man kann nicht die Verwendung =
Operator Strings in C Strings in C kopieren sind als Arrays von Zeichen mit einem abschließenden Null-Zeichen dargestellt, so die Verwendung =
Operator nur die Adresse speichern ( Zeiger) einer Zeichenfolge.
#include <stdio.h>
int main(void) {
int a = 10, b;
char c[] = "abc", *d;
b = a; /* Integer is copied */
a = 20; /* Modifying a leaves b unchanged - b is a 'deep copy' of a */
printf("%d %d\n", a, b); /* "20 10" will be printed */
d = c;
/* Only copies the address of the string -
there is still only one string stored in memory */
c[1] = 'x';
/* Modifies the original string - d[1] = 'x' will do exactly the same thing */
printf("%s %s\n", c, d); /* "axc axc" will be printed */
return 0;
}
Das obige Beispiel wurde kompiliert, weil wir char *d
anstelle von char d[3]
. Die Verwendung des letzteren würde einen Compiler-Fehler verursachen. Sie können Arrays in C nicht zuordnen.
#include <stdio.h>
int main(void) {
char a[] = "abc";
char b[8];
b = a; /* compile error */
printf("%s\n", b);
return 0;
}
Zeichenketten mit Standardfunktionen kopieren
strcpy()
Um Strings tatsächlich zu kopieren, strcpy()
Funktion strcpy()
in string.h
. Vor dem Kopieren muss genügend Speicherplatz für das Ziel zugewiesen werden.
#include <stdio.h>
#include <string.h>
int main(void) {
char a[] = "abc";
char b[8];
strcpy(b, a); /* think "b special equals a" */
printf("%s\n", b); /* "abc" will be printed */
return 0;
}
snprintf()
Um einen Pufferüberlauf zu vermeiden, kann snprintf()
verwendet werden. Diese Lösung ist nicht die beste Lösung, da sie die Vorlagenzeichenfolge parsen muss, aber sie ist die einzige Pufferlimit-sichere Funktion zum Kopieren von in der Standardbibliothek verfügbaren Zeichenfolgen, die ohne zusätzliche Schritte verwendet werden kann.
#include <stdio.h>
#include <string.h>
int main(void) {
char a[] = "012345678901234567890";
char b[8];
#if 0
strcpy(b, a); /* causes buffer overrun (undefined behavior), so do not execute this here! */
#endif
snprintf(b, sizeof(b), "%s", a); /* does not cause buffer overrun */
printf("%s\n", b); /* "0123456" will be printed */
return 0;
}
strncat()
Eine zweite Option mit besserer Leistung ist die Verwendung von strncat()
(einer Pufferüberlauf-Überprüfungsversion von strcat()
). Es strcat()
ein drittes Argument, das die maximale Anzahl der zu kopierenden Bytes strcat()
:
char dest[32];
dest[0] = '\0';
strncat(dest, source, sizeof(dest) - 1);
/* copies up to the first (sizeof(dest) - 1) elements of source into dest,
then puts a \0 on the end of dest */
Beachten Sie, dass diese Formulierung sizeof(dest) - 1
; Dies ist wichtig, da strncat()
immer ein Null-Byte (gut) hinzufügt, dies jedoch nicht in der Größe der Zeichenfolge zählt (eine Ursache für Verwirrung und Pufferüberschreibungen).
Beachten Sie auch, dass die Alternative - Verketten nach einer nicht leeren Zeichenfolge - noch schwieriger ist. Erwägen:
char dst[24] = "Clownfish: ";
char src[] = "Marvin and Nemo";
size_t len = strlen(dst);
strncat(dst, src, sizeof(dst) - len - 1);
printf("%zu: [%s]\n", strlen(dst), dst);
Die Ausgabe ist:
23: [Clownfish: Marvin and N]
Beachten Sie jedoch, dass die als Länge angegebene Größe nicht die Größe des Zielarrays, sondern die Menge des verbleibenden Speicherplatzes war und nicht das Nullbyte des Terminals zählt. Dies kann zu großen Überschreibungsproblemen führen. Es ist auch etwas verschwenderisch; Um das Längenargument korrekt anzugeben, kennen Sie die Länge der Daten im Ziel. Daher können Sie stattdessen die Adresse des Nullbytes am Ende des vorhandenen Inhalts angeben. Dadurch wird strncat()
vor dem erneuten Scannen geschützt:
strcpy(dst, "Clownfish: ");
assert(len < sizeof(dst) - 1);
strncat(dst + len, src, sizeof(dst) - len - 1);
printf("%zu: [%s]\n", strlen(dst), dst);
Dies erzeugt dieselbe Ausgabe wie zuvor, aber strncat()
muss den vorhandenen Inhalt von dst
nicht scannen, bevor er mit dem Kopieren beginnt.
strncpy()
Die letzte Option ist die Funktion strncpy()
. Obwohl Sie denken, dass es zuerst kommen sollte, handelt es sich um eine eher betrügerische Funktion, die zwei Hauptstricke aufweist:
- Wenn das Kopieren über
strncpy()
das Pufferlimit erreicht, wird kein abschließendes Nullzeichen geschrieben. -
strncpy()
füllt das Ziel immer vollständig aus, falls erforderlich mit null Bytes.
(Eine solche skurrile Implementierung ist historisch und war ursprünglich für die Verarbeitung von UNIX-Dateinamen vorgesehen. )
Der einzig richtige Weg, um es zu benutzen, ist die manuelle Absperrung der Null-Terminierung:
strncpy(b, a, sizeof(b)); /* the third parameter is destination buffer size */
b[sizeof(b)/sizeof(*b) - 1] = '\0'; /* terminate the string */
printf("%s\n", b); /* "0123456" will be printed */
Selbst dann, wenn Sie einen großen Puffer haben, wird die Verwendung von strncpy()
wegen des zusätzlichen strncpy()
sehr ineffizient.
Konvertieren Sie Strings in Number: atoi (), atof () (gefährlich, verwenden Sie sie nicht)
Warnung: Die Funktionen atoi
, atol
, atoll
und atof
sind von Natur aus unsicher, weil: Wenn der Wert des Ergebnisses nicht dargestellt werden kann, ist das Verhalten nicht definiert. (7.20.1p1)
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char** argv)
{
int val;
if (argc < 2)
{
printf("Usage: %s <integer>\n", argv[0]);
return 0;
}
val = atoi(argv[1]);
printf("String value = %s, Int value = %d\n", argv[1], val);
return 0;
}
Wenn die zu konvertierende Zeichenfolge eine gültige Dezimalzahl ist, die im Bereich liegt, funktioniert die Funktion:
$ ./atoi 100
String value = 100, Int value = 100
$ ./atoi 200
String value = 200, Int value = 200
Für Zeichenfolgen, die mit einer Zahl beginnen, gefolgt von einer anderen, wird nur die erste Zahl analysiert:
$ ./atoi 0x200
0
$ ./atoi 0123x300
123
In allen anderen Fällen ist das Verhalten undefiniert:
$ ./atoi hello
Formatting the hard disk...
Aufgrund der oben genannten Unklarheiten und dieses undefinierten Verhaltens sollte die Funktionsfamilie atoi
niemals verwendet werden.
- Um in
long int
zu konvertieren, verwenden Siestrtol()
anstelle vonatol()
. - Verwenden
strtod()
zum Konvertieren indouble
strtod()
anstelle vonatof()
.
- Um in
long long int
zu konvertieren, verwenden Siestrtoll()
anstelle vonatoll()
.
String formatierte Daten lesen / schreiben
Schreibe formatierte Daten in einen String
int sprintf ( char * str, const char * format, ... );
Verwenden sprintf
Funktion sprintf
, um Float-Daten in eine Zeichenfolge zu schreiben.
#include <stdio.h>
int main ()
{
char buffer [50];
double PI = 3.1415926;
sprintf (buffer, "PI = %.7f", PI);
printf ("%s\n",buffer);
return 0;
}
Formatierte Daten aus String lesen
int sscanf ( const char * s, const char * format, ...);
Verwenden sscanf
Funktion sscanf
, um formatierte Daten zu analysieren.
#include <stdio.h>
int main ()
{
char sentence []="date : 06-06-2012";
char str [50];
int year;
int month;
int day;
sscanf (sentence,"%s : %2d-%2d-%4d", str, &day, &month, &year);
printf ("%s -> %02d-%02d-%4d\n",str, day, month, year);
return 0;
}
Strings in Number: strtoX-Funktionen sicher konvertieren
Seit C99 verfügt die C-Bibliothek über eine Reihe sicherer Konvertierungsfunktionen, die eine Zeichenfolge als Zahl interpretieren. Ihre Namen haben die Form strtoX
, wobei X
für l
, ul
, d
usw. steht, um den strtoX
der Konvertierung zu bestimmen
double strtod(char const* p, char** endptr);
long double strtold(char const* p, char** endptr);
Sie stellen sicher, dass eine Konvertierung einen Über- oder Unterlauf hatte:
double ret = strtod(argv[1], 0); /* attempt conversion */
/* check the conversion result. */
if ((ret == HUGE_VAL || ret == -HUGE_VAL) && errno == ERANGE)
return; /* numeric overflow in in string */
else if (ret == HUGE_VAL && errno == ERANGE)
return; /* numeric underflow in in string */
/* At this point we know that everything went fine so ret may be used */
Wenn die Zeichenfolge tatsächlich keine Nummer enthält, gibt diese Verwendung von strtod
0.0
.
Wenn dies nicht zufriedenstellend ist, kann der zusätzliche Parameter endptr
verwendet werden. Es ist ein Zeiger auf einen Zeiger, der auf das Ende der erkannten Zahl in der Zeichenfolge zeigt. Wenn es wie oben auf 0
ist oder NULL
, wird es einfach ignoriert.
Dieser endptr
Parameter gibt an, ob die Konvertierung erfolgreich war und wenn ja, wo die Nummer endete:
char *check = 0;
double ret = strtod(argv[1], &check); /* attempt conversion */
/* check the conversion result. */
if (argv[1] == check)
return; /* No number was detected in string */
else if ((ret == HUGE_VAL || ret == -HUGE_VAL) && errno == ERANGE)
return; /* numeric overflow in in string */
else if (ret == HUGE_VAL && errno == ERANGE)
return; /* numeric underflow in in string */
/* At this point we know that everything went fine so ret may be used */
Es gibt analoge Funktionen, die in breitere Integer-Typen konvertiert werden können:
long strtol(char const* p, char** endptr, int nbase);
long long strtoll(char const* p, char** endptr, int nbase);
unsigned long strtoul(char const* p, char** endptr, int nbase);
unsigned long long strtoull(char const* p, char** endptr, int nbase);
Diese Funktionen haben einen dritten Parameter nbase
, der die Nummernbasis enthält, in die die Nummer geschrieben wird.
long a = strtol("101", 0, 2 ); /* a = 5L */
long b = strtol("101", 0, 8 ); /* b = 65L */
long c = strtol("101", 0, 10); /* c = 101L */
long d = strtol("101", 0, 16); /* d = 257L */
long e = strtol("101", 0, 0 ); /* e = 101L */
long f = strtol("0101", 0, 0 ); /* f = 65L */
long g = strtol("0x101", 0, 0 ); /* g = 257L */
Der Sonderwert 0
für nbase
bedeutet, dass die Zeichenfolge genauso interpretiert wird, wie nbase
in einem C-Programm interpretiert werden: Ein Präfix von 0x
entspricht einer hexadezimalen Darstellung, ansonsten ist eine führende 0
eine oktale Zahl und alle anderen Zahlen werden als Dezimalzahlen betrachtet.
Die praktischste Art und Weise, ein Befehlszeilenargument als Zahl zu interpretieren, wäre daher der beste Weg
int main(int argc, char* argv[] {
if (argc < 1)
return EXIT_FAILURE; /* No number given. */
/* use strtoull because size_t may be wide */
size_t mySize = strtoull(argv[1], 0, 0);
/* then check conversion results. */
...
return EXIT_SUCCESS;
}
Das bedeutet, dass das Programm mit einem Parameter in Oktal, Dezimal oder Hexadezimal aufgerufen werden kann.