Hi ha alguns problemes en el seu codi:
-
la funció
getNofTokens()
no pren la cadena de l’separador com a argument, compta el nombre de paraules separades per espais en blanc, el que pot retornar un recompte inconsistent al seu interlocutor. -
la mida assignat
result = malloc(sizeof(char *) * count + 1);
és incorrecte: hauria de ser:result = malloc(sizeof(char *) * (count + 1));
l’emmagatzematge de l’
NULL
punter final escriurà més enllà de la fi de l’espai assignat. -
emmagatzemar dit
NULL
terminador a la fi de la matriu és realment necessari, ja que el bloc de memòria retornat permalloc()
no està inicialitzat. -
la còpia de la cadena assignada i analitzada per
split_string
no es pot alliberar de forma segura perquè el puntertmp
no es guarda enlloc. El punter a la primera token serà diferent detmp
en 2 casos: si la cadena conté només delimitadors (no s’ha trobat cap testimoni) o si la cadena comença amb un delimitador (s’ometran els delimitadors inicials). Per simplificar el codi i fer-ho fiable, cada token podria duplicar itmp
hauria alliberar-se. De fet, la sevafree_split_string()
funció es basa en aquest comportament. Amb la implementació actual, el comportament és indefinit. -
que utilitza
unsigned long
iint
inconsistentment per a longituds de cadenes i variables d’índex de matriu. Per coherència, ha d’usarsize_t
per a tots dos. -
amb la qual ha d’assignar còpies de cadena
strdup()
. Si aquesta funció estàndard POSIX no està disponible al vostre sistema, escriviu una implementació simple. -
mai prova la falla d’assignació de memòria. Això està bé per a fins de prova i rebutjar codi, però tals falles potencials sempre s’han de tenir en compte en el codi de producció.
-
strtok()
és una funció difícil d’usar: modifica la cadena d’origen i manté un estat estàtic ocult que la fa no reentrant. Hauria evitar l’ús d’aquesta funció, encara que en aquest cas particular funciona correctament, però si la persona que trucasplit_string
o esgetNofTokens
basa en la preservació d’aquest estat ocult, obtindria un comportament inesperat.
Aquí hi ha una versió 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í hi ha una alternativa sense strtok()
i sense assignacions intermèdies:
#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 Després de tornar a llegir la especificació en el seu comentari, sembla haver-hi una possible confusió pel que fa a la semàntica de l ‘split
argument:
- Si
split
és un conjunt de delimitadors, el codi anterior fa la feina. I els exemples es dividiran com s’esperava. - if
split
és una cadena real que coincideix explícitament, el codi anterior només funciona per coincidència en els exemples donats en el comentari.
Per implementar l’última semàntica, ha d’usar strstr()
per buscar la split
subcadena en ambdós getNofTokens
i split_string
.
Aquí hi ha un exemple:
#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);}