Hai algúns problemas no seu código:
-
a función
getNofTokens()
non tomar a cadea separadora como un argumento, conta o número de palabras separadas por espazos en branco, que poden devolver un reconto inconsistente do seu interlocutor. -
O tamaño asignado
result = malloc(sizeof(char *) * count + 1);
é incorrecto: debe ser:result = malloc(sizeof(char *) * (count + 1));
o almacenamento do
NULL
O punteiro final escribirá máis aló do final do espazo asignado. -
Store dixo
NULL
Terminator ao final da matriz é Realmente necesario, xa que o bloque de memoria devolto pormalloc()
non está inicializado. -
A copia da cadea asignada e analizada por
split_string
non se pode lanzar de forma segura porque o punteirotmp
non se garda en ningún lado. O punteiro ao primeiro token será diferente detmp
en 2 casos: se a cadea contén só delimitadores (non se atopou ningún token) ou se a corda comeza cun delimitador (os delimitadores omitir as iniciais). Para simplificar o código e facelo fiable, cada token podería duplicar etmp
debe ser lanzado. De feito, a súa funciónfree_split_string()
baséase neste comportamento. Coa implementación actual, o comportamento é indefinido. -
que usa
unsigned long
eint
Cada vez máis para lonxitudes de cordas e variables de índice de matriz. Por consistencia, debes usarsize_t
para ambos. -
co que debes asignar copias da cadea
strdup()
Se esta función de POSIX estándar non está dispoñible no seu sistema, introduza unha aplicación sinxela. -
Nunca proba a falla de asignación de memoria. Isto é bo para a proba e o código de eliminación, pero estes fallos potenciais deben ter en conta sempre no código de produción.
-
strtok()
é unha función difícil de usar: Modifica a cadea de orixe e mantén un estado estático oculto que non o fai reentrante. Debería evitar usar esta función, aínda que neste caso particular funciona correctamente, pero se o chamadorsplit_string
ougetNofTokens
sobre a preservación de Este estado oculto, obtería un comportamento inesperado.
Aquí tes unha versión modificada:
#include <stdio.h>#include <string.h>#include <stdlib.h>#include "stringsplit.h"/* Split string by another string, return split parts + NULL in array. * * Parameters: * str: the string to split * split: the string to split str with * * Returns: * A dynamically reserved array of dynamically reserved string parts. * * For example called with "Test string split" and " ", * returns . * Or called with "Another - test" and " - ", * returns . */size_t getNofTokens(const char *string, const char *split) { char *tmp = strdup(string); size_t count = 0; if (strtok(tmp, split) != NULL) { count++; while (strtok(NULL, split) != NULL) count++; } free(tmp); return count;}char **split_string(const char *str, const char *split) { size_t count = getNofTokens(str, split); char **result = malloc(sizeof(*result) * (count + 1)); char *tmp = strdup(str); char *token = strtok(tmp, split); size_t idx = 0; while (token != NULL && idx < count) { result = strdup(token); token = strtok(NULL, split); } result = NULL; free(tmp); return result;}void print_split_string(char **split_string) { for (size_t i = 0; split_string != NULL; i++) { printf("%s\n", split_string); }}void free_split_string(char **split_string) { for (size_t i = 0; split_string != NULL; i++) { free(split_string); } free(split_string);}
aquí é unha alternativa sen strtok()
e sen asignacións intermedias:
#include <stdio.h>#include <string.h>#include <stdlib.h>#include "stringsplit.h"size_t getNofTokens(const char *str, const char *split) { size_t count = 0; size_t pos = 0, len; for (pos = 0;; pos += len) { pos += strspn(str + pos, split); // skip delimiters len = strcspn(str + pos, split); // parse token if (len == '\0') break; count++; } return count;}char **split_string(const char *str, const char *split) { size_t count = getNofTokens(str, split); char **result = malloc(sizeof(*result) * (count + 1)); size_t pos, len, idx; for (pos = 0, idx = 0; idx < count; pos += len, idx++) { pos += strspn(str + pos, split); // skip delimiters len = strcspn(str + pos, split); // parse token if (len == '\0') break; result = strndup(str + pos, len); } result = NULL; return result;}void print_split_string(char **split_string) { for (size_t i = 0; split_string != NULL; i++) { printf("%s\n", split_string); }}void free_split_string(char **split_string) { for (size_t i = 0; split_string != NULL; i++) { free(split_string); } free(split_string);}
Editar despois de reler a especificación do seu comentario, alí Parece ser unha posible confusión sobre a semántica do argumento :
- Se
split
é a Conxunto de delimitadores, o código anterior fai o traballo. E os exemplos dividiranse como se esperaba. - Se
split
é unha serie real que coincide explícitamente, o código anterior só funciona por coincidencia nos exemplos que se indican no comentario.
Para implementar a última semántica, debes usar strstr()
para atopar a subkue
en ambosgetNofTokens
esplit_string
.
Aquí está un exemplo:
#include <stdio.h>#include <string.h>#include <stdlib.h>#include "stringsplit.h"/* Split string by another string, return split parts + NULL in array. * * Parameters: * str: the string to split * split: the string to split str with * * Returns: * A dynamically reserved array of dynamically reserved string parts. * * For example called with "Test string split" and " ", * returns . * Or called with "Another - test" and " - ", * returns . */size_t getNofTokens(const char *str, const char *split) { const char *p; size_t count = 1; size_t len = strlen(split); if (len == 0) return strlen(str); for (p = str; (p = strstr(p, split)) != NULL; p += len) count++; return count;}char **split_string(const char *str, const char *split) { size_t count = getNofTokens(str, split); char **result = malloc(sizeof(*result) * (count + 1)); size_t len = strlen(split); size_t idx; const char *p = str; for (idx = 0; idx < count; idx++) { const char *q = strstr(p, split); if (q == NULL) { q = p + strlen(p); } else if (q == p && *q != '\0') { q++; } result = strndup(p, q - p); p = q + len; } result = NULL; return result;}void print_split_string(char **split_string) { for (size_t i = 0; split_string != NULL; i++) { printf("%s\n", split_string); }}void free_split_string(char **split_string) { for (size_t i = 0; split_string != NULL; i++) { free(split_string); } free(split_string);}