2010-01-31 21 views
5

¿Cuál sería una forma eficiente de convertir una cadena delimitada en una matriz de cadenas en C (no en C++)? Por ejemplo, podría tener:C: crear un conjunto de cadenas a partir de una cadena fuente delimitada

char *input = "valgrind --leak-check=yes --track-origins=yes ./a.out" 

La cadena fuente siempre tendrá un único espacio como delimitador. Y me gustaría una matriz de cadenas malloc'ed malloc'ed char *myarray[] tal que:

myarray[0]=="valgrind" 
myarray[1]=="--leak-check=yes" 
... 

Editar Tengo que asumir que hay un número arbitrario de fichas en el inputString así que no puedo acaba de limitar a 10 o algo así.

He intentado una solución desordenada con strtok y una lista vinculada que he implementado, pero valgrind se quejó tanto que me rendí.

(Si usted se está preguntando, esto es para una estructura básica de Unix que estoy tratando de escribir.)

+0

@Sneesh: Este es un excelente ejemplo de cómo puedes hacerlo en C, ya que el viejo adagio es 'Hay muchas maneras de despellejar a un gato ...' +1 de mi parte. – t0mm13b

Respuesta

2

Lo que es algo como:

char* string = "valgrind --leak-check=yes --track-origins=yes ./a.out"; 
char** args = (char**)malloc(MAX_ARGS*sizeof(char*)); 
memset(args, 0, sizeof(char*)*MAX_ARGS); 

char* curToken = strtok(string, " \t"); 

for (int i = 0; curToken != NULL; ++i) 
{ 
    args[i] = strdup(curToken); 
    curToken = strtok(NULL, " \t"); 
} 
+0

En realidad, creo que usar un búfer 256 de punteros a cadenas no sería un desperdicio, a menos que realmente necesite preservar la memoria ... – Jack

+0

strtok() modifica la cadena de entrada, por lo que usarla en una cadena literal se bloqueará en algunos plataformas. – bk1e

+0

Podría suponer que 'MAX_ARGS' es algo seguro como 10,000, pero el código todavía debería funcionar para 10,001 args ... – yavoh

1

¿Se le recuerda a malloc un byte adicional para la terminación nulo que marca el final de la cadena?

+0

Sí: 'char * singleToken = (char *) malloc (strlen (tokPtr) * sizeof (char) +1);' donde 'tokPtr' era el valor de retorno de' strtok'. – yavoh

1

Desde la página de manual strsep(3) en OSX:

char **ap, *argv[10], *inputstring; 

    for (ap = argv; (*ap = strsep(&inputstring, " \t")) != NULL;) 
      if (**ap != '\0') 
        if (++ap >= &argv[10]) 
          break; 

Editado por arbitraria # de fichas:

char **ap, **argv, *inputstring; 

int arglen = 10; 
argv = calloc(arglen, sizeof(char*)); 
for (ap = argv; (*ap = strsep(&inputstring, " \t")) != NULL;) 
    if (**ap != '\0') 
     if (++ap >= &argv[arglen]) 
     { 
      arglen += 10; 
      argv = realloc(argv, arglen); 
      ap = &argv[arglen-10]; 
     } 

O algo parecido a eso. Lo anterior puede no funcionar, pero si no, no está lejos. Crear una lista vinculada sería más eficiente que llamar continuamente al realloc, pero eso es realmente además del punto: el punto es cómo hacer mejor uso de strsep.

+0

Gracias. Olvidé mencionar que tengo que suponer que hay un número arbitrario de tokens en 'inputString', por ejemplo, no puedo suponer 10. – yavoh

2

si tiene toda la entrada en input, para empezar, entonces nunca puede tener más fichas que strlen(input). Si no permite "" como token, entonces nunca podrá tener más de strlen(input)/2 tokens. Por lo tanto, a menos que input sea enorme, puede escribir con seguridad.

char ** myarray = malloc((strlen(input)/2) * sizeof(char*)); 

int NumActualTokens = 0; 
while (char * pToken = get_token_copy(input)) 
{ 
    myarray[++NumActualTokens] = pToken; 
    input = skip_token(input); 
} 

char ** myarray = (char**) realloc(myarray, NumActualTokens * sizeof(char*)); 

Como optimización adicional, puede mantener input alrededor y simplemente sustituir los espacios \ 0 y poner punteros en el búfer input en myarray []. No es necesario un malloc separado para cada token a menos que por alguna razón necesite para liberarlos individualmente.

+0

Usando su idea 'strlen (input)/2'- ¡Gracias! – yavoh

0

En cuanto a las otras respuestas, para un principiante en C, se vería complejo debido al tamaño del código, pensé que podría poner esto en un principiante, podría ser más fácil analizar la cadena en lugar de utilizando strtok ... algo como esto:

 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 
#include <ctype.h> 

char **parseInput(const char *str, int *nLen); 
void resizeptr(char ***, int nLen); 

int main(int argc, char **argv){ 
    int maxLen = 0; 
    int i = 0; 
    char **ptr = NULL; 
    char *str = "valgrind --leak-check=yes --track-origins=yes ./a.out"; 
    ptr = parseInput(str, &maxLen); 
    if (!ptr) printf("Error!\n"); 
    else{ 
     for (i = 0; i < maxLen; i++) printf("%s\n", ptr[i]); 
    } 
    for (i = 0; i < maxLen; i++) free(ptr[i]); 
    free(ptr); 
    return 0; 
} 

char **parseInput(const char *str, int *Index){ 
    char **pStr = NULL; 
    char *ptr = (char *)str; 
    int charPos = 0, indx = 0; 
    while (ptr++ && *ptr){ 
     if (!isspace(*ptr) && *ptr) charPos++; 
     else{ 
      resizeptr(&ptr, ++indx); 
      pStr[indx-1] = (char *)malloc(((charPos+1) * sizeof(char))+1); 
      if (!pStr[indx-1]) return NULL; 
      strncpy(pStr[indx-1], ptr - (charPos+1), charPos+1); 
      pStr[indx-1][charPos+1]='\0'; 
      charPos = 0; 
     } 
    } 
    if (charPos > 0){ 
     resizeptr(&pStr, ++indx); 
     pStr[indx-1] = (char *)malloc(((charPos+1) * sizeof(char))+1); 
     if (!pStr[indx-1]) return NULL; 
     strncpy(pStr[indx-1], ptr - (charPos+1), charPos+1); 
     pStr[indx-1][charPos+1]='\0'; 
    } 
    *Index = indx; 
    return (char **)pStr; 
} 

void resizeptr(char ***ptr, int nLen){ 
    if (*(ptr) == (char **)NULL){ 
     *(ptr) = (char **)malloc(nLen * sizeof(char*)); 
     if (!*(ptr)) perror("error!"); 
    }else{ 
     char **tmp = (char **)realloc(*(ptr),nLen); 
     if (!tmp) perror("error!"); 
     *(ptr) = tmp; 
    } 
} 

poco he modificado el código para hacerlo más fácil. La única función de cadena que usé fue strncpy ..seguro que es un poco largo, pero reasigna dinámicamente la matriz de cadenas en lugar de utilizar un MAX_ARGS codificado, lo que significa que el puntero doble ya está acaparando la memoria cuando solo 3 o 4 harían, lo que también haría que el uso de memoria eficiente y pequeño, mediante el uso de realloc, el análisis simple se cubre mediante el empleo de isspace, ya que itera con el puntero. Cuando se encuentra un espacio, realloc ates el puntero doble, y malloc el desplazamiento para mantener la cadena.

Observe cómo se usan los punteros triples en la función resizeptr ... de hecho, pensé que esto serviría como un excelente ejemplo de un simple programa C, punteros, realloc, malloc, paso por referencia, elemento básico de análisis una cadena ...

Espero que esto ayude, Saludos cordiales, Tom.

Cuestiones relacionadas