2009-05-17 6 views
0

Voir aussi: Is this a good substr() for C?C Tokenizer (et il retourne vide aussi lorsque les champs manquent yay.!)


strtok() et les amis sauter sur les champs vides, et je ne sais pas comment dire que ce ne pas sauter mais revenez plutôt vide dans de tels cas.

Un comportement similaire à celui de la plupart des tokenizers que j'ai pu voir, et ne m'a même pas commencé sscanf() (mais alors il n'a jamais dit que cela fonctionnerait sur des champs vides pour commencer).

Je suis sur un rouleau et une sensation de somnolence et, donc ici, il va pour examen:

char* substr(const char* text, int nStartingPos, int nRun) 
{ 
    char* emptyString = strdup(""); /* C'mon! This cannot fail */ 

    if(text == NULL) return emptyString; 

    int textLen = strlen(text); 

    --nStartingPos; 

    if((nStartingPos < 0) || (nRun <= 0) || (textLen == 0) || (textLen < nStartingPos)) return emptyString; 

    char* returnString = (char *)calloc((1 + nRun), sizeof(char)); 

    if(returnString == NULL) return emptyString; 

    strncat(returnString, (nStartingPos + text), nRun); 

    /* We do not need emptyString anymore from this point onwards */ 
    free(emptyString); 
    emptyString = NULL; 

    return returnString; 
} 

int TokenizeC(const char* text, char delim, char ***output) 
{ 
    if((*output) != NULL) return -1; /* I will allocate my own storage */ 

    int nCountOfDelimiters = 0; 
    int dx = 0; 
    int nCountOfElements = 0; 
    int textLen = strlen(text); 

    for(; dx < textLen; ++dx) 
    { 
     if((text[dx] == delim) && (dx != (textLen - 1))) ++nCountOfDelimiters; 
     /* trailing delimiter need not be counted separately as we are */ 
     /* incrementing the count always by 1 */ 
    } 

    /* 
    We will have as many array elements as nCountOfDelimiters + 1 
    Tokenizing an empty string should return a single token that would 
    be empty (Is this not how most libraries behave? Or should it return NULL?) 
    */ 

    (*output) = (char **)malloc((1 + nCountOfDelimiters) * sizeof(char *)); 

    for(dx = 0; dx < textLen; dx++) 
    { 
     int nStartOfString = (1 + dx); 

     //printf("\n[! 1]dx = %d, nStartOfString = %d", dx, nStartOfString); 

     /* Get the run between delimiters */ 
     while((dx < textLen) && (text[dx] != delim)) dx++; 

     //printf("\n[! 2]dx = %d, nStartOfString = %d", dx, nStartOfString); 

     (*output)[nCountOfElements] = (1 + dx - nStartOfString) ? substr(text, nStartOfString, (1 + dx - nStartOfString)) : strdup(""); 

     //printf("\n[!]substr(text, %d, %d) => '%s'", nStartOfString, (1 + dx - nStartOfString), (*output)[nCountOfElements]); 

     if(NULL == (*output)[nCountOfElements]) 
     { 
      // Woops! Undo all 
      // TODO: How to test this scenario?! 

      for(; nCountOfElements >= 0; --nCountOfElements) 
      { 
       if((*output)[nCountOfElements] != NULL) free((*output)[nCountOfElements]); 
       (*output)[nCountOfElements] = NULL; 
      } 

      return -2; 
     } 

     ++nCountOfElements; 
    } 

    return nCountOfElements; /* Return the number of tokens if sucessful */ 
} 

void reclaim2D(char ***store, unsigned int itemCount) 
{ 
    for (int x = 0; itemCount < itemCount; ++x) 
    { 
     if((*store)[x] != NULL) free((*store)[x]); 
     (*store)[x] = NULL; 
    } 

    if((*store) != NULL) free((*store)); 
    (*store) = NULL; 
} 

Voici le pilote:

int main() 
{ 
    // Trailing '-' scenarios not giving correct count of elements 
    // (off by 1 for the last element that should come as empty) 

    const char *text = "1-2-3-6-7-8-9-10-11-", delim = '-'; // 10 elements 

    char **output = NULL; 

    int c = TokenizeC(text, delim, &output); 

    printf("\n\n[*]%d", c); 

    for (int x = 0; x < c; ++x) 
    { 
     printf("\n[main]'%s'", output[x]); //Expected : 1-2-3-6-7-8-9-10-11-<empty> 
    } 

    reclaim2D(&output, c); 

    text = "12-3-6-7-8-9-10-11"; // 8 elements 

    c = TokenizeC(text, delim, &output); 

    printf("\n\n[*]%d", c); 

    for(int x = 0; x < c; ++x) 
    { 
     printf("\n[main]'%s'", output[x]); //Expected : 12-3-6-7-8-9-10-11 
    } 

    reclaim2D(&output, c); 

    text = "-----2--4--6-7100000000-8-9-10-11-100000000-"; // 17 elements 

    c = TokenizeC(text, delim, &output); 

    printf("\n\n[*]%d", c); 

    for(int x = 0; x < c; ++x) 
    { 
     printf("\n[main]'%s'", output[x]); 
     //Expected <empty>-<empty>-<empty>-<empty> 
     // -<empty>-2-<empty>-4-<empty>-6-7100000000 
     // -8-9-10-11-100000000-<empty> 
    } 

    reclaim2D(&output, c); 

    text = "-----2--4--6-7100000000-8-9-10-11-100000000"; // 16 elements 

    c = TokenizeC(text, delim, &output); 

    printf("\n\n[*]%d", c); 

    for(int x = 0; x < c; ++x) 
    { 
     printf("\n[main]'%s'", output[x]); 
     //Expected : <empty>-<empty>-<empty>-<empty>-<empty> 
     //-2-<empty>-4-<empty>-6-7100000000-8-9-10-11-100000000 
    } 

    reclaim2D(&output, c); 

    return 0; 
} 

Oui, vous l'avez remarqué droit; cela fonctionne maintenant seulement pour un seul délimiteur, mais bien sûr, nous avons un bug à surveiller.

Sorties:

[*]9 
[main]'1' 
[main]'2' 
[main]'3' 
[main]'6' 
[main]'7' 
[main]'8' 
[main]'9' 
[main]'10' 
[main]'11' 

[*]8 
[main]'12' 
[main]'3' 
[main]'6' 
[main]'7' 
[main]'8' 
[main]'9' 
[main]'10' 
[main]'11' 

[*]16 
[main]'' 
[main]'' 
[main]'' 
[main]'' 
[main]'' 
[main]'2' 
[main]'' 
[main]'4' 
[main]'' 
[main]'6' 
[main]'7100000000' 
[main]'8' 
[main]'9' 
[main]'10' 
[main]'11' 
[main]'100000000' 

[*]16 
[main]'' 
[main]'' 
[main]'' 
[main]'' 
[main]'' 
[main]'2' 
[main]'' 
[main]'4' 
[main]'' 
[main]'6' 
[main]'7100000000' 
[main]'8' 
[main]'9' 
[main]'10' 
[main]'11' 
[main]'100000000' 

Je suis aussi ce qui en fait un wiki parce que j'ai vu de nombreuses demandes similaires sur le net.

+1

strdup() sur une chaîne vide peut échouer. Ce n'est pas le cas le plus souvent - vous avez raison. Mais si le programme dans lequel votre code est intégré fonctionne depuis un an, il se peut qu'il manque d'espace pour que même cet appel échoue. –

+1

La définition du champ pour strtok() et les amis exclut la possibilité de 'champs vides'. Si vous essayez de les utiliser lorsque vous voulez un seul caractère de séparation, vous devez utiliser une fonction distincte. Mais blâmer une fonction pour ne pas faire ce que vous voulez quand il n'est pas conçu pour faire ce que vous voulez n'est pas raisonnable. –

+0

Salut Jonathan! Je ne blâmais rien - la fonctionnalité est par définition bien sûr, mais je me demandais comment sauter sur des champs vides au lieu de retourner un jeton vide pourrait être utile. Les champs vides retournés par une fonction peuvent être ignorés, mais une valeur jamais retournée ne peut pas être traitée du tout! – PoorLuzer

Répondre

Questions connexes