Buscar..


Introducción

En C, una cadena no es un tipo intrínseco. Una cadena en C es la convención de tener una matriz de caracteres unidimensional que termina con un carácter nulo, con un '\0' .

Esto significa que una cadena C con un contenido de "abc" tendrá cuatro caracteres 'a' , 'b' , 'c' y '\0' .

Vea el ejemplo básico de introducción a cadenas .

Sintaxis

  • char str1 [] = "¡Hola mundo!"; / * Modificable * /
  • char str2 [14] = "¡Hola mundo!"; / * Modificable * /
  • char * str3 = "¡Hola mundo!"; /* No modificable*/

Calcular la longitud: 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;
}

Este programa calcula la longitud de su segundo argumento de entrada y almacena el resultado en len . Luego imprime esa longitud al terminal. Por ejemplo, cuando se ejecuta con los parámetros program_name "Hello, world!" , el programa emitirá The length of the second argument is 13. porque la cadena Hello, world! Tiene 13 caracteres de longitud.

strlen cuenta todos los bytes desde el principio de la cadena hasta, pero sin incluir, el carácter NUL de terminación, '\0' . Como tal, solo se puede utilizar cuando se garantiza que la cadena terminará en NUL.

También tenga en cuenta que si la cadena contiene caracteres Unicode, strlen no le dirá cuántos caracteres hay en la cadena (ya que algunos caracteres pueden tener varios bytes). En tales casos, debe contar usted mismo los caracteres ( es decir , las unidades de código). Considere la salida del siguiente ejemplo:

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

Salida:

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

Copia y concatenación: 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;
}

Salidas:

foo
foobar
bar

Si agrega o copia desde una cadena existente, asegúrese de que esté terminada en NUL.

Los literales de cadena (por ejemplo, "foo" ) siempre terminarán en NUL mediante el compilador.

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

Las strcase* strcase no son estándar C, sino una extensión POSIX.

La función strcmp compara lexicográficamente dos matrices de caracteres terminadas en nulo. Las funciones devuelven un valor negativo si el primer argumento aparece antes del segundo en orden lexicográfico, cero si se comparan igual o positivo si el primer argumento aparece después del segundo en orden lexicográfico.

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

Salidas:

BBB equals BBB
BBB comes before CCCCC
BBB comes after AAAAAA

Como función strcmp , la función strcasecmp también compara lexicográficamente sus argumentos después de traducir cada carácter a su correspondiente en minúscula:

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

Salidas:

BBB equals bBB
BBB comes before ccCCC
BBB comes after aaaaaa

strncmp y strncasecmp comparan como máximo n caracteres:

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

Salidas:

BBB equals Bb
BBB comes before Bb
BBB comes before Bb

Tokenización: strtok (), strtok_r () y strtok_s ()

La función strtok rompe una cadena en cadenas o tokens más pequeños, utilizando un conjunto de delimitadores.

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

Salida:

1: [Hello]
2: [world]

La cadena de delimitadores puede contener uno o más delimitadores y se pueden usar diferentes cadenas delimitadoras con cada llamada a strtok .

Las llamadas a strtok para continuar tokenizando la misma cadena fuente no deben pasar la cadena fuente nuevamente, sino que pasan NULL como el primer argumento. Si se pasa la misma cadena de origen, el primer token se volverá a tokenizar. Es decir, dados los mismos delimitadores, strtok simplemente devolvería el primer token nuevamente.

Tenga en cuenta que como strtok no asigna nueva memoria para los tokens, modifica la cadena de origen . Es decir, en el ejemplo anterior, la cadena src se manipulará para producir los tokens a los que hace referencia el puntero devuelto por las llamadas a strtok . Esto significa que la cadena de origen no puede ser const (por lo que no puede ser un literal de cadena). También significa que la identidad del byte delimitador se pierde (es decir, en el ejemplo, "," y "!" Se eliminan de la cadena fuente de manera efectiva y no se puede saber qué carácter delimitador coincide).

Tenga en cuenta también que varios delimitadores consecutivos en la cadena de origen se tratan como uno solo; en el ejemplo, la segunda coma se ignora.

strtok no es seguro para subprocesos ni re-entrante porque usa un búfer estático mientras analiza. Esto significa que si una función llama a strtok , ninguna función a la que llama mientras está usando strtok también puede usar strtok , y no puede ser llamada por ninguna función que esté usando strtok .

Un ejemplo que demuestra los problemas causados ​​por el hecho de que strtok no es re-entrante es el siguiente:

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

Salida:

[1.2]
 [1]
 [2]

