Sök…


Introduktion

I C är en sträng inte en egen typ. En C-sträng är konventionen att ha en endimensionell grupp av tecken som avslutas med ett noll-tecken, med en '\0' .

Detta betyder att en C-sträng med innehållet "abc" kommer att ha fyra tecken 'a' , 'b' , 'c' och '\0' .

Se den grundläggande introduktionen till strängsexempel .

Syntax

  • char str1 [] = "Hej, världen!"; / * Modifierbar * /
  • char str2 [14] = "Hej, världen!"; / * Modifierbar * /
  • char * str3 = "Hej, världen!"; / * Ej modifierbart * /

Beräkna längden: 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;
}

Detta program beräknar längden på det andra inmatningsargumentet och lagrar resultatet i len . Den skriver sedan ut den längden till terminalen. Till exempel när du kör med parametrarna program_name "Hello, world!" , programmet kommer att matas ut The length of the second argument is 13. eftersom strängen Hello, world! är 13 tecken lång.

strlen räknar alla byte från början av strängen upp till men inte inkluderande avslutande NUL-tecken, '\0' . Som sådan kan den endast användas när strängen garanteras att NUL-avslutas.

Tänk också på att om strängen innehåller några Unicode-tecken, kommer strlen inte att berätta hur många tecken som finns i strängen (eftersom vissa tecken kan vara flera byte långa). I sådana fall måste du själv räkna tecknen ( dvs. kodenheter). Tänk på resultatet från följande exempel:

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

Produktion:

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

Copy and Concatenation: 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;
}

utgångar:

foo
foobar
bar

Om du lägger till eller från eller kopierar från en befintlig sträng, se till att den är NUL-avslutad!

Strängbokstäver (t.ex. "foo" ) kommer alltid att avslutas av kompilatorn.

Jämförelse: strcmp (), strncmp (), strcasecmp (), strncasecmp ()

strcase* -funktionerna är inte Standard C utan en POSIX-förlängning.

strcmp funktionen jämför lexikografiskt två nollavslutade teckenuppsättningar. Funktionerna returnerar ett negativt värde om det första argumentet visas före det andra i leksikografisk ordning, noll om de jämför lika, eller positivt om det första argumentet visas efter det andra i leksikografisk ordning.

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

utgångar:

BBB equals BBB
BBB comes before CCCCC
BBB comes after AAAAAA

Som strcmp strcasecmp funktionen också lexikografiskt sina argument efter att ha översatt varje tecken till dess små bokstäver:

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

utgångar:

BBB equals bBB
BBB comes before ccCCC
BBB comes after aaaaaa

strncmp och strncasecmp jämför högst n tecken:

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

utgångar:

BBB equals Bb
BBB comes before Bb
BBB comes before Bb

Tokenisering: strtok (), strtok_r () och strtok_s ()

Funktionen strtok bryter en sträng i mindre strängar, eller tokens, med hjälp av en uppsättning avgränsare.

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

Produktion:

1: [Hello]
2: [world]

Strängen av avgränsare kan innehålla en eller flera avgränsare och olika avgränsningssträngar kan användas med varje anrop till strtok .

Samtal till strtok att fortsätta tokenisera samma källsträng bör inte passera källsträngen igen, utan istället passera NULL som det första argumentet. Om samma källa sträng leds sedan den första token istället återbefogenhets. Det vill säga, med tanke på samma avgränsare, skulle strtok helt enkelt returnera det första tecknet igen.

Observera att eftersom strtok inte tilldelar nytt minne för tokens, ändrar det källsträngen . Det är, i exemplet ovan kommer strängen src att manipuleras för att producera de tokens som refereras av pekaren som returneras av samtal till strtok . Detta betyder att källsträngen inte kan vara const (så det kan inte vara en strängbokstavlig). Det betyder också att identiteten för den avgränsande byten förloras (dvs. i exemplet "," och "!" Raderas effektivt från källsträngen och du kan inte se vilket avgränsande tecken som matchade).

Observera också att flera på varandra följande avgränsare i källsträngen behandlas som en; i exemplet ignoreras den andra komma.

strtok är varken trådsäker eller återinträffar eftersom den använder en statisk buffert vid analys. Detta innebär att om en funktion kallar strtok , kan ingen funktion som den kallar medan den använder strtok också använda strtok , och den kan inte kallas av någon funktion som själv använder strtok .

Ett exempel som visar problemen orsakade av att strtok inte återinförs är följande:

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

Produktion:

[1.2]
 [1]
 [2]

Den förväntade åtgärden är att den yttre do while slingan ska skapa tre tokens som består av varje decimalnummersträng ( "1.2" , "3.5" , "4.2" ), varvid strtok kräver den inre slingan ska dela upp den i separat siffra strängar ( "1" , "2" , "3" , "5" , "4" , "2" ).

Eftersom strtok inte återinförs inträffar detta dock inte. Istället skapar den första strtok korrekt "1.2 \ 0" -token, och den inre slingan skapar korrekt symbolerna "1" och "2" . Men då är strtok i den yttre slingan i slutet av strängen som används av den inre slingan och returnerar NULL omedelbart. Det andra och tredje underlaget i src arrayet analyseras inte alls.

C11

Standard C-biblioteken innehåller inte en trådsäker version eller återinförande version men vissa andra gör det, till exempel POSIX ' strtok_r . Observera att på MSVC strtok ekvivalent, strtok_s är tråd-säker.

C11

C11 har en valfri del, bilaga K, som erbjuder en trådsäker och återinförande version med namnet strtok_s . Du kan testa efter funktionen med __STDC_LIB_EXT1__ . Den här valfria delen stöds inte i stort.

strtok_s funktionen skiljer sig från POSIX strtok_r funktionen genom att skydda mot att lagra utanför strängen som tokeniseras och genom att kontrollera körtidskrav. Men på korrekt skrivna program strtok_s och strtok_r på samma sätt.

Att använda strtok_s med exemplet ger nu rätt svar, så här:

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

Och utgången kommer att vara:

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

Hitta första / sista förekomst av ett specifikt tecken: strchr (), strrchr ()

strchr och strrchr funktionerna hittar ett tecken i en sträng, det vill säga i en NUL-avslutad teckenuppsättning. strchr returnera en pekare till den första händelsen och strrchr till den sista.

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

Utgångar (efter att ha genererat en körbar namngiven pos ):

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

En vanlig användning för strrchr är att extrahera ett filnamn från en sökväg. Till exempel för att extrahera myfile.txt från C:\Users\eak\myfile.txt :

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

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

    return NULL;
}

Iterera över karaktärerna i en sträng

Om vi vet längden på strängen kan vi använda en för-loop för att iterera över dess tecken:

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

Alternativt kan vi använda standardfunktionen strlen() att få längden på en sträng om vi inte vet vad strängen är:

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

Slutligen kan vi dra nytta av det faktum att strängar i C garanteras att upphöra (vilket vi redan gjorde när vi skickade det till strlen() i föregående exempel ;-)). Vi kan iterera över matrisen oavsett storlek och sluta upprepas när vi når en noll-karaktär:

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

Grundläggande introduktion till strängar

I C är en sträng en sekvens av tecken som avslutas med ett nolltecken ('\ 0').

Vi kan skapa strängar med strängbokstäver , som är sekvenser av tecken omgiven av dubbla citattecken; ta till exempel strängens bokstavliga "hello world" . Strängbokstäver avslutas automatiskt.

Vi kan skapa strängar med flera metoder. Vi kan till exempel förklara ett char * och initiera det för att peka på det första tecknet i en sträng:

char * string = "hello world";

När man initialiserar ett char * till en strängkonstant som ovan, tilldelas själva strängen vanligtvis i skrivskyddad data; string är en pekare till det första elementet i matrisen, som är tecknet 'h' .

Eftersom strängbokstäverna allokeras i skrivskyddat minne är den inte modifierbar 1 . Varje försök att modifiera det kommer att leda till odefinierat beteende , så det är bättre att lägga till const att få ett kompileringstidsfel som detta

char const * string = "hello world";

Det har liknande effekt 2 som

char const string_arr[] = "hello world";

För att skapa en modifierbar sträng kan du förklara en teckenuppsättning och initiera dess innehåll med en bokstavlig sträng:

char modifiable_string[] = "hello world";

Detta motsvarar följande:

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

Eftersom den andra versionen använder brace-slutna initialiserare, upphör inte strängen automatiskt om inte ett '\0' -tecken inkluderas uttryckligen i teckenfältet vanligtvis som det sista elementet.


1 Icke-modifierbara innebär att tecknen i stränglitteral inte kan ändras, men kom ihåg att pekaren string kan ändras (kan peka någon annanstans eller kan ökas eller minskas).

2 Båda strängarna har liknande effekt i en mening att tecken i båda strängarna inte kan modifieras. Det bör noteras att string är en pekare till char och det är ett modifierbart l-värde så det kan ökas eller pekas på någon annan plats medan matrisen string_arr är ett icke-modifierbart l-värde, det kan inte modifieras.

