Zoeken…


Invoering

In C is een string geen intrinsiek type. Een C-string is de conventie om een eendimensionale reeks tekens te hebben die wordt beëindigd door een nul-teken, door een '\0' .

Dit betekent dat een C-string met de inhoud van "abc" vier tekens 'a' , 'b' , 'c' en '\0' .

Zie de basisintroductie van tekenreeksen .

Syntaxis

  • char str1 [] = "Hallo wereld!"; / * Aanpasbaar * /
  • char str2 [14] = "Hallo wereld!"; / * Aanpasbaar * /
  • char * str3 = "Hallo wereld!"; /* Niet aanpasbaar*/

Bereken de lengte: 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;
}

Dit programma berekent de lengte van het tweede invoerargument en slaat het resultaat op in len . Vervolgens wordt die lengte naar de terminal afgedrukt. Bijvoorbeeld, wanneer uitgevoerd met de parameters program_name "Hello, world!" , zal het programma uitvoeren. The length of the second argument is 13. omdat de string Hello, world! is 13 tekens lang.

strlen telt alle bytes vanaf het begin van de string tot, maar niet inclusief het afsluitende NUL-teken '\0' . Als zodanig kan het alleen worden gebruikt wanneer gegarandeerd is dat de string NUL-beëindigd is.

Houd er ook rekening mee dat als de tekenreeks Unicode-tekens bevat, strlen u niet vertelt hoeveel tekens er in de tekenreeks voorkomen (omdat sommige tekens meerdere bytes lang kunnen zijn). In dergelijke gevallen moet u de tekens ( dwz code-eenheden) zelf tellen. Overweeg de uitvoer van het volgende voorbeeld:

#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));
}

Output:

asciiString has 50 bytes in the array
utf8String has 50 bytes in the array
"Hello world!" is 12 bytes
"Γειά σου Κόσμε!" is 27 bytes

Kopiëren en samenvoegen: 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;
}

uitgangen:

foo
foobar
bar

Als u toevoegt aan of van of kopieert uit een bestaande string, zorg er dan voor dat deze NUL beëindigd is!

Stringliterals (bijv. "foo" ) worden altijd NUL beëindigd door de compiler.

Vergelijking: strcmp (), strncmp (), strcasecmp (), strncasecmp ()

De strcase* -functies zijn geen standaard C, maar een POSIX-extensie.

De strcmp functie vergelijkt lexicografisch twee ongeldige karaktermatrices. De functies retourneren een negatieve waarde als het eerste argument vóór de tweede in lexicografische volgorde verschijnt, nul als ze gelijk zijn of positief als het eerste argument na de tweede in lexicografische volgorde verschijnt.

#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;
}

uitgangen:

BBB equals BBB
BBB comes before CCCCC
BBB comes after AAAAAA

Als strcmp strcasecmp functie strcasecmp ook lexicografisch zijn argumenten na het vertalen van elk karakter naar zijn kleine correspondent:

#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;
}

uitgangen:

BBB equals bBB
BBB comes before ccCCC
BBB comes after aaaaaa

strncmp en strncasecmp vergelijken maximaal n tekens:

#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;
}

uitgangen:

BBB equals Bb
BBB comes before Bb
BBB comes before Bb

Tokenisation: strtok (), strtok_r () en strtok_s ()

De functie strtok breekt een string in een kleinere string, of tokens, met behulp van een set scheidingstekens.

#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" */
}

Output:

1: [Hello]
2: [world]

De reeks scheidingstekens kan een of meer scheidingstekens bevatten en bij elke aanroep naar strtok kunnen verschillende scheidingstekens worden gebruikt.

Oproepen naar strtok om dezelfde strtok te blijven tokeniseren, moeten de brontekenreeks niet opnieuw doorgeven, maar in plaats daarvan NULL doorgeven als het eerste argument. Als dezelfde brontekenreeks wordt doorgegeven, wordt het eerste token in plaats daarvan opnieuw tokenized. Dat wil zeggen, gegeven dezelfde afbakeningen, zou strtok eenvoudig het eerste token opnieuw retourneren.