La operación esperada es que el bucle do while externo debe crear tres tokens que constan de cada cadena de número decimal ( "1.2" , "3.5" , "4.2" ), para cada uno de los cuales strtok solicita el bucle interno debe dividirlo en partes separadas. cadenas de dígitos ( "1" , "2" , "3" , "5" , "4" , "2" ).

Sin embargo, debido a que strtok no es reingresante, esto no ocurre. En su lugar, el primer strtok crea correctamente el token "1.2 \ 0", y el bucle interno crea correctamente los tokens "1" y "2" . Pero entonces el strtok en el bucle externo está al final de la cadena utilizada por el bucle interno, y devuelve NULL inmediatamente. La segunda y tercera subcadenas de la matriz src no se analizan en absoluto.

C11

Las bibliotecas estándar de C no contienen una versión segura para subprocesos o de reingreso, pero otras sí, como el strtok_r POSIX. Tenga en cuenta que en el MSVC strtok equivalente, strtok_s es seguro para subprocesos.

C11

C11 tiene una parte opcional, el Anexo K, que ofrece una versión segura para subprocesos y re-entrant llamada strtok_s . Puede probar la función con __STDC_LIB_EXT1__ . Esta parte opcional no es ampliamente soportada.

La función strtok_s difiere de la función strtok_r POSIX al protegerse contra el almacenamiento fuera de la cadena que está siendo tokenizada, y al verificar las restricciones de tiempo de ejecución. Sin embargo, en programas escritos correctamente, los strtok_s y strtok_r comportan igual.

El uso de strtok_s con el ejemplo ahora produce la respuesta correcta, así:

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

Y la salida será:

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

Encuentre la primera / última aparición de un carácter específico: strchr (), strrchr ()

Las funciones strchr y strrchr encuentran un carácter en una cadena, que está en una matriz de caracteres terminada en NUL. strchr devuelve un puntero a la primera aparición y strrchr a la última.

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

Salidas (después de haber generado un ejecutable llamado 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.

Un uso común para strrchr es extraer un nombre de archivo de una ruta. Por ejemplo, para extraer myfile.txt de C:\Users\eak\myfile.txt :

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

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

    return NULL;
}

Iterando sobre los personajes en una cadena

Si conocemos la longitud de la cadena, podemos usar un bucle for para iterar sobre sus caracteres:

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

Alternativamente, podemos usar la función estándar strlen() para obtener la longitud de una cadena si no sabemos qué es la cadena:

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

Finalmente, podemos aprovechar el hecho de que se garantiza que las cadenas en C terminen en nulo (lo que ya hicimos al pasarlo a strlen() en el ejemplo anterior ;-)). Podemos iterar sobre la matriz sin importar su tamaño y dejar de iterar una vez que alcancemos un carácter nulo:

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

Introducción básica a las cuerdas.

En C, una cadena es una secuencia de caracteres que termina con un carácter nulo ('\ 0').

Podemos crear cadenas utilizando literales de cadena , que son secuencias de caracteres rodeadas por comillas dobles; por ejemplo, toma la cadena literal "hello world" . Los literales de cadena se terminan automáticamente en nulo.

Podemos crear cadenas utilizando varios métodos. Por ejemplo, podemos declarar un char * e inicializarlo para que apunte al primer carácter de una cadena:

char * string = "hello world";

Cuando se inicializa un char * a una constante de cadena como la anterior, la cadena misma suele asignarse en datos de solo lectura; string es un puntero al primer elemento de la matriz, que es el carácter 'h' .

Dado que la cadena literal se asigna en la memoria de solo lectura, no es modificable 1 . Cualquier intento de modificarlo conducirá a un comportamiento indefinido , por lo que es mejor agregar const para obtener un error de compilación como este

char const * string = "hello world";

Tiene el mismo efecto 2 como

char const string_arr[] = "hello world";

Para crear una cadena modificable, puede declarar una matriz de caracteres e inicializar su contenido utilizando un literal de cadena, de esta manera:

char modifiable_string[] = "hello world";

Esto es equivalente a lo siguiente:

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

Dado que la segunda versión utiliza un inicializador con llave, la cadena no termina automáticamente en nulo a menos que se incluya explícitamente un carácter '\0' en la matriz de caracteres, generalmente como su último elemento.


1 No modificable implica que los caracteres en el literal de cadena no pueden modificarse, pero recuerde que la string puntero puede modificarse (puede apuntar a otra parte o puede incrementarse o disminuirse).

2 Ambas cadenas tienen un efecto similar en el sentido de que los caracteres de ambas cadenas no pueden modificarse. Debe tenerse en cuenta que la string es un puntero a char y es un valor l modificable, por lo que puede incrementarse o apuntar a otra ubicación, mientras que el array string_arr es un valor l no modificable, no se puede modificar.

Creando matrices de cuerdas

Una serie de cadenas puede significar un par de cosas:

  1. Una matriz cuyos elementos son caracteres char *
  2. Una matriz cuyos elementos son matrices de char s

Podemos crear una serie de punteros de caracteres así:

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

Recuerde: cuando asignamos literales de cadena a char * , las cadenas se asignan en la memoria de solo lectura. Sin embargo, la matriz string_array se asigna en la memoria de lectura / escritura. Esto significa que podemos modificar los punteros en la matriz, pero no podemos modificar las cadenas a las que apuntan.

En C, el parámetro a main argv (la matriz de argumentos de línea de comando que se pasó cuando se ejecutó el programa) es una matriz de char * : char * argv[] .

También podemos crear matrices de matrices de caracteres. Dado que las cadenas son matrices de caracteres, una matriz de cadenas es simplemente una matriz cuyos elementos son matrices de caracteres:

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

Esto es equivalente a:

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

Tenga en cuenta que especificamos 4 como el tamaño de la segunda dimensión de la matriz; Cada una de las cadenas en nuestra matriz es en realidad 4 bytes, ya que debemos incluir el carácter de terminación nula.

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 busca en el haystack (primero) el argumento de la cadena apuntada por la needle . Si se encuentra, strstr devuelve la dirección de la ocurrencia. Si no pudo encontrar la needle , devuelve NULL. Usamos zbpos para no seguir encontrando la misma aguja una y otra vez. Para omitir la primera instancia, agregamos un desplazamiento de zbpos . Un clon del Bloc de notas podría llamar a findnext como este, para implementar su diálogo "Buscar siguiente":

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

Literales de cuerda

Los literales de cadena representan terminados en cero, estática duración matrices de char . Debido a que tienen una duración de almacenamiento estático, una cadena literal o un puntero a la misma matriz subyacente se puede usar de manera segura de varias maneras que un puntero a una matriz automática no puede. Por ejemplo, devolver un literal de cadena desde una función tiene un comportamiento bien definido:

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

Por razones históricas, los elementos de la matriz que corresponden a un literal de cadena no son formalmente const . Sin embargo, cualquier intento de modificarlos tiene un comportamiento indefinido . Normalmente, un programa que intenta modificar la matriz correspondiente a un literal de cadena se bloqueará o funcionará mal.

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

Cuando un puntero apunta a una cadena literal, o donde a veces puede hacerlo, es aconsejable declarar la const referente de ese puntero para evitar la participación accidental de tal comportamiento indefinido.

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

Por otro lado, un puntero hacia o dentro de la matriz subyacente de un literal de cadena no es inherentemente especial; su valor se puede modificar libremente para señalar otra cosa:

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

Además, aunque inicializadores para char matrices pueden tener la misma forma que los literales de cadena, el uso de un inicializador tales no confiere las características de una cadena literal en la matriz inicializada. El inicializador simplemente designa la longitud y los contenidos iniciales de la matriz. En particular, los elementos son modificables si no se declaran explícitamente const :

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

Poniendo a cero una cuerda

Puede llamar a memset para memset a cero una cadena (o cualquier otro bloque de memoria).

Donde str es la cadena a cero, y n es el número de bytes en la cadena.

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

Huellas dactilares:

'fortytwo'
''

Otro ejemplo:

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

Huellas dactilares:

'fortytwo'
'forty'
''

strspn y strcspn

Dada una cadena, strspn calcula la longitud de la subcadena inicial (intervalo) que consiste únicamente en una lista específica de caracteres. strcspn es similar, excepto que calcula la longitud de la subcadena inicial que consta de cualquier carácter excepto los listados:

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

Las funciones análogas que utilizan cadenas de caracteres wcsspn son wcsspn y wcscspn ; Se usan de la misma manera.

Copiando cuerdas

Las asignaciones de punteros no copian cadenas

Puede usar el operador = para copiar enteros, pero no puede usar el operador = para copiar cadenas en C. Las cadenas en C se representan como matrices de caracteres con un carácter nulo de terminación, por lo que usar el operador = solo guardará la dirección ( puntero) de una cadena.

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

El ejemplo anterior se compiló porque usamos char *d lugar de char d[3] . El uso de este último causaría un error del compilador. No se puede asignar a matrices en C.

#include <stdio.h>

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

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

    return 0;
}

Copiando cadenas utilizando funciones estándar

strcpy()

Para copiar cadenas, la función strcpy() está disponible en string.h . Se debe asignar suficiente espacio para el destino antes de copiar.

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

Para evitar la saturación del búfer, se puede usar snprintf() . No es la mejor solución en cuanto a rendimiento, ya que tiene que analizar la cadena de la plantilla, pero es la única función segura de límite de búfer para copiar cadenas fácilmente disponibles en la biblioteca estándar, que se puede usar sin ningún paso adicional.

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

Una segunda opción, con un mejor rendimiento, es utilizar strncat() (una versión de comprobación de desbordamiento de búfer de strcat() ): requiere un tercer argumento que le indica el número máximo de bytes que se debe copiar:

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

Tenga en cuenta que esta formulación utiliza sizeof(dest) - 1 ; esto es crucial porque strncat() siempre agrega un byte nulo (bueno), pero no cuenta eso en el tamaño de la cadena (causa de confusión y sobrescritura del búfer).

También tenga en cuenta que la alternativa, concatenar después de una cadena no vacía, es aún más complicada. Considerar:

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

La salida es:

23: [Clownfish: Marvin and N]

Tenga en cuenta, sin embargo, que el tamaño especificado como la longitud no era el tamaño de la matriz de destino, sino la cantidad de espacio que quedaba en ella, sin contar el byte nulo del terminal. Esto puede causar grandes problemas de sobrescritura. También es un poco derrochador; para especificar correctamente el argumento de longitud, conoce la longitud de los datos en el destino, por lo que podría especificar la dirección del byte nulo al final del contenido existente, evitando que strncat() vuelva a escanearlo:

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

Esto produce la misma salida que antes, pero strncat() no tiene que escanear el contenido existente de dst antes de que comience a copiar.

strncpy()

La última opción es la función strncpy() . Aunque podría pensar que debería ser lo primero, es una función bastante engañosa que tiene dos objetivos principales:

  1. Si la copia a través de strncpy() alcanza el límite del búfer, no se escribirá un carácter nulo de terminación.
  2. strncpy() siempre llena completamente el destino, con bytes nulos si es necesario.

(Tal implementación peculiar es histórica y fue inicialmente diseñada para manejar nombres de archivos UNIX )

La única forma correcta de usarlo es asegurar manualmente la terminación nula:

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

Incluso entonces, si tienes un búfer grande, se vuelve muy ineficiente usar strncpy() debido al relleno nulo adicional.

Convierta cadenas a números: atoi (), atof () (peligroso, no las use)

Advertencia: Las funciones atoi , atol , atoll y atof son intrínsecamente inseguras, porque: si el valor del resultado no puede representarse, el comportamiento no está definido. (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;
}

Cuando la cadena a convertir es un entero decimal válido que está en el rango, la función funciona:

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

Para cadenas que comienzan con un número, seguidas de otra cosa, solo se analiza el número inicial:

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

En todos los demás casos, el comportamiento es indefinido:

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

Debido a las ambigüedades anteriores y este comportamiento indefinido, la familia de funciones atoi nunca debe usarse.

  • Para convertir a long int , use strtol() lugar de atol() .
  • Para convertir al double , use strtod() lugar de atof() .
C99
  • Para convertir a long long int , use strtoll() lugar de atoll() .

cadena de datos con formato de lectura / escritura

Escribe datos formateados en una cadena

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

use la función sprintf para escribir datos flotantes en la cadena.

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

Leer datos formateados de cadena

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

Utilice la función sscanf para analizar datos formateados.

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

Convertir con seguridad cadenas a números: funciones strtoX

C99

Desde C99, la biblioteca C tiene un conjunto de funciones de conversión seguras que interpretan una cadena como un número. Sus nombres son de la forma strtoX , donde X es uno de l , ul , d , etc. para determinar el tipo de destino de la conversión.

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

Proporcionan la verificación de que una conversión tuvo un desbordamiento o desbordamiento:

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

Si la cadena de hecho no contiene ningún número, este uso de strtod devuelve 0.0 .

Si esto no es satisfactorio, se puede usar el parámetro adicional endptr . Es un puntero a puntero que se señalará al final del número detectado en la cadena. Si se establece en 0 , como anteriormente, o NULL , simplemente se ignora.

Este parámetro endptr proporciona indica si ha habido una conversión exitosa y, de ser así, dónde terminó el número:

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

Hay funciones análogas para convertir a los tipos enteros más anchos:

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

Estas funciones tienen un tercer parámetro nbase que contiene la base numérica en la que se escribe el número.

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

El valor especial 0 para nbase significa que la cadena se interpreta de la misma manera que los literales numéricos se interpretan en un programa C: un prefijo de 0x corresponde a una representación hexadecimal, de lo contrario, un 0 inicial es octal y todos los demás números se ven como decimales.

Así, la forma más práctica de interpretar un argumento de línea de comando como un número sería

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

Esto significa que se puede llamar al programa con un parámetro en octal, decimal o hexadecimal.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow