Guía completa para extraer subcadenas en C | Funciones estándar, personalizadas y multibyte

目次

1. Introducción

La manipulación de cadenas en el lenguaje C es una de las habilidades importantes al aprender programación. En particular, la extracción de subcadenas (extracción de subcadenas) se utiliza comúnmente en el procesamiento de datos y la conversión de formatos. En este artículo, explicaremos en detalle los métodos para extraer subcadenas en el lenguaje C, incluyendo el uso de funciones de la biblioteca estándar, la creación de funciones personalizadas, el manejo de caracteres multibyte (japonés), métodos de división de cadenas, etc. También introduciremos ejemplos de aplicación y manejo de errores, así que por favor léanlo hasta el final.

Lo que se puede aprender en este artículo

Al leer este artículo, puede adquirir habilidades como las siguientes.
  • Conceptos básicos de las cadenas en el lenguaje C y el rol del carácter de terminación ' '
  • strncpy o strchr , como extracción de subcadenas usando funciones de la biblioteca estándar
  • Métodos de implementación de manipulación de cadenas usando funciones personalizadas
  • Procesamiento de cadenas UTF-8 o Shift_JIS considerando caracteres multibyte (japonés)
  • strtok para métodos de división de cadenas
  • Métodos para obtener el contenido antes y después de un carácter específico y ejemplos de aplicación
Explicaremos intercalando ejemplos de código para que sea fácil de entender incluso para principiantes.

¿Por qué es importante la extracción de subcadenas en el lenguaje C?

El lenguaje C maneja las cadenas como «arreglos (arreglos de tipo char)», por lo que no se puede obtener subcadenas fácilmente como en otros lenguajes de alto nivel (Python o JavaScript, etc.). Por lo tanto, es importante seleccionar el método adecuado en situaciones como las siguientes.

1. Procesamiento de datos de entrada

Por ejemplo, al analizar datos como logs o archivos CSV, es necesario extraer ítems específicos.

2. Búsqueda de palabras clave específicas

Buscar una palabra clave específica en una cadena y obtener la información antes y después es indispensable para funciones de búsqueda y extracción de datos.

3. Mejora de la seguridad del programa

strncpy Al usar funciones como esta de manera adecuada, se puede prevenir desbordamientos de búfer (escritura de datos que exceden el tamaño del búfer). Esto es importante para evitar riesgos de seguridad.

Estructura de este artículo

En este artículo, procederemos con la explicación en el siguiente flujo.
  1. ¿Qué son las cadenas en el lenguaje C? Importancia de los conceptos básicos y el carácter de terminación
  2. Métodos para extraer subcadenas en el lenguaje C [Edición de biblioteca estándar]
  3. Métodos para extraer subcadenas en el lenguaje C [Edición de funciones personalizadas]
  4. Métodos de extracción de cadenas según la codificación de caracteres
  5. Métodos para dividir cadenas en el lenguaje C
  6. Ejemplo de aplicación: Métodos para extraer el contenido antes y después de un carácter específico
  7. Resumen
  8. FAQ
Entonces, empecemos detallando «¿Qué son las cadenas en el lenguaje C? Importancia de los conceptos básicos y el carácter de terminación».

2. ¿Qué son las cadenas en C? Conceptos básicos e importancia del carácter de terminación

2.1 Conceptos básicos de las cadenas en C

Las cadenas son «arrays de char»

En C, las cadenas se tratan como arrays de caracteres (arrays de tipo char). Por ejemplo, el siguiente código es un ejemplo básico de definición e impresión de una cadena.
#include 

int main() {
    char str[] = "Hello, World!"; // Definir el literal de cadena como un array
    printf("%s\n", str); // Imprimir la cadena
    return 0;
}
En este código, la cadena "Hello, World!" se almacena como un array de tipo char, y se imprime mediante printf("%s", str);.

Estructura interna de las cadenas

La cadena "Hello" se almacena en memoria de la siguiente manera.
Índice012345
CarácterHello
En C, se agrega automáticamente al final un carácter especial que indica el terminador de la cadena (carácter nulo '\0'), por lo que la longitud de la cadena es «número real de caracteres + 1».

2.2 Importancia del carácter de terminación (carácter nulo '

¿Qué es el carácter nulo?

'
)

Problemas si no hay carácter nulo

El carácter nulo ('\0') es un carácter especial que indica el final de la cadena. Para manejar correctamente las cadenas en C, es necesario entender la existencia de este carácter nulo.
#include 

int main() {
    char str[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; // Especificar explícitamente el carácter de terminación
    printf("%s\n", str); // Se muestra correctamente
    return 0;
}
En el código anterior, si no hay '\0', no se reconoce el final de "Hello", y puede ocurrir un comportamiento no deseado.

2.3 Cómo definir correctamente las cadenas

De la siguiente manera, si se olvida el carácter de terminación, puede causar un comportamiento anormal en la memoria.
#include 

int main() {
    char str[5] = {'H', 'e', 'l', 'l', 'o'}; // No incluye el carácter nulo
    printf("%s\n", str); // Puede ocurrir un comportamiento inesperado
    return 0;
}
Causa del error
  • printf("%s", str); es continúa imprimiendo caracteres hasta encontrar el carácter nulo '\0'.
  • Si no hay '\0', es posible que se imprima otro dato en la memoria.

Método 1: Usar literales de cadena

Método 2: Definir el array explícitamente

El método más común para definir cadenas es usar literales de cadena.
char str[] = "Hello";
En este método, el compilador de C agrega automáticamente el carácter nulo '\0', por lo que no se necesita procesamiento especial.

2.4 Cómo verificar el tamaño de la cadena

Si se define manualmente incluyendo '\0', se escribe de la siguiente manera.
char str[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
  • Es importante especificar el tamaño de número de caracteres +1 e insertar '\0' al final.
  • Si se olvida insertar str[5] en '\0', ocurrirá un comportamiento inesperado.

Funcionamiento de strlen

Para obtener la longitud de la cadena (número de caracteres), se usa la función strlen.
#include 
#include 

int main() {
    char str[] = "Hello";
    printf("Longitud de la cadena: %lu\n", strlen(str)); // Imprime 5 (sin incluir el carácter nulo)
    return 0;
}

2.5 Resumen

  • strlen cuenta el número de caracteres hasta que aparece el carácter nulo '\0'.
  • sizeof(str) a diferencia, obtiene no el tamaño del array sino «la longitud real de la cadena».
年収訴求

3. Cómo extraer subcadenas en lenguaje C [Edición de biblioteca estándar]

  1. Las cadenas en C se representan con arrays de char.
  2. Dado que el carácter de terminación (carácter nulo '\0') indica el final de la cadena, es necesario incluirlo siempre.
  3. Para obtener la longitud de la cadena, se usa strlen.
  4. Si no se define la cadena de manera adecuada, puede ocurrir un error inesperado.

3.1 Obtención de subcadenas utilizando strncpy

En el lenguaje C, para extraer subcadenas, hay métodos que utilizan la biblioteca estándar. En esta sección, explicamos elmétodo para obtener parcialmente cadenas de texto utilizando funciones de la biblioteca estándar como strncpy y strchr.

Sintaxis básica de strncpy

strncpy es unafunción que copia una parte de la cadena a otro búfer.

Ejemplo de uso básico

char *strncpy(char *dest, const char *src, size_t n);
  • dest: Búfer de destino de la copia
  • src: Cadena de origen de la copia
  • n: Número máximo de caracteres a copiar (sin incluir el '\0')

Puntos de atención de strncpy

#include 
#include 

int main() {
    char src[] = "Hello, World!";
    char dest[6];  // Búfer para almacenar la subcadena

    strncpy(dest, src, 5); // Copiar los primeros 5 caracteres "Hello"
    dest[5] = '\0';  // Agregar el carácter nulo manualmente

    printf("Subcadena: %s\n", dest);  // Imprimir "Hello"

    return 0;
}

3.2 Copia de cadenas segura utilizando strncpy_s

  1. Es necesario agregar manualmente el carácter nulo '\0'strncpy copia un máximo de n caracteres, perono agrega automáticamente el '\0', por lo que es necesario agregar explícitamente dest[n] = '\0';.
  2. Prestar atención al desbordamiento de búferSi n es mayor que el tamaño de dest, existe la posibilidad de escribir más allá del búfer.

Sintaxis básica de strncpy_s

strncpy_s es una versión que mejora la seguridad de strncpy y puede prevenir desbordamientos de búfer.

Ejemplo de uso

errno_t strncpy_s(char *dest, rsize_t destsz, const char *src, rsize_t n);
  • dest: Búfer de destino de la copia
  • destsz: Tamaño de dest
  • src: Cadena de origen de la copia
  • n: Número máximo de caracteres a copiar

Ventajas de strncpy_s

#include 
#include 

int main() {
    char src[] = "Hello, World!";
    char dest[6];

    if (strncpy_s(dest, sizeof(dest), src, 5) == 0) {
        dest[5] = '\0';  // Agregar el carácter nulo por si acaso
        printf("Subcadena: %s\n", dest);
    } else {
        printf("Error de copia\n");
    }

    return 0;
}

3.3 Extracción hasta un carácter específico utilizando strchr

  • Al especificar el tamaño del búfer (destsz), se puede copiar de manera segura.
  • Si n es mayor que destsz, devuelve un error.
Sin embargo, strncpy_s se agregó en el estándar C11, por lo que en algunos entornos no se puede usar, así que preste atención a ese punto.

Sintaxis básica de strchr

Al usar strchr, se puede encontrar la posición de un carácter específico y obtener la cadena hasta esa parte.

Ejemplo de uso

char *strchr(const char *str, int c);
  • str: Cadena de búsqueda
  • c: Carácter a buscar (tipo char)

Puntos clave

#include 
#include 

int main() {
    char str[] = "Hello, World!";
    char *pos = strchr(str, ','); // Buscar la posición de ','

    if (pos != NULL) {
        int length = pos - str; // Calcular el número de caracteres hasta ','
        char result[20];

        strncpy(result, str, length);
        result[length] = '\0'; // Agregar el carácter nulo

        printf("Subcadena: %s\n", result);  // Imprimir "Hello"
    }

    return 0;
}

3.4 Búsqueda de palabras clave y extracción utilizando strstr

  • strchr devuelve la dirección del c encontrado primero, por lo que se puede usar para determinar el rango de la subcadena.
  • Al calcular la longitud de la subcadena con pos - str y copiar con strncpy, se puede obtener la subcadena hasta un carácter específico.

Sintaxis básica de strstr

strstr es conveniente para buscar subcadenas y obtener la cadena a partir de allí.

Ejemplo de uso

char *strstr(const char *haystack, const char *needle);
  • haystack: Cadena de búsqueda
  • needle: Subcadena a buscar

Puntos clave

#include 
#include 

int main() {
    char str[] = "Hello, World!";
    char *pos = strstr(str, "World"); // Buscar la posición de "World"

    if (pos != NULL) {
        printf("Subcadena encontrada: %s\n", pos);
    } else {
        printf("No se encontró la subcadena.\n");
    }

    return 0;
}

3.5 Resumen

  • strstr devuelve el puntero del needle encontrado primero.
  • Si se devuelve NULL, significa que needle no existe en haystack.

4. Cómo extraer subcadenas en C [Edición de funciones personalizadas]

  1. Al usar strncpy, se puede copiar subcadenas de manera segura, pero es necesario agregar manualmente el carácter nulo.
  2. strncpy_s permite especificar destsz, mejorando la seguridad.
  3. Al usar strchr, se puede obtener la subcadena hasta un carácter específico.
  4. Al usar strstr, se puede obtener la posición de una palabra clave específica y extraer a partir de allí.
Al utilizar la biblioteca estándar, se puede implementar el procesamiento de cadenas en lenguaje C de manera simple y segura.

4.1 Ventajas de crear funciones personalizadas

Si se utiliza la biblioteca estándar, es posible extraer subcadenas básicas, pero en algunos casos se requiere un método más flexible. Por lo tanto, en esta sección explicaremos la extracción de subcadenas usando funciones personalizadas.

4.2 Función básica para extraer subcadenas

Con la biblioteca estándar se pueden copiar y buscar subcadenas, pero hay los siguientes problemas.
  • strncpy no agrega automáticamente el carácter nulo '\0'
  • strchr o strstr solo permiten búsquedas parciales
  • Es difícil realizar operaciones de cadenas más flexibles
Por lo tanto, crear funciones personalizadas que se puedan personalizar según usos específicos es efectivo.

Especificaciones de la función

Primero, creamos una función básica que extrae una cadena desde una posición especificada.

Código de implementación

  • Argumentos
  • const char *source (cadena original)
  • int start (posición de inicio)
  • int length (número de caracteres a extraer)
  • char *dest (buffer para almacenar la subcadena extraída)
  • Contenido del procesamiento
  • start desde length caracteres de la cadena a dest copiar
  • '\0' agregar automáticamente

Puntos clave

#include 
#include 

void substring(const char *source, int start, int length, char *dest) {
    int i;
    for (i = 0; i < length && source[start + i] != '\0'; i++) {
        dest[i] = source[start + i];
    }
    dest[i] = '\0'; // Agregar carácter nulo
}

int main() {
    char text[] = "Hello, World!";
    char result[10];

    substring(text, 7, 5, result); // Extraer "World"
    printf("Subcadena: %s\n", result);

    return 0;
}

4.3 Obtención dinámica de subcadenas usando malloc

  • for bucle para copiar los length caracteres especificados.
  • '\0' al llegar, terminar el bucle.
  • dest[i] = '\0'; agregar para colocar siempre el carácter nulo al final.

Especificaciones de la función

En la función anterior, es necesario asegurar el tamaño de dest de antemano. Sin embargo, si se puede asegurar el tamaño necesario dinámicamente, se convierte en una función más general.

Código de implementación

  • Asegurar la memoria necesaria con malloc
  • start desde length caracteres de subcadena a un nuevo buffer copiar
  • El llamador debe free

Puntos clave

#include 
#include 
#include 

char *substring_dynamic(const char *source, int start, int length) {
    char *dest = (char *)malloc(length + 1); // +1 para el carácter nulo
    if (dest == NULL) {
        return NULL; // Fallo en la asignación de memoria
    }

    int i;
    for (i = 0; i < length && source[start + i] != '\0'; i++) {
        dest[i] = source[start + i];
    }
    dest[i] = '\0';

    return dest;
}

int main() {
    char text[] = "Hello, World!";
    char *result = substring_dynamic(text, 7, 5);

    if (result != NULL) {
        printf("Subcadena: %s\n", result);
        free(result); // Liberar memoria
    } else {
        printf("Fallo en la asignación de memoria.\n");
    }

    return 0;
}

4.4 Compatibilidad con caracteres multibyte (japonés)

  • malloc para asignar memoria dinámicamente, por lo que no es necesario preocuparse por el tamaño del buffer.
  • Después del uso, es necesario liberar la memoria con free(result);.

Implementación considerando caracteres multibyte

Al manejar japonés (caracteres multibyte como UTF-8), un carácter no siempre es 1 byte, por lo que una función substring simple no funcionará correctamente.

Código de implementación (compatible con UTF-8)

  • mbstowcs usar para convertir cadena multibyte a cadena amplia (wchar_t)
  • wcsncpy usar para obtener subcadena
  • wcstombs para volver a cadena multibyte

Puntos clave

#include 
#include 
#include 
#include 
#include 

void substring_utf8(const char *source, int start, int length, char *dest) {
    setlocale(LC_ALL, ""); // Establecer localización

    wchar_t wsource[256];
    mbstowcs(wsource, source, 256); // Convertir cadena UTF-8 a cadena amplia

    wchar_t wresult[256];
    wcsncpy(wresult, wsource + start, length); // Extraer en cadena amplia
    wresult[length] = L'\0';

    wcstombs(dest, wresult, 256); // Volver a cadena multibyte
}

int main() {
    char text[] = "¡Hola, mundo!"; // Cadena UTF-8
    char result[20];

    substring_utf8(text, 5, 3, result); // Obtener "世界"
    printf("Subcadena: %s\n", result);

    return 0;
}

4.5 Resumen

  • setlocale(LC_ALL, ""); para establecer localización y compatibilidad multibyte.
  • mbstowcs para convertir cadena multibyte a cadena amplia.
  • wcsncpy para obtener subcadena, luego wcstombs para volver a multibyte.

5. Método de extracción de subcadenas por código de caracteres

  1. Si se crea substring uno mismo, se puede obtener subcadenas de manera flexible.
  2. Usar asignación de memoria dinámica (malloc) permite obtener subcadenas de tamaño variable.
  3. Para manejar caracteres multibyte (japonés), utilizar mbstowcs / wcstombs.
En casos donde strncpy o strchr de la biblioteca estándar son difíciles de usar, crear funciones personalizadas puede hacer el procesamiento de cadenas en C más potente.

5.1 Caso de ASCII (caracteres de 1 byte)

En C, si no se presta atención a las diferencias en los códigos de caracteres, el procesamiento de extracción de cadenas no funcionará correctamente en algunos casos. En particular, al manejar caracteres multibyte como el japonés (UTF-8, Shift_JIS, EUC-JP, etc.), ya que 1 carácter ≠ 1 byte, funciones simples como strncpy o substring no pueden procesarlas adecuadamente. En esta sección, explicamos en detalle el método de extracción de subcadenas por código de caracteres.

Obtención básica de subcadena

Ejemplo de implementación
Los caracteres ASCII son 1 carácter = 1 byte, por lo que strncpy o substring permiten procesarlos fácilmente.

5.2 Caso de UTF-8 (caracteres multibyte)

#include 
#include 

void substring_ascii(const char *source, int start, int length, char *dest) {
    strncpy(dest, source + start, length);
    dest[length] = ' '; // Agregar el carácter nulo
}

int main() {
    char text[] = "Hello, World!";
    char result[6];

    substring_ascii(text, 7, 5, result); // Obtener "World"
    printf("Subcadena: %s\n", result);

    return 0;
}
Puntos clave
  • En el caso de caracteres ASCII (solo alfanuméricos), strncpy es suficiente
  • ' ' (carácter nulo) debe agregarse siempre

Características de UTF-8

Método de procesamiento correcto

En UTF-8, el número de bytes por carácter es 1-4 bytes y variable, por lo que usar simplemente strncpy puede cortar en medio de un carácter.
Obtención de subcadena compatible con UTF-8
En C, para procesar UTF-8 de manera segura, se recomienda usar mbstowcs para convertir a cadena ancha (wchar_t) y obtener la subcadena.

5.3 Caso de Shift_JIS (caracteres multibyte)

#include 
#include 
#include 
#include 

void substring_utf8(const char *source, int start, int length, char *dest) {
    setlocale(LC_ALL, ""); // Configurar el locale

    wchar_t wsource[256];
    mbstowcs(wsource, source, 256); // Convertir la cadena multibyte a cadena ancha

    wchar_t wresult[256];
    wcsncpy(wresult, wsource + start, length); // Obtener la subcadena
    wresult[length] = L' ';

    wcstombs(dest, wresult, 256); // Convertir de nuevo la cadena ancha a multibyte
}

int main() {
    char text[] = "¡Hola, mundo!"; // Cadena UTF-8
    char result[20];

    substring_utf8(text, 5, 3, result); // Obtener "mundo"
    printf("Subcadena: %s\n", result);

    return 0;
}
Puntos clave
  • setlocale(LC_ALL, ""); Sin configurar el locale, los caracteres multibyte no se procesan correctamente.
  • mbstowcs convierte la cadena multibyte a wchar_t y wcsncpy procesa de manera segura.
  • wcstombs vuelve a convertir a cadena multibyte.

Características de Shift_JIS

Obtención de subcadena compatible con Shift_JIS

En Shift_JIS, 1 carácter es 1 byte o 2 bytes, por lo que strncpy simple causa garabateo de caracteres.
Implementación en Shift_JIS
También en el caso de Shift_JIS se recomienda el método de conversión a cadena ancha para procesar.

5.4 Caso de EUC-JP (caracteres multibyte)

#include 
#include 
#include 
#include 

void substring_sjis(const char *source, int start, int length, char *dest) {
    setlocale(LC_ALL, "Japanese"); // Configurar el locale para procesar Shift_JIS

    wchar_t wsource[256];
    mbstowcs(wsource, source, 256);

    wchar_t wresult[256];
    wcsncpy(wresult, wsource + start, length);
    wresult[length] = L' ';

    wcstombs(dest, wresult, 256);
}

int main() {
    char text[] = "¡Hola, mundo!"; // Cadena Shift_JIS (depende del entorno)
    char result[20];

    substring_sjis(text, 5, 3, result);
    printf("Subcadena: %s\n", result);

    return 0;
}
Puntos clave
  • Para procesar Shift_JIS correctamente, configurar setlocale(LC_ALL, "Japanese"); .
  • mbstowcs y wcstombs se usan para procesar la cadena de manera segura.

Características de EUC-JP

Obtención de subcadena compatible con EUC-JP

Al igual que Shift_JIS, en EUC-JP el número de bytes por carácter varía, por lo que se necesita procesamiento de conversión utilizando caracteres anchos.

5.5 Resumen

#include 
#include 
#include 
#include 

void substring_eucjp(const char *source, int start, int length, char *dest) {
    setlocale(LC_ALL, "ja_JP.eucJP"); // Configurar el locale para procesar EUC-JP

    wchar_t wsource[256];
    mbstowcs(wsource, source, 256);

    wchar_t wresult[256];
    wcsncpy(wresult, wsource + start, length);
    wresult[length] = L' ';

    wcstombs(dest, wresult, 256);
}

int main() {
    char text[] = "¡Hola, mundo!"; // Cadena EUC-JP (depende del entorno)
    char result[20];

    substring_eucjp(text, 5, 3, result);
    printf("Subcadena: %s\n", result);

    return 0;
}
Puntos clave
  • setlocale(LC_ALL, "ja_JP.eucJP"); configura el locale para EUC-JP.
  • mbstowcs / wcstombs se usan para procesar correctamente los caracteres multibyte.

6. Cómo dividir cadenas en C

Código de caracteresNúmero de bytesMétodo de procesamiento recomendado
ASCII1 bytestrncpy está bien
UTF-81-4 bytesmbstowcs / wcstombs se usan
Shift_JIS1 o 2 bytesmbstowcs / wcstombs se usan
EUC-JP1 o 2 bytesmbstowcs / wcstombs se usan
  • Si solo caracteres ASCII, strncpy está bien
  • En los casos de UTF-8, Shift_JIS, EUC-JP, usar mbstowcs / wcstombs
  • Configurar setlocale(LC_ALL, "..."); de manera adecuada según el entorno

6.1 División de cadenas usando strtok

El procesamiento de división de cadenas es necesario en muchas situaciones, como el análisis de datos CSV, el procesamiento de argumentos de línea de comandos, el análisis de datos de log, entre otros. En C, hay métodos para usar funciones de la biblioteca estándar como strtok o strtok_r, o para crear funciones personalizadas. En esta sección, explicamos en detalle el método para dividir cadenas con un delimitador específico.

Sintaxis básica

strtok es una función que divide la cadena con el delimitador especificado.

Ejemplo de uso: Dividir cadena con coma ,

char *strtok(char *str, const char *delim);
  • str: Cadena objetivo de división (especificada en la primera llamada)
  • delim: Delimitador (se pueden especificar varios)
  • Valor de retorno: El primer token (la primera parte dividida)
  • Notas: strtok modifica la cadena original (cambia el delimitador a '\0')

Resultado de ejecución

#include 
#include 

int main() {
    char str[] = "apple,banana,orange,grape"; // Cadena a dividir
    char *token = strtok(str, ","); // Obtener el primer token

    while (token != NULL) {
        printf("Token: %s\n", token);
        token = strtok(NULL, ","); // Obtener el siguiente token
    }

    return 0;
}

Notas sobre strtok

Token: apple
Token: banana
Token: orange
Token: grape

6.2 División de cadenas segura para hilos usando strtok_r

  1. Modifica la cadena original
  • strtok sobrescribe el delimitador con ‘\0’, por lo que se pierde la cadena original.
  1. No es seguro para hilos
  • strtok usa una variable estática global internamente, por lo que no es recomendable usarla en entornos multihilo.

Sintaxis básica

strtok_r es la versión segura para hilos de strtok, y guarda el estado en saveptr, por lo que se puede usar de manera segura en entornos multihilo.

Ejemplo de uso: Dividir cadena con espacio

char *strtok_r(char *str, const char *delim, char **saveptr);
  • str: Cadena objetivo de división (especificada en la primera llamada)
  • delim: Delimitador (se pueden especificar varios)
  • saveptr: Puntero que mantiene el estado interno (se actualiza en cada llamada)

Ventajas de strtok_r

#include 
#include 

int main() {
    char str[] = "Hello World from C"; // Cadena a dividir
    char *token;
    char *saveptr; // Puntero para guardar el estado interno

    token = strtok_r(str, " ", &saveptr); // Obtener el primer token
    while (token != NULL) {
        printf("Token: %s\n", token);
        token = strtok_r(NULL, " ", &saveptr); // Obtener el siguiente token
    }

    return 0;
}

6.3 Dividir cadenas con una función personalizada (sin usar strtok)

  • Seguro para hilos
  • Permite procesar múltiples cadenas en paralelo

Especificaciones de la función personalizada

Dado que strtok modifica la cadena original, también es posible crear una función personalizada que divida la cadena sin modificarla.

Código de implementación

  • Entrada
  • const char *source (cadena original)
  • const char delim (delimitador)
  • char tokens[][50] (arreglo para almacenar las cadenas divididas)
  • Procesamiento
  • source se copia para no modificar el original
  • delim se usa para almacenar los resultados divididos en tokens

Resultado de ejecución

#include 
#include 

void split_string(const char *source, char delim, char tokens[][50], int *count) {
    int i = 0, j = 0, token_index = 0;

    while (source[i] != '\0') {
        if (source[i] == delim) {
            tokens[token_index][j] = '\0';
            token_index++;
            j = 0;
        } else {
            tokens[token_index][j] = source[i];
            j++;
        }
        i++;
    }
    tokens[token_index][j] = '\0';
    *count = token_index + 1;
}

int main() {
    char text[] = "dog,cat,bird,fish";
    char tokens[10][50]; // Puede almacenar hasta 10 palabras
    int count;

    split_string(text, ',', tokens, &count);

    for (int i = 0; i < count; i++) {
        printf("Token: %s\n", tokens[i]);
    }

    return 0;
}

Puntos clave

Token: dog
Token: cat
Token: bird
Token: fish

6.4 Aplicaciones de la división de cadenas (procesamiento de datos CSV)

  • source se copia y se procesa sin modificar el original.
  • tokens almacena los resultados divididos y mantiene la cadena original.

Ejemplo de análisis de datos CSV

Los datos CSV (separados por comas) se pueden analizar usando strtok.

Resultado de ejecución

#include 
#include 

int main() {
    char csv[] = "Alice,24,Female\nBob,30,Male\nCharlie,28,Male"; // Datos CSV
    char *line = strtok(csv, "\n"); // Procesar línea por línea

    while (line != NULL) {
        char *name = strtok(line, ",");
        char *age = strtok(NULL, ",");
        char *gender = strtok(NULL, ",");

        printf("Nombre: %s, Edad: %s, Género: %s\n", name, age, gender);

        line = strtok(NULL, "\n");
    }

    return 0;
}

6.5 Resumen

Nombre: Alice, Edad: 24, Género: Female
Nombre: Bob, Edad: 30, Género: Male
Nombre: Charlie, Edad: 28, Género: Male

Conclusión

MétodoVentajasDesventajas
strtokPermite dividir fácilmenteModifica la cadena original
strtok_rSeguro para hilosUso un poco más complejo
Función personalizadaNo modifica la cadena originalEl código se alarga
Análisis CSVÚtil para procesamiento de datosPrestar atención a las limitaciones de strtok

7. Ejemplo de aplicación: Cómo extraer antes y después de un carácter específico

  • Para divisiones simples, usa strtok
  • Para multihilo, usa strtok_r
  • Si no quieres modificar el original, usa una función personalizada
  • También se puede aplicar al análisis de datos CSV
En la siguiente sección, explicamos en detalle «Ejemplo de aplicación: Cómo extraer antes y después de un carácter específico».

7.1 Usar strchr para obtener la cadena antes de un carácter específico

En el procesamiento de cadenas, a menudo es necesario realizar operaciones para extraer antes y después de un carácter o palabra clave específico. Por ejemplo, se pueden considerar casos como los siguientes.
  • Obtener solo la parte de dominio de una URL
  • Extraer el nombre de archivo de una ruta de archivo
  • Obtener la cadena antes y después de una etiqueta o símbolo específico
En C, se puede realizar este procesamiento utilizando strchr o strstr . Además, si se necesita un procesamiento más flexible, crear una función personalizada también es efectivo.

Sintaxis básica

Usando strchr , se puede identificar la posición de un carácter específico (el primero encontrado).

Ejemplo de uso: Obtener el nombre de archivo de una ruta de archivo

char *strchr(const char *str, int c);
  • str: Cadena de búsqueda
  • c: Carácter a buscar (tipo char )
strchr devuelve la dirección si encuentra c .

Resultado de ejecución

#include 
#include 

void get_filename(const char *path, char *filename) {
    char *pos = strrchr(path, '/'); // Buscar la última '/'
    
    if (pos != NULL) {
        strcpy(filename, pos + 1); // Copiar desde la posición siguiente a '/'
    } else {
        strcpy(filename, path); // Si no hay '/', copiar tal como está
    }
}

int main() {
    char path[] = "/home/user/documents/report.txt";
    char filename[50];
    
    get_filename(path, filename);
    printf("Nombre del archivo: %s\n", filename);
    
    return 0;
}

Puntos clave

Nombre del archivo: report.txt

7.2 Usar strstr para obtener la cadena después de una palabra clave específica

  • strrchr permite obtener la posición del carácter específico (/ ) que aparece por última vez.
  • pos + 1 copia la cadena siguiente a la barra diagonal, lo que permite obtener solo el nombre del archivo.

Sintaxis básica

Usando strstr , se puede buscar una cadena específica (palabra clave) y obtener la cadena desde esa posición en adelante.

Ejemplo de uso: Obtener el dominio de una URL

char *strstr(const char *haystack, const char *needle);
  • haystack: Cadena de búsqueda
  • needle: Subcadena a buscar
strstr devuelve la dirección de la posición si encuentra needle .

Resultado de ejecución

#include 
#include 

void get_domain(const char *url, char *domain) {
    char *pos = strstr(url, "://"); // Buscar la posición de "://"
    
    if (pos != NULL) {
        strcpy(domain, pos + 3); // Copiar desde después de "://"
    } else {
        strcpy(domain, url); // Si no hay "://", copiar tal como está
    }
}

int main() {
    char url[] = "https://www.example.com/page.html";
    char domain[50];
    
    get_domain(url, domain);
    printf("Parte de dominio: %s\n", domain);
    
    return 0;
}

Puntos clave

Parte de dominio: www.example.com/page.html

7.3 Usar strchr para dividir las partes antes y después de un carácter específico

  • strstr se usa para obtener desde "https://" o "http://" la parte posterior a "//" .
  • pos + 3 copia desde después de :// .

Ejemplo de uso: Separar nombre de usuario y dominio de una dirección de correo electrónico

Explotando strchr , se puede obtener dividiendo la cadena antes y después de un carácter específico.

Resultado de ejecución

#include 
#include 

void split_email(const char *email, char *username, char *domain) {
    char *pos = strchr(email, '@'); // Buscar la posición de '@'
    
    if (pos != NULL) {
        strncpy(username, email, pos - email); // Copiar antes de '@'
        username[pos - email] = '\0'; // Agregar carácter nulo
        strcpy(domain, pos + 1); // Copiar la parte después de '@'
    }
}

int main() {
    char email[] = "user@example.com";
    char username[50], domain[50];
    
    split_email(email, username, domain);
    printf("Nombre de usuario: %s\n", username);
    printf("Dominio: %s\n", domain);
    
    return 0;
}

Puntos clave

Nombre de usuario: user
Dominio: example.com

7.4 Aplicación: Extraer un atributo específico dentro de una etiqueta HTML

  • strchr busca la posición de '@' .
  • strncpy copia la parte antes de '@' y agrega el carácter nulo.
  • strcpy copia la parte después de '@' .

Ejemplo de uso: Obtener la URL de <a href="URL">

Para obtener un atributo específico de una etiqueta HTML, también se puede explotar strstr .

Resultado de ejecución

#include 
#include 

void get_href(const char *html, char *url) {
    char *start = strstr(html, "href=\""); // Buscar la posición de href="
    if (start != NULL) {
        start += 6; // Mover después de href="
        char *end = strchr(start, '"'); // Buscar la siguiente "
        if (end != NULL) {
            strncpy(url, start, end - start);
            url[end - start] = '\0'; // Agregar carácter nulo
        }
    }
}

int main() {
    char html[] = "Click Here";
    char url[100];
    
    get_href(html, url);
    printf("URL extraída: %s\n", url);
    
    return 0;
}

Puntos clave

URL extraída: https://example.com

7.5 Resumen

  • strstr busca la posición de "href="" y se mueve 6 caracteres.
  • strchr busca la posición de " (comilla) y determina el rango.

Conclusión

Contenido del procesamientoFunción utilizadaVentajas
Obtener antes de un carácter específicostrchr / strrchrSimple y rápido
Obtener después de un carácter específicostrstrPosible búsqueda de palabras clave
Dividir antes y después de un carácter específicostrchr + strncpyÚtil para división de nombre de usuario y dominio, etc.
Obtención de atributos de etiquetas HTMLstrstr + strchrAplicables a web scraping

8. Resumen

  • Explotando strchr o strstr , se puede obtener fácilmente antes y después de un carácter o palabra clave específico.
  • Útil en muchos escenarios, como procesamiento de rutas de archivos, análisis de URL y división de direcciones de correo electrónico.
  • Aplicable también a procesamientos avanzados como web scraping.

8.1 Repaso del artículo

En este artículo, hemos explicado en detalle los métodos para extraer subcadenas en el lenguaje C, desde lo básico hasta lo avanzado. Aquí, repasaremos los puntos clave de cada sección y organizaremos los métodos óptimos según el uso.

8.2 Métodos óptimos según el uso

SecciónContenidoPuntos clave
Conceptos básicos de cadenas en CEn C, las cadenas se manejan como arrays de char, y el carácter de terminación '\0' es importanteAl manejar cadenas, no olvidar el terminador nulo
Extracción con la biblioteca estándarAprovechar strncpy, strchr, etc.Con strncpy, es necesario agregar manualmente el terminador nulo
Extracción con funciones personalizadasCrear una función substring flexibleUsar malloc permite obtener subcadenas de longitud variable
Procesamiento según la codificación de caracteresMétodos de compatibilidad con UTF-8, Shift_JIS, EUC-JPUsar mbstowcs / wcstombs para convertir a caracteres amplios es seguro
Métodos para dividir cadenasDivisión con strtok, strtok_r o funciones personalizadasCon strtok, se modifica la cadena original, así que tener cuidado
Extracción antes y después de un carácter específicoObtención de datos con strchr, strstrSe puede aplicar a obtención de nombres de archivos, análisis de URL, análisis de HTML

1. Extracción de subcadenas

2. División de cadenas

Casos de usoMétodo óptimo
Quiero obtener una cadena de longitud fijastrncpy o substring()
Quiero una extracción segurastrncpy_s (C11 en adelante)
Manejar caracteres multibyte (UTF-8, Shift_JIS, EUC-JP)mbstowcs / wcstombs

3. Obtención antes y después de un carácter específico

Casos de usoMétodo óptimo
Quiero dividir la cadena de manera simplestrtok
Quiero una división thread-safestrtok_r
Quiero dividir sin modificar la cadena originalFunción personalizada (split_string())

8.3 Precauciones en el procesamiento de cadenas en C

Casos de usoMétodo óptimo
Obtener el nombre de archivo de una rutastrrchr(path, '/')
Obtener la parte de dominio de una URLstrstr(url, "://")
Separar nombre de usuario y dominio de una dirección de correostrchr(email, '@')
Obtener valores de atributos de etiquetas HTMLstrstr(tag, "href=\"") + strchr(tag, '"')

1. Gestionar exhaustivamente el terminador nulo '
Ejemplo de copia de cadena segura
'

2. Prestar atención a desbordamientos de búfer

En el procesamiento de cadenas en C, gestionar adecuadamente el carácter de terminación '\0' es lo más importante. Especialmente al usar strncpy o strchr, tenga cuidado de agregar manualmente el carácter nulo.

3. Para el procesamiento de caracteres multibyte, usar mbstowcs

#include 
#include 

int main() {
    char src[] = "Hello, World!";
    char dest[6];

    strncpy(dest, src, 5);
    dest[5] = '\0'; // Agregar terminador nulo por seguridad

    printf("Subcadena: %s\n", dest);

    return 0;
}

4. Gestión del tamaño del búfer

En las operaciones de cadenas en C, es necesario implementar con precaución para no acceder fuera del rango del array. Especialmente al usar strncpy, es importante controlar el número de bytes a copiar. Ejemplo de copia de cadena segura
#include 
#include 

int main() {
    char src[] = "Hello, World!";
    char dest[6];

    strncpy(dest, src, sizeof(dest) - 1);
    dest[5] = '\0'; // Agregar explícitamente el carácter nulo

    printf("Subcadena: %s\n", dest);
    return 0;
}

8.4 Hacia un aprendizaje más profundo

Al manejar caracteres multibyte como UTF-8 o Shift_JIS, funciones como strncpy o strlen no funcionan correctamente. Por lo tanto, al manejar caracteres multibyte, se recomienda convertir una vez a cadenas amplias con mbstowcs y procesar adecuadamente.
#include 
#include 
#include 

int main() {
    setlocale(LC_ALL, ""); // Establecer la localización

    char text[] = "¡Hola, mundo!"; // UTF-8
    wchar_t wtext[256];

    mbstowcs(wtext, text, 256); // Convertir a cadena amplia

    printf("Cadena amplia después de la conversión: %ls\n", wtext);

    return 0;
}

Temas para profundizar el aprendizaje

En el procesamiento de cadenas, es importante calcular de antemano el tamaño de memoria necesario y prevenir desbordamientos de búfer. Especialmente al asignar memoria dinámica con malloc, asegúrese de conocer con precisión su tamaño.

8.5 Resumen

El procesamiento de cadenas en C es una habilidad importante para mejorar la seguridad y legibilidad del programa. Basándose en el contenido introducido en este artículo, aprender los siguientes temas permitirá un procesamiento de cadenas más avanzado.

Temas para profundizar el aprendizaje

  1. Expresiones regulares (regex) (posible con bibliotecas externas de C)
  2. Operaciones de archivos (procesamiento de cadenas con fgets, fscanf)
  3. Gestión de memoria (procesamiento dinámico de cadenas con malloc, realloc)
  4. Análisis de datos (métodos de análisis de JSON, XML)

8.5 Resumen

  1. Las cadenas en C se gestionan con arrays de char, por lo que el manejo del carácter de terminación '\0' es importante
  2. Para extraer subcadenas, usar strncpy, substring(), malloc
  3. Para dividir cadenas, aprovechar strtok / strtok_r / funciones personalizadas
  4. Para obtener antes y después de un carácter específico, aprovechar strchr, strstr
  5. Para manejar caracteres multibyte (japonés), utilizar mbstowcs
  6. Tener en cuenta un procesamiento seguro de cadenas y prestar atención a desbordamientos de búfer
Al utilizar el contenido de este artículo, será posible realizar un procesamiento práctico de cadenas en C. Una vez comprendidas las funciones básicas, desafíese a crear funciones personalizadas y procesamiento avanzado para poder escribir código más eficiente.
侍エンジニア塾