Merk op dat aangezien strtok geen nieuw geheugen toewijst voor de tokens, het de strtok wijzigt . Dat wil zeggen dat in het bovenstaande voorbeeld de tekenreeks src wordt gemanipuleerd om de tokens te produceren waarnaar wordt verwezen door de aanwijzer die wordt geretourneerd door de aanroepen naar strtok . Dit betekent dat de bronreeks niet const kan zijn (dus het kan geen letterlijke tekenreeks zijn). Het betekent ook dat de identiteit van de afbakenende byte verloren gaat (dwz in het voorbeeld worden de "," en "!" Effectief verwijderd uit de bronreeks en kunt u niet zien welk scheidingsteken overeenkomt).

Merk ook op dat meerdere opeenvolgende scheidingstekens in de bronreeks als één worden behandeld; in het voorbeeld wordt de tweede komma genegeerd.

strtok is niet thread-safe en ook niet opnieuw actief omdat het een statische buffer gebruikt tijdens het parseren. Dit betekent dat als een functie oproepen strtok , geen functie dat wordt aangespoord tijdens het gebruik strtok kan ook strtok en kan niet worden aangeroepen door elke functie die zelf via strtok .

Een voorbeeld dat de problemen aantoont die worden veroorzaakt door het feit dat strtok niet opnieuw binnenkomt, is als volgt:

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);

Output:

[1.2]
 [1]
 [2]

De verwachte operatie is dat de buitenste do while lus drie tokens moet maken die bestaan uit elke decimale getallenreeks ( "1.2" , "3.5" , "4.2" ), voor elk waarvan de strtok de binnenste lus oproept, deze in afzonderlijke cijferreeksen ( "1" , "2" , "3" , "5" , "4" , "2" ).

Omdat strtok echter niet opnieuw binnenkomt, gebeurt dit niet. In plaats daarvan maakt het eerste strtok correct het "1.2 \ 0" strtok en maakt de binnenste lus correct de tokens "1" en "2" . Maar dan bevindt het strtok in de buitenste lus zich aan het einde van de string die door de binnenste lus wordt gebruikt, en retourneert onmiddellijk NULL. Het tweede en derde deel van de src reeks worden helemaal niet geanalyseerd.

C11

De standaard C-bibliotheken bevatten geen thread-safe of nieuwkomersversie maar sommige andere wel, zoals POSIX ' strtok_r . Merk op dat op MSVC het strtok equivalent, strtok_s thread-safe is.

C11

C11 heeft een optioneel onderdeel, Bijlage K, dat een thread-safe en re-entrant versie biedt met de naam strtok_s . U kunt de functie testen met __STDC_LIB_EXT1__ . Dit optionele onderdeel wordt niet breed ondersteund.

De strtok_s functie verschilt van de POSIX strtok_r functie door te bewaken dat deze buiten de string wordt opgeslagen die tokenized is en door runtime-beperkingen te controleren. Op correct geschreven programma's gedragen de strtok_s en strtok_r zich echter hetzelfde.

Het gebruik van strtok_s met het voorbeeld levert nu de juiste reactie, als volgt:

/* 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);

En de output zal zijn:

[1.2]
 [1]
 [2]
[3.5]
 [3]
 [5]
[4.2]
 [4]
 [2]

Zoek het eerste / laatste exemplaar van een specifiek teken: strchr (), strrchr ()

De strchr en strrchr functies vinden een teken in een tekenreeks, dat wil zeggen in een NUL-terminated character array. strchr een pointer terug naar het eerste exemplaar en strrchr naar het laatste exemplaar.

#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;
}

Uitgangen (nadat een uitvoerbaar bestand met de naam pos gegenereerd):

$ ./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.

Een veelgebruikt gebruik voor strrchr is om een bestandsnaam uit een pad te extraheren. Bijvoorbeeld om myfile.txt uit te pakken van C:\Users\eak\myfile.txt :

char *getFileName(const char *path)
{
    char *pend;

    if ((pend = strrchr(path, '\')) != NULL)
        return pend + 1;

    return NULL;
}

De tekens in een string herhalen

Als we de lengte van de tekenreeks kennen, kunnen we een for-lus gebruiken om de tekens te herhalen:

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. */
}

Als alternatief kunnen we de standaardfunctie strlen() gebruiken om de lengte van een string te krijgen als we niet weten wat de string is:

size_t length = strlen(string);
size_t i = 0; 
for (; i < length; i++) {
    printf("%c\n", string[i]);    /* Print each character of the string. */
}

Ten slotte kunnen we profiteren van het feit dat tekenreeksen in C gegarandeerd worden beëindigd (wat we al deden toen we het in het vorige voorbeeld aan strlen() ;-)). We kunnen de array ongeacht de grootte herhalen en stoppen met itereren zodra we een nul-teken hebben:

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++;
}

Basisintroductie van strings

In C is een tekenreeks een reeks tekens die wordt beëindigd door een nul-teken ('\ 0').

We kunnen tekenreeksen maken met tekenreeksliteralen . Dit zijn reeksen tekens omringd door dubbele aanhalingstekens; Neem bijvoorbeeld de letterlijke letterlijke "hello world" . Stringliterals worden automatisch beëindigd door nul.

We kunnen tekenreeksen maken met behulp van verschillende methoden. We kunnen bijvoorbeeld een char * declareren en initialiseren om naar het eerste teken van een tekenreeks te wijzen:

char * string = "hello world";

Bij het initialiseren van een char * naar een tekenreeksconstante zoals hierboven, wordt de tekenreeks zelf meestal toegewezen in alleen-lezen gegevens; string is een pointer naar het eerste element van de array, het teken 'h' .

Aangezien de tekenreeks letterlijk wordt toegewezen in alleen-lezen geheugen, kan deze niet worden gewijzigd 1 . Elke poging om het te wijzigen zal leiden tot ongedefinieerd gedrag , dus het is beter om const toe te voegen om een compilatie-fout als deze te krijgen

char const * string = "hello world";

Het heeft hetzelfde effect 2 als

char const string_arr[] = "hello world";

Om een aanpasbare tekenreeks te maken, kunt u een tekenreeks declareren en de inhoud initialiseren met een letterlijke tekenreeks, als volgt:

char modifiable_string[] = "hello world";

Dit komt overeen met het volgende:

char modifiable_string[] = {'h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd', '\0'};

Aangezien de tweede versie gebruikmaakt van een accolade-initialisatie, wordt de tekenreeks niet automatisch beëindigd door een null tenzij een '\0' teken expliciet wordt opgenomen in de tekenarray, meestal als het laatste element.


1 Niet-wijzigbaar houdt in dat de tekens in de letterlijke tekenreeks niet kunnen worden gewijzigd, maar onthoud dat de pointer string kan worden gewijzigd (kan ergens anders wijzen of kan worden verhoogd of verlaagd).

2 Beide reeksen hebben een vergelijkbaar effect in die zin dat tekens van beide reeksen niet kunnen worden gewijzigd. Opgemerkt moet worden dat string een pointer is naar char en het is een aanpasbare l-waarde, zodat deze kan worden verhoogd of naar een andere locatie kan worden string_arr terwijl de array string_arr een niet-wijzigbare l-waarde is en niet kan worden gewijzigd.

Arrays of Strings maken

Een reeks strings kan een aantal dingen betekenen:

  1. Een array waarvan de elementen char * s zijn
  2. Een array waarvan de elementen arrays van char

We kunnen een reeks karakterwijzers maken zoals:

char * string_array[] = {
    "foo",
    "bar",
    "baz"
};

Onthoud: wanneer we tekenreeksliteralen toewijzen aan char * , worden de tekenreeksen zelf toegewezen in alleen-lezen geheugen. De array string_array wordt echter toegewezen in het lees- / schrijfgeheugen. Dit betekent dat we de pointers in de array kunnen wijzigen, maar we kunnen de tekenreeksen waarnaar ze verwijzen niet wijzigen.

In C is de parameter voor argv (de array met opdrachtregelargumenten die is doorgegeven toen het programma werd uitgevoerd) een array met char * : char * argv[] .

We kunnen ook arrays van character arrays maken. Aangezien tekenreeksen reeksen tekens zijn, is een reeks reeksen gewoon een reeks waarvan de elementen reeksen tekens zijn:

char modifiable_string_array_literals[][4] = {
    "foo",
    "bar",
    "baz"
};

Dit komt overeen met:

char modifiable_string_array[][4] = {
    {'f', 'o', 'o', '\0'},
    {'b', 'a', 'r', '\0'},
    {'b', 'a', 'z', '\0'}
};

Merk op dat we 4 specificeren als de grootte van de tweede dimensie van de array; elk van de strings in onze array is eigenlijk 4 bytes omdat we het null-terminating karakter moeten bevatten.

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 zoekt in het haystack (eerste) argument naar de string strstr de needle strstr . Indien gevonden, geeft strstr het adres van het exemplaar terug. Als het de needle niet kon vinden, retourneert het NULL. We gebruiken zbpos zodat we niet steeds dezelfde naald vinden. Om de eerste instantie over te slaan, voegen we een offset van zbpos . Een Kladblok-kloon kan findnext op deze manier oproepen om het findnext "Volgende zoeken" te implementeren:

/*
    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 literals

Stringliterals vertegenwoordigen niet-beëindigde, statische duur arrays van char . Omdat ze een statische opslagduur hebben, kan een letterlijke tekenreeks of een pointer naar dezelfde onderliggende array veilig op verschillende manieren worden gebruikt die een pointer naar een automatische array niet kan. Het retourneren van een letterlijke tekenreeks uit een functie heeft bijvoorbeeld een goed gedefinieerd gedrag:

const char *get_hello() {
    return "Hello, World!";  /* safe */
}

Om historische redenen zijn de elementen van de array die overeenkomen met een letterlijke tekenreeks niet formeel const . Niettemin heeft elke poging om ze te wijzigen ongedefinieerd gedrag . Doorgaans zal een programma dat probeert de array te wijzigen dat overeenkomt met een letterlijke tekenreeks crashen of anderszins defect raken.

char *foo = "hello";
foo[0] = 'y';  /* Undefined behavior - BAD! */

Waar een wijzer naar een letterlijke tekenreeks verwijst - of waar dit soms het geval is - is het raadzaam om de referentie const van die wijzer aan te geven om te voorkomen dat per ongeluk dergelijk ongedefinieerd gedrag plaatsvindt.

const char *foo = "hello";
/* GOOD: can't modify the string pointed to by foo */

Aan de andere kant is een wijzer naar of in de onderliggende reeks van een letterlijke tekenreeks niet inherent bijzonder; de waarde ervan kan vrij worden gewijzigd om naar iets anders te wijzen:

char *foo = "hello";
foo = "World!"; /* OK - we're just changing what foo points to */

Hoewel initialisatoren voor char reeksen dezelfde vorm kunnen hebben als stringliteralen, verleent het gebruik van een dergelijke initialisatie bovendien niet de karakteristieken van een stringliteraal aan de geïnitialiseerde array. De initialisatie geeft eenvoudig de lengte en de initiële inhoud van de array aan. In het bijzonder kunnen de elementen worden gewijzigd als ze niet expliciet worden aangegeven als const :

char foo[] = "hello";
foo[0] = 'y';  /* OK! */

Een string op nul zetten

U kunt memset om een string (of een ander geheugenblok) op nul te zetten.

Waar str is de string tot nul uit en n is het aantal bytes in de string.

#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;
}

prints:

'fortytwo'
''

Een ander voorbeeld:

#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;
}

prints:

'fortytwo'
'forty'
''

strspn en strcspn

Gegeven een string berekent strspn de lengte van de initiële substring (span) die uitsluitend uit een specifieke lijst met tekens bestaat. strcspn is vergelijkbaar, behalve dat het de lengte van de initiële substring berekent, bestaande uit alle tekens behalve de vermelde:

/*
  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 functies met tekenreeksen met brede tekens zijn wcsspn en wcscspn ; ze worden op dezelfde manier gebruikt.

Tekenreeksen kopiëren

Aanwijzeropdrachten kopiëren geen tekenreeksen

U kunt de operator = gebruiken om gehele getallen te kopiëren, maar u kunt de operator = gebruiken om tekenreeksen in C te kopiëren. Tekenreeksen in C worden weergegeven als arrays van tekens met een terminaal nul-teken, dus als u de operator = , wordt alleen het adres opgeslagen ( aanwijzer) van een string.

#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;
}

Het bovenstaande voorbeeld is samengesteld omdat we char *d hebben gebruikt in plaats van char d[3] . Het gebruik van de laatste zou een compilerfout veroorzaken. U kunt geen arrays toewijzen in C.

#include <stdio.h>

int main(void) {
    char a[] = "abc";
    char b[8];

    b = a; /* compile error */
    printf("%s\n", b);

    return 0;
}

Strings kopiëren met behulp van standaardfuncties

strcpy()

Om strings daadwerkelijk te kopiëren, is de functie strcpy() beschikbaar in string.h . Vóór het kopiëren moet voldoende ruimte worden toegewezen voor de bestemming.

#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()

Om bufferoverschrijding te voorkomen, kan snprintf() worden gebruikt. Het is qua prestaties niet de beste oplossing omdat het de sjabloonreeks moet ontleden, maar het is de enige bufferlimietveilige functie voor het kopiëren van tekenreeksen die direct beschikbaar zijn in de standaardbibliotheek, die zonder extra stappen kan worden gebruikt.

#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()

Een tweede optie, met betere prestaties, is het gebruik van strncat() (een buffercontroleversie van strcat() ) - er is een derde argument voor nodig dat het maximale aantal bytes strcat() dat moet worden gekopieerd:

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 */

Merk op dat deze formulering sizeof(dest) - 1 ; dit is cruciaal omdat strncat() altijd een null-byte (goed) toevoegt, maar dat niet meetelt in de grootte van de string (een oorzaak van verwarring en bufferoverschrijvingen).

Merk ook op dat het alternatief - samenvoegen na een niet-lege string - nog meer beladen is. Overwegen:

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);

De output is:

23: [Clownfish: Marvin and N]

Houd er echter rekening mee dat de grootte die is opgegeven als de lengte niet de grootte was van de bestemmingsmatrix, maar de hoeveelheid ruimte die erin overbleef, de null-byte niet meegerekend. Dit kan grote overschrijfproblemen veroorzaken. Het is ook een beetje verspillend; om het lengte-argument correct op te geven, weet u de lengte van de gegevens in de bestemming, dus u kunt in plaats daarvan het adres van de null-byte aan het einde van de bestaande inhoud strncat() , waardoor strncat() opnieuw wordt strncat() :

    strcpy(dst, "Clownfish: ");
    assert(len < sizeof(dst) - 1);
    strncat(dst + len, src, sizeof(dst) - len - 1);
    printf("%zu: [%s]\n", strlen(dst), dst);

Dit produceert dezelfde uitvoer als voorheen, maar strncat() hoeft de bestaande inhoud van dst niet te scannen voordat het begint te kopiëren.

strncpy()

De laatste optie is de functie strncpy() . Hoewel je misschien denkt dat het op de eerste plaats moet komen, is het een nogal misleidende functie die twee belangrijke gotcha's heeft:

  1. Als kopiëren via strncpy() de bufferlimiet bereikt, wordt er geen terminaal null-teken geschreven.
  2. strncpy() vult de bestemming altijd volledig, indien nodig met nul bytes.

(Een dergelijke eigenzinnige implementatie is historisch en was oorspronkelijk bedoeld voor het verwerken van UNIX-bestandsnamen )

De enige juiste manier om het te gebruiken is om handmatig te zorgen voor null-beëindiging:

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 */

Zelfs dan wordt het, als je een grote buffer hebt, erg inefficiënt om strncpy() vanwege extra nulvulling.

Tekenreeksen omzetten in nummer: atoi (), atof () (gevaarlijk, gebruik ze niet)

Waarschuwing: de functies atoi , atol , atoll en atof zijn inherent onveilig, omdat: als de waarde van het resultaat niet kan worden weergegeven, het gedrag niet is gedefinieerd. (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;
}

Wanneer de te converteren string een geldig decimaal geheel getal is dat binnen bereik is, werkt de functie:

$ ./atoi 100
String value = 100, Int value = 100
$ ./atoi 200
String value = 200, Int value = 200

Voor tekenreeksen die beginnen met een nummer, gevolgd door iets anders, wordt alleen het initiële nummer ontleed:

$ ./atoi 0x200
0
$ ./atoi 0123x300
123

In alle andere gevallen is het gedrag niet gedefinieerd:

$ ./atoi hello
Formatting the hard disk...

Vanwege de dubbelzinnigheden hierboven en dit ongedefinieerde gedrag, mag de atoi familie van functies nooit worden gebruikt.

  • Gebruik strtol() plaats van atol() om naar long int te converteren.
  • Gebruik strtod() plaats van atof() om naar double te converteren.
C99
  • Gebruik strtoll() plaats van atoll() om te converteren naar long long int .

tekenreeks geformatteerde gegevens lezen / schrijven

Schrijf opgemaakte gegevens naar een string

int sprintf ( char * str, const char * format, ... );

gebruik de sprintf functie om float-gegevens naar een string te schrijven.

#include <stdio.h>
int main ()
{
  char buffer [50];
  double PI = 3.1415926;
  sprintf (buffer, "PI = %.7f", PI);
  printf ("%s\n",buffer);
  return 0;
}

Opgemaakte gegevens uit string lezen

int sscanf ( const char * s, const char * format, ...);

gebruik de sscanf functie om opgemaakte gegevens te parseren.

#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;
}

Converteer Strings veilig naar Number: strtoX-functies

C99

Sinds C99 heeft de C-bibliotheek een set veilige conversiefuncties die een string als een getal interpreteren. Hun namen hebben de vorm strtoX , waarbij X een van l , ul , d , enz. strtoX om het strtoX van de conversie te bepalen

double strtod(char const* p, char** endptr);
long double strtold(char const* p, char** endptr);

Ze zorgen ervoor dat een conversie een over- of onderloop heeft:

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 */

Als de string helemaal geen nummer bevat, retourneert dit gebruik van strtod 0.0 .

Als dit niet bevredigend is, kan de extra parameter endptr worden gebruikt. Het is een aanwijzer naar aanwijzer die naar het einde van het gedetecteerde nummer in de tekenreeks wijst. Als het is ingesteld op 0 , zoals hierboven, of NULL , wordt het gewoon genegeerd.

Deze parameter endptr geeft aan of er een succesvolle conversie is geweest en zo ja, waar het nummer is geëindigd:

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 */

Er zijn analoge functies om te zetten naar de bredere typen integer:

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);

Deze functies hebben een derde parameter nbase die de nbase bevat waarin het nummer is geschreven.

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 */

De speciale waarde 0 voor nbase betekent dat de tekenreeks op dezelfde manier wordt geïnterpreteerd als getallen in een C-programma worden geïnterpreteerd: een voorvoegsel van 0x komt overeen met een hexadecimale representatie, anders is een voorlopende 0 octaal en worden alle andere getallen als decimaal gezien.

Dus de meest praktische manier om een opdrachtregelargument als een getal te interpreteren zou zijn

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;
}

Dit betekent dat het programma kan worden opgeroepen met een parameter in octaal, decimaal of hexadecimaal.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow