2011-01-14 2 views
4

Рассмотрим следующий фрагмент, который использует strtok для разделения строки madddy.Попытка понять strtok

char* str = (char*) malloc(sizeof("Madddy")); 
strcpy(str,"Madddy"); 

char* tmp = strtok(str,"d"); 
std::cout<<tmp; 

do 
{ 
    std::cout<<tmp; 
    tmp=strtok(NULL, "dddy"); 
}while(tmp!=NULL); 

Он отлично работает, выход Ma. Но путем изменения strtok к следующему,

tmp=strtok(NULL, "ay"); 

Выход будет Madd. Как работает strtok? У меня есть этот вопрос, потому что я ожидал, что strtok возьмет каждый символ, который находится в строке разделителя, который будет использоваться как разделитель. Но в некоторых случаях это делается именно так, но в некоторых случаях это дает неожиданные результаты. Может ли кто-нибудь помочь мне понять это?

+6

Я честно думаю, что правильный способ сделать это, чтобы полностью прекратить использование 'strtok'. Это сложная в использовании, трудно отлаживаемая функция без каких-либо гарантий безопасности потоков. Вероятно, вам лучше использовать некоторую комбинацию 'string :: find' и' string :: substr' для синтаксического анализа. – templatetypedef

+0

Или 'boost :: token_iterator' –

+0

Я готов повторить это по важности и акценту, тем более, что вы используете C++, а не C. Также вы можете посмотреть в boost :: tokenize. –

ответ

1

Кажется, вы забыли, что у вас есть вызов strtok в первый раз (вне цикла) разделителем «d».

Strtok работает нормально.Вы должны иметь ссылку here.

Для второго примера (strtok("ay")):

Во-первых, вы вызываете strtok (ул, "d"). Он будет искать первый «d» и разделять вашу строку. В частности, он устанавливает tmp = «Ma», а str = «ddy» (отбрасывает первый «d»).

Затем вы вызываете strtok (str, "ay"). Он будет искать «a» в str, но поскольку ваша строка теперь только «ddy», сопоставление не происходит. Затем он будет искать «y». Итак str = "dd" и tmp = "".

Он печатает «Мэдд», как вы видели.

+0

@ Karthick: Первый пример работает, но он может работать не так, как вы думаете. Я рекомендую вместо cout << tmp, используя cout << tmp << »-« посмотреть, что на самом деле происходит. Вы можете видеть, что есть много пустых строк. –

10

«Попытка понять strtok» Удачи!

Во всяком случае, мы в 2011 году Tokenise должным образом:

std::string str("abc:def"); 
char split_char = ':'; 
std::istringstream split(str); 
std::vector<std::string> token; 

for (std::string each; std::getline(split, each, split_char); token.push_back(each)); 

: D

+3

Да, я понимаю, что это не дает строгого ответа на вопрос. Но ИМО это достойная и превосходная альтернатива, поэтому я думаю, что она по-прежнему стоит ответа. –

+0

Я полностью осознаю эту проблему. Но тогда мне было просто интересно узнать о стандартной спецификации! –

3

Фред щебенки, вероятно, использовали strtok(). Он предшествует многопоточным средам и превосходит (изменяет) исходную строку.

Когда вызывается с NULL для первого параметра, он продолжает разбор последней строки. Эта функция была удобной, но немного необычной даже в свое время.

+0

+1 для справки Фред Флинтстоун. –

2

На самом деле ваш код не так, не удивительно, что вы не получите неожиданные результаты:

char* str = (char*) malloc(sizeof("Madddy")); 

должен быть

char* str = (char*) malloc(strlen("Madddy") + 1); 
+1

Да, первый пример, вероятно, выделяет 4 байта (в 32-разрядных средах), который является размером указателя. Тип строковой константы, такой как '' abcdefghijkm ", является указателем (в частности,' char * 'или' const char * 'в зависимости от компилятора). – wallyk

+1

@wallyk: На самом деле, [это не так] (http://codepad.org/H7zJkjCN). Тип строковых литералов - это массив символов. @Anders: Хотя код странный, ваш работает * тождественно. * –

+0

@Fred Nurk: О да. Это, кажется, нарушает шаблон, вероятно, более полезно. Долгое время я избегаю конструкции. (Извините за неправильную информацию два комментария выше.) – wallyk

0

я задал вопрос, вдохновленный от другого вопроса о functions causing security problems/bad practise functions and the c standard library.

Цитирую ответ дал мне оттуда:

Типичной ошибкой с функцией strtok() заключается в предположении, что разобраны строка остается без изменений, в то время как он фактически заменяет разделитель символ с '\0'.

Также, strtok() используется, делая следующие звонки , до тех пор, пока целая цепочка не будет маркирована. Некоторые реализаций библиотек хранят strtok() «s внутреннего состояния в глобальной переменной , что может вызвать некоторые неприятных сюрпризы, если strtok() это вызывается из нескольких потоков в одновременно.

Как вы отметили свой вопрос C++, используйте что-то еще! Если вы хотите использовать C, я бы предложил реализовать свой собственный токенизатор, который работает безопасным образом.

0

Поскольку вы изменили свой тег на C, а не на C++, я переписал вашу функцию, чтобы использовать printf, чтобы вы могли видеть, что происходит. Хоанг прав. Вы видите правильный результат, но я думаю, что вы печатаете все на одной строке, поэтому вы запутались в выходе. Посмотрите на ответ Хоанга, объясняя, что происходит правильно. Кроме того, как отмечали другие, strtok уничтожает входную строку, поэтому вы должны быть осторожны в этом - и она не является потокобезопасной. Но если вам нужен быстрый грязный токенизатор, он работает. Кроме того, я изменил код, чтобы правильно использовать strlen, а не sizeof, как правильно указал Anders.

Вот ваш код изменен, чтобы быть более C-как:

char* str = (char*) malloc(strlen("Madddy") + 1); 
strcpy(str,"Madddy"); 

char* tmp = strtok(str,"d"); 
printf ("first token: %s\n", tmp); 

do 
{ 
    tmp=strtok(NULL, "ay"); 
    if (tmp != NULL) { 
     printf ("next token: %s\n", tmp); 
    } 
} while(tmp != NULL); 
Смежные вопросы