Existem alguns problemas no seu código:
-
a função
getNofTokens()
Do Não pegue a cadeia separadora como um argumento, informa ao número de palavras separadas por espaços em branco, que podem devolver uma contagem inconsistente de seu interlocutor. -
o tamanho atribuído
result = malloc(sizeof(char *) * count + 1);
É incorreto: deve ser:result = malloc(sizeof(char *) * (count + 1));
O armazenamento do
NULL
O ponteiro final irá escrever além do final do espaço atribuído. -
Loja disse
NULL
Terminador no final da matriz é Realmente necessário, uma vez que o bloco de memória retornou pormalloc()
não é inicializado. -
A cópia da cadeia atribuída e analisada por
split_string
Não pode ser lançado com segurança porque o ponteirotmp
não é salvo em qualquer lugar. O ponteiro para o primeiro token será diferente detmp
em 2 casos: se a corrente contiver apenas delimitadores (nenhum token foi encontrado) ou se a string começar com um delimitador (os delimitadores iniciais omitidas). Para simplificar o código e torná-lo confiável, cada token pode duplicar etmp
deve ser liberado. Na verdade, sua funçãofree_split_string()
é baseada nesse comportamento. Com a implementação atual, o comportamento é indefinido. -
que usa
unsigned long
eint
Cada vez mais por comprimentos de string e variáveis de índice matric. Por consistência, você deve usarsize_t
para ambos. -
com os quais você deve atribuir cópias da cadeia
strdup()
Se esta função POSIX padrão não estiver disponível em seu sistema, insira uma implementação simples. -
Nunca teste a falha da atribuição da memória. Isso é bom para teste e código de descarte, mas essas falhas potenciais devem sempre ser levadas em conta no código de produção.
-
strtok()
É uma função difícil de usar: modifica a cadeia de origem e mantém um estado estático oculto que não o torna reentrada. Você deve evitar usar esta função, embora neste caso específico funcione corretamente, mas se o chamadorsplit_string
ougetNofTokens
Base na preservação de Este estado oculto, seria um comportamento inesperado.
Aqui está uma versão 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);}
aqui é uma alternativa sem strtok()
e sem atribuições intermediárias:
#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 após reler a especificação no seu comentário, lá Parece ser uma possível confusão em relação à semântica do split
- se
split
é um Conjunto de delimitadores, o código anterior faz o trabalho. E os exemplos serão divididos como esperado. - Se
split
é uma string real que corresponda explicitamente, o código anterior funciona apenas por coincidência nos exemplos dados no comentário.
para implementar a última semântica, você deve usar strstr()
para encontrar o split
subkue em ambos getNofTokens
e split_string
.
Aqui está um 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);}