Skapa arrayer av strängar

En rad strängar kan betyda ett par saker:

  1. En matris vars element är char * s
  2. En matris vars element är arrayer av char

Vi kan skapa en rad teckenpekare som så:

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

Kom ihåg: när vi tilldelar strängbokstäver till char * tilldelas själva strängarna i skrivskyddat minne. Array string_array allokeras emellertid i läs- / skrivminnet. Det betyder att vi kan ändra pekarna i matrisen, men vi kan inte ändra strängarna de pekar på.

I C är parametern till main argv (arrayen med kommandoradsargument som passerade när programmet kördes) en array av char * : char * argv[] .

Vi kan också skapa matriser av karaktärsuppsättningar. Eftersom strängar är matriser av tecken, är en rad strängar helt enkelt en matris vars element är matriser av tecken:

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

Detta motsvarar:

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

Observera att vi anger 4 som storleken på den andra dimensionen i matrisen; var och en av strängarna i vårt array är faktiskt 4 byte eftersom vi måste inkludera den nollslutande karaktären.

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 sökningar den haystack (första) argument för strängen som utpekas av needle . Om den hittas, strstr returnerar adressen till händelsen. Om den inte kunde hitta needle returnerar den NULL. Vi använder zbpos så att vi inte fortsätter hitta samma nål om och om igen. För att hoppa över första instansen lägger vi till en offset av zbpos . En anteckningar klon kan ringa findnext som denna, för att implementera sin "Hitta nästa" dialog:

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

Strängbokstäver

Strängbokstäver representerar nollavslutade, statiska varianter av char . Eftersom de har statisk lagringstid kan en strängbokstav eller en pekare till samma underliggande grupp säkert användas på flera sätt som en pekare till en automatisk matris inte kan. Till exempel att returnera en strängbokstav från en funktion har väl definierat beteende:

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

Av historiska skäl är elementen i matrisen som motsvarar en strängbokstav inte formellt const . Ändå har alla försök att modifiera dem odefinierat beteende . Vanligtvis kraschar ett program som försöker modifiera den matris som motsvarar en strängbokstav eller på annat sätt fungerar.

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

Där en pekare pekar på en stränglitteral - eller var det ibland kan göra - är det lämpligt att förklara att pekarens referent const att undvika engagerande så odefinierat beteende misstag.

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

Å andra sidan är en pekare till eller in i den underliggande uppsättningen av en strängbokstav inte i sig självt speciell; dess värde kan fritt ändras för att peka på något annat:

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

Även om initialisatorer för char arrays kan ha samma form som stränglitteraler ger användningen av en sådan initialisator inte egenskaperna hos en stränglitteral till den initialiserade matrisen. Initialiseraren anger helt enkelt längden och det ursprungliga innehållet i matrisen. I synnerhet är elementen modifierbara om de inte uttryckligen deklareras const :

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

Nollställ ut en sträng

Du kan ringa memset att nollställa ut en sträng (eller något annat minnesblock).

Där str är strängen till noll ut, och n är antalet byte i strängen.

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

Grafik:

'fortytwo'
''

Ett annat exempel:

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

Grafik:

'fortytwo'
'forty'
''

strspn och strcspn

Givet en sträng, strspn längden på den initiala substring (span) som endast består av en specifik lista med tecken. strcspn är liknande, förutom att det beräknar längden på den initiala substrängen som består av alla tecken utom de som listas:

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

Analoga funktioner som använder wcsspn är wcsspn och wcscspn ; de används på samma sätt.

Kopiera strängar

Pekertilldelningar kopierar inte strängar

Du kan använda operatören = att kopiera heltal, men du kan inte använda operatören = att kopiera strängar i C. Strängar i C representeras som matriser av tecken med ett avslutande nolltecken, så att använda operatören = sparar bara adressen ( pekaren) för en sträng.

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

Exemplet ovan kompilerade eftersom vi använde char *d snarare än char d[3] . Att använda det senare skulle orsaka ett kompilatorfel. Du kan inte tilldela matriser i C.

#include <stdio.h>

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

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

    return 0;
}

Kopiera strängar med standardfunktioner

strcpy()

För att faktiskt kopiera strängar, strcpy() funktionen strcpy() tillgänglig i string.h . Tillräckligt med utrymme måste tilldelas för destinationen innan kopiering.

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

För att undvika överskridande av buffertar kan snprintf() användas. Det är inte den bästa lösningen prestandamässigt eftersom den måste analysera mallsträngen, men det är den enda buffertgränssäkra funktionen för att kopiera strängar som är lättillgängliga i standardbiblioteket, som kan användas utan några extra steg.

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

Ett andra alternativ, med bättre prestanda, är att använda strncat() (en buffertöverskridningskontrollversion av strcat() ) - det krävs ett tredje argument som anger det maximala antalet byte som ska kopieras:

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

Observera att denna formulering använder sizeof(dest) - 1 ; detta är avgörande eftersom strncat() lägger alltid till en nollbyte (bra), men räknar inte det i strängens storlek (en orsak till förvirring och buffertöverskridande).

Observera också att alternativet - sammankoppling efter en icke-tom sträng - är ännu mer fylld. Överväga:

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

Utgången är:

23: [Clownfish: Marvin and N]

Observera dock att storleken som anges som längd inte var storleken på destinationsfältet utan den mängd utrymme som finns kvar i den, inte räknar terminalen nollbyte. Detta kan orsaka stora överskrivningsproblem. Det är också lite slöseri; För att ange längdargumentet korrekt vet du längden på informationen i destinationen, så du kan istället ange adressen för nollbyten i slutet av det befintliga innehållet och spara strncat() från att skanna om det:

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

Detta ger samma utdata som tidigare, men strncat() behöver inte skanna över det befintliga innehållet i dst innan det börjar kopiera.

strncpy()

Det sista alternativet är strncpy() -funktionen. Även om du kanske tror att det borde komma först, är det en ganska vilseledande funktion som har två huvudchinkar:

  1. Om kopiering via strncpy() träffar strncpy() ett avslutande nolltecken.
  2. strncpy() fyller alltid destinationen fullständigt med nollbyte vid behov.

(Sådant udda implementering är historiskt och var ursprungligen avsett för hantering av UNIX-filnamn )

Det enda korrekta sättet att använda det är att manuellt säkerställa nolluppsägning:

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

Även då, om du har en stor buffert blir det mycket ineffektivt att använda strncpy() grund av ytterligare nollpolstring.

Konvertera strängar till nummer: atoi (), atof () (farligt, använd dem inte)

Varning: Funktionerna atoi , atol , atoll och atof är i sig osäkra, eftersom: Om värdet på resultatet inte kan representeras är beteendet odefinierat. (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;
}

När strängen som ska konverteras är ett giltigt decimaltal som är inom räckvidd fungerar funktionen:

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

För strängar som börjar med ett nummer, följt av något annat, är bara det initiala numret parsat:

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

I alla andra fall är beteendet odefinierat:

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

På grund av oklarheterna ovan och detta odefinierade beteende, atoi familjens funktioner aldrig användas.

  • För att konvertera till long int , använd strtol() istället för atol() .
  • För att konvertera till double , använd strtod() istället för atof() .
C99
  • För att konvertera till long long int , använd strtoll() istället för atoll() .

strängformaterad data läs / skriv

Skriv formaterad data till strängen

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

använd sprintf funktion för att skriva floatdata till sträng.

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

Läs formaterade data från strängen

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

använd sscanf funktionen för att analysera formaterad data.

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

Konvertera säkert strängar till nummer: strtoX-funktioner

C99

Sedan C99 har C-biblioteket en uppsättning säkra konverteringsfunktioner som tolkar en sträng som ett nummer. Deras namn är av formen strtoX , där X är en av l , ul , d , etc för att bestämma måltypen för konverteringen

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

De tillhandahåller kontroll av att en konvertering hade ett över- eller underflöde:

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

Om strängen faktiskt inte innehåller något nummer strtod returnerar denna användning av strtod 0.0 .

Om detta inte är tillfredsställande kan den extra parametern endptr användas. Det är en pekare till pekare som kommer att pekas mot slutet av det detekterade numret i strängen. Om den är inställd på 0 , som ovan, eller NULL , ignoreras det helt enkelt.

Denna endptr parameter tillhandahåller indikerar om det har skett en framgångsrik konvertering och i så fall var numret slutade:

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

Det finns analoga funktioner för att konvertera till de bredare heltalstyperna:

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

Dessa funktioner har en tredje parameternbase som innehåller den nbase där numret skrivs.

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

Specialvärdet 0 för nbase betyder att strängen tolkas på samma sätt som antalet bokstäver tolkas i ett C-program: ett prefix på 0x motsvarar en hexadecimal representation, annars är en ledande 0 oktal och alla andra siffror ses som decimal.

Således skulle det mest praktiska sättet att tolka ett kommandoradsargument som ett nummer vara

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

Detta innebär att programmet kan anropas med en parameter i oktal, decimal eller hexadecimal.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow