Как работает функция strtok в C?
Я нашел эту примерную программу, которая объясняет функцию strtok
:
#include <stdio.h>
#include <string.h>
int main ()
{
char str[] ="- This, a sample string.";
char * pch;
printf ("Splitting string \"%s\" into tokens:\n",str);
pch = strtok (str," ,.-");
while (pch != NULL)
{
printf ("%s\n",pch);
pch = strtok (NULL, " ,.-");
}
return 0;
}
Однако я не вижу, как это можно работать.
Как возможно, что pch = strtok (NULL, " ,.-");
возвращает новый токен. Я имею в виду, мы вызываем strtok
с NULL
. Это не имеет большого значения для меня.
Ответы
Ответ 1
Две вещи, которые нужно знать о strtok
. Как уже упоминалось, он "поддерживает внутреннее состояние". Кроме того, он испортил строку, которую вы ее кормите. По существу, он напишет '\0'
, где он найдет маркер, который вы предоставили, и вернет указатель на начало строки. Внутренне он поддерживает расположение последнего токена; и в следующий раз, когда вы его назовете, он начнется оттуда.
Важным следствием является то, что вы не можете использовать strtok
для строки типа const char* "hello world";
, так как вы получите нарушение доступа при изменении содержимого строки const char*
.
"Хорошая вещь" в strtok
заключается в том, что на самом деле она не копирует строки, поэтому вам не нужно управлять дополнительным распределением памяти и т.д. Но если вы не поймете вышеизложенное, у вас возникнут проблемы с его использованием.
Пример. Если у вас есть "это, есть, строка", последовательные вызовы strtok
будут генерировать указатели следующим образом (значение ^
- это возвращаемое значение). Обратите внимание, что добавляется '\0'
, где найдены токены; это означает, что исходная строка изменена:
t h i s , i s , a , s t r i n g \0 this,is,a,string
t h i s \0 i s , a , s t r i n g \0 this
^
t h i s \0 i s \0 a , s t r i n g \0 is
^
t h i s \0 i s \0 a \0 s t r i n g \0 a
^
t h i s \0 i s \0 a \0 s t r i n g \0 string
^
Надеюсь, что это имеет смысл.
Ответ 2
Функция strtok()
хранит данные между вызовами. Он использует эти данные, когда вы вызываете его с помощью указателя NULL.
Из http://www.cplusplus.com/reference/cstring/strtok/:
Точка, в которой был найден последний токен, хранится внутри с помощью функции, которая будет использоваться при следующем вызове (для предотвращения сбоев данных не требуется реализация конкретной библиотеки).
Ответ 3
strtok
поддерживает внутреннее состояние. Когда вы вызываете его с не-NULL, он повторно инициализирует себя, чтобы использовать строку, которую вы поставляете. Когда вы вызываете его с помощью NULL
, он использует эту строку и любое другое состояние, которое оно в настоящее время получает, чтобы вернуть следующий токен.
В связи с тем, как работает strtok
, вам необходимо убедиться, что вы связываетесь с многопоточной версией среды выполнения C, если вы пишете многопоточное приложение. Это гарантирует, что каждый поток получит собственное внутреннее состояние для strtok
.
Ответ 4
Функция strtok
хранит данные во внутренней статической переменной, которая распределяется между всеми потоками.
Для обеспечения безопасности потоков вы должны использовать strtok_r
От http://www.opensource.apple.com/source/Libc/Libc-167/string.subproj/strtok.c
Взгляните на static char *last;
char *
strtok(s, delim)
register char *s;
register const char *delim;
{
register char *spanp;
register int c, sc;
char *tok;
static char *last;
if (s == NULL && (s = last) == NULL)
return (NULL);
/*
* Skip (span) leading delimiters (s += strspn(s, delim), sort of).
*/
cont:
c = *s++;
for (spanp = (char *)delim; (sc = *spanp++) != 0;) {
if (c == sc)
goto cont;
}
if (c == 0) { /* no non-delimiter characters */
last = NULL;
return (NULL);
}
tok = s - 1;
/*
* Scan token (scan for delimiters: s += strcspn(s, delim), sort of).
* Note that delim must have one NUL; we stop if we see that, too.
*/
for (;;) {
c = *s++;
spanp = (char *)delim;
do {
if ((sc = *spanp++) == c) {
if (c == 0)
s = NULL;
else
s[-1] = 0;
last = s;
return (tok);
}
} while (sc != 0);
}
/* NOTREACHED */
}