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.

C11

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

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:

  1. Ein Array, dessen Elemente char * s sind
  2. 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;
}
C99

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:

  1. Wenn das Kopieren über strncpy() das Pufferlimit erreicht, wird kein abschließendes Nullzeichen geschrieben.
  2. 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 Sie strtol() anstelle von atol() .
  • Verwenden strtod() zum Konvertieren in double strtod() anstelle von atof() .
C99
  • Um in long long int zu konvertieren, verwenden Sie strtoll() anstelle von atoll() .

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

C99

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.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow