Почему strtok_r всегда находит шаблон .?

В моей системе следующая программа:

int main(){
        char *strgptr;
        char buf[5] = {'b','a','a','a','\0'};
        char *tmp = strtok_r(buf, ".", &strgptr);
        if(tmp != NULL){
                printf("Found a . in baaa?\n");
                printf("It was found starting at: %s\n", tmp);
        }
        else
                printf("Everything is working.\n");
}

печатает:

Found a . in baaa?
It was found starting at: baaa

Однако, если я поменяю местами "." строка разделителя в strtok_r для "a", я получаю (как и ожидалось):

Found a . in baaa?
It was found starting at: b

Но замена "." для любого другого символа, не появляющегося в buf (например, "c"), получается:

Found a . in baaa?
It was found starting at: baaa

Страница руководства для strtok_r, как и ожидалось, говорит:

The strtok() and strtok_r() functions return a pointer to the next token, 
or NULL if there are no more tokens.

Так почему же strtok_r не возвращает NULL при передаче строки, не содержащей ни одного из рассматриваемых токенов?


person John Doucette    schedule 19.11.2012    source источник


Ответы (3)


Поскольку разделитель не найден, вы получаете всю строку. Это действует так, как будто после строки есть невидимый разделитель.

person unwind    schedule 19.11.2012
comment
@JohnDoucette Хороший вопрос ... Я думаю, это не очень очевидно, хотя и противоположное (ваш ожидаемый результат) также не очевидно для меня. - person unwind; 19.11.2012
comment
Я думал, что, поскольку у меня должен быть какой-то способ доступа к buf (чтобы передать его вызову strtok), возвращать указатель на buf после вызова strtok бессмысленно, поскольку я не могу сказать, содержит ли строка мой разделитель. без выполнения strcmp или подобного. Напротив, если возвращается NULL, у меня все еще есть buf, но теперь это O (1), работающий для проверки наличия разделителя. Я думаю, что вместо этого я мог бы работать с strgptr, но это решение для конкретной реализации... - person John Doucette; 19.11.2012
comment
@John: это задокументировано в стандарте: функция strtok затем ищет оттуда байт, который содержится в текущей строке разделителя. Если такой байт не найден, текущий токен расширяется до конца строки, на которую указывает s1. На справочной странице моей системы об этом не говорится — лично я считаю ее дефектной, потому что она описывает, как возвращаются последующие токены, но на самом деле не говорит, что такое первый токен в случае отсутствия разделителей. - person Steve Jessop; 19.11.2012
comment
@SteveJessop Спасибо! Хорошо, что это хоть где-то задокументировано. Странно, что это не одна из наших справочных страниц. - person John Doucette; 19.11.2012
comment
@John: это обычное хобби людей, которые внедряют стандарты, переписывать текст стандарта так, как они считают проще. Они, вероятно, больше выигрывают, чем проигрывают в плане создания краткого справочника по стандарту, но часто менее точны, и поэтому вам всегда следует обращаться к стандарту, если вы удивлены поведением или если справочная страница расплывчата. В этом случае справочная страница расплывчата, но я не думаю, что она очевидно расплывчата, я вижу, как вы ее читали. - person Steve Jessop; 19.11.2012
comment
Есть причина, по которой я не доверяю справочным страницам в отношении стандартных функций C. - person DevSolar; 19.11.2012

Так как разделитель "." не найден в buf, ваш вызов strtok успешно возвращает указатель на ваш первый (и единственный) токен: "baaa".

person DevSolar    schedule 19.11.2012
comment
Хорошо, в этом есть некоторый смысл, но почему он разработан таким образом? Например, в приведенном выше коде я не могу использовать if(tmp != buf), потому что он всегда будет равен buf для первого токена. Поэтому проверка наличия разделителя вообще должна быть совершенно отдельной операцией. Кажется расточительным, так как strtok все равно проверяет это в ходе своей работы. - person John Doucette; 19.11.2012
comment
Причина, по которой он разработан таким образом, заключается в том, что strtok предназначен для разделения строки на токены, разделенные разделителем. Ваша строка состоит из одного токена (не из ни одного) точно так же, как строка hello содержит одно слово (не из ни одного). - person Steve Jessop; 19.11.2012

Я полагаю, вам действительно нужно использовать функцию strstr(). strtok_r предназначен для разделения строк, например, на запятые или \n.

person LtWorf    schedule 19.11.2012