Punter lliure i invàlid

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 per malloc() no està inicialitzat.

  • la còpia de la cadena assignada i analitzada per split_string no es pot alliberar de forma segura perquè el punter tmp no es guarda enlloc. El punter a la primera token serà diferent de tmp 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 i tmp hauria alliberar-se. De fet, la seva free_split_string() funció es basa en aquest comportament. Amb la implementació actual, el comportament és indefinit.

  • que utilitza unsigned long i int inconsistentment per a longituds de cadenes i variables d’índex de matriu. Per coherència, ha d’usar size_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 truca split_string o es getNofTokens 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);}

Deixa un comentari

L'adreça electrònica no es publicarà. Els camps necessaris estan marcats amb *