Siehe auch: Is this a good substr() for C?C Tokenizer (und es kehrt zu leer, wenn Felder fehlen yay.!)
strtok()
und Freunde leere Felder überspringen, und ich weiß nicht, wie es zu sagen, nicht zu überspringen sondern kehren Sie in solchen Fällen leer zurück.
Ähnliches Verhalten von den meisten Tokenizern, die ich sehen konnte, und mich nicht einmal auf sscanf()
gestartet haben (aber dann hat es nie gesagt, dass es auf leeren Feldern anfangen würde).
Ich habe auf einer Rolle gewesen und schläfrig fühlen als gut, so hier geht es zur Übersicht:
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;
}
Hier ist der Fahrer:
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;
}
Ja, man bemerkt es richtig; Es funktioniert jetzt nur noch für ein einzelnes Delimiter, aber natürlich müssen wir uns um diesen einen Fehler kümmern.
Ausgänge:
[*]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'
Ich mache das auch ein Wiki, weil ich viele ähnliche Anfragen im Netz sah.
strdup() bei einer leeren Zeichenfolge kann fehlschlagen. Es wird meistens nicht - Sie haben Recht. Aber wenn das Programm, in das der Code eingebettet ist, seit einem Jahr läuft, ist möglicherweise der Speicherplatz knapp, sodass selbst dieser einfache Aufruf fehlschlägt. –
Die Definition des Feldes für strtok() und Freunde schließt die Möglichkeit von "leeren Feldern" aus. Wenn Sie versuchen, sie zu verwenden, wenn Sie ein einzelnes Trennzeichen benötigen, müssen Sie eine separate Funktion verwenden. Aber eine Funktion zu beschuldigen, nicht zu tun, was Sie wollen, wenn es nicht dafür ausgelegt ist, das zu tun, was Sie wollen, ist nicht sinnvoll. –
Hallo Jonathan! Ich habe nichts beschuldigt - die Funktionalität ist natürlich per definitionem, aber ich habe mich gefragt, wie es sinnvoll sein kann, leere Felder zu überspringen, anstatt ein leeres Token zurückzugeben. Leere Felder, die von einer Funktion zurückgegeben werden, können ignoriert werden, aber ein Wert, der nie zurückgegeben wurde, kann überhaupt nicht bearbeitet werden! – PoorLuzer