Windows. Вирусы. Ноутбуки. Интернет. Office. Утилиты. Драйверы

Other Alias

strtok

ОБЗОР

#include

char *strtok(char * str , const char * delim );
char *strtok_r(char * str , const char * delim , char ** saveptr );

Требования макроса тестирования свойств для glibc (см. feature_test_macros (7)):

strtok_r (): _SVID_SOURCE || _BSD_SOURCE || _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCE

ОПИСАНИЕ

Функция strtok () разделяет строку на последовательность нуля или более непустых токенов. При первом вызове strtok () анализируемую строку нужно указывать в аргументе str . В каждом последующем вызове, в котором анализируется эта же строка, значение str должно быть NULL.

В аргументе delim задаётся набор байт, которые считаются разделителями токенов в анализируемой строке. Вызывающий может указывать разные строки в delim в последующих вызовах при анализе той же строки.

Каждый вызов strtok () возвращает указатель на строку, завершающуюся null, которая содержит следующий токен. Эта строка не включает байт-разделитель. Если больше токенов нет, то strtok () возвращает NULL.

Последовательность вызовов strtok (), оперирующих одной строкой, поддерживает указатель, который определяет точку, с которой начинается поиск следующего токена. Первый вызов strtok () назначает этому указателю ссылку на первый байт строки. Начало следующего токена определяется поиском вперёд в str следующего байта не разделителя. Если байт найден, то он берётся в качестве начала следующего токена. Если такой байт не найден, то токенов больше нет и strtok () возвращает NULL (для пустой строки или состоящей только из разделителей в этом случае NULL вернётся при первом вызове strtok ()).

Конец каждого токена находится поиском вперёд, длящемся до тех пор, пока не будет найден байт-разделитель или завершающий байт null ("\0"). Если найден байт-разделитель, то он заменяется байтом null для завершения текущего токена, и strtok () сохраняет указатель на следующий байт; этот указатель будет использован в качестве начальной точки при поиске следующего токена. В этом случае strtok () возвращает указатель на начало найденного токена.

Из описания выше следует, что последовательность из двух и более непрерывных байтов-разделителей в просматриваемой строке считается одним разделителем, а байты-разделители в начале или конце строки игнорируются. Другими словами, токены, возвращаемые strtok () - всегда не пустые строки. То есть, например, если есть строка «aaa;;bbb, », то последующие вызовы strtok () с заданными разделителями строк «;, » вернули бы строки «aaa » и «bbb », а затем указатель null.

Функция strtok_r () является реентерабельной версией strtok (). Аргумент saveptr является указателем на переменную char * , которая используется внутри strtok_r () для учёта контекста между последующими вызовами при анализе одной и той же строки.

При первом вызове strtok_r () значение str должно указывать на анализируемую строку, а значение saveptr игнорируется. При последующих вызовах значение str должно быть NULL, а значение saveptr не должно изменяться с момента предыдущего вызова.

Одновременно могут анализироваться разные строки при нескольких запусках strtok_r () с различными аргументами saveptr .

ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ

Функции strtok () и strtok_r () возвращают указатель на следующий токен или NULL, если больше токенов нет.

АТРИБУТЫ

Описание терминов данного раздела смотрите в attributes (7).
Интерфейс Атрибут Значение
strtok () безвредность в нитях небезопасно (MT-Unsafe race:strtok)
strtok_r () безвредность в нитях безвредно (MT-Safe)

СООТВЕТСТВИЕ СТАНДАРТАМ

strtok () POSIX.1-2001, POSIX.1-2008, C89, C99, SVr4, 4.3BSD. strtok_r () POSIX.1-2001, POSIX.1-2008.

ДЕФЕКТЫ

Используйте данные функции с осторожностью. Учитывайте, что: * Эти функции изменяют свой первый аргумент. * Эти функции не могут использоваться со строками-константами. * Теряется идентичность байта-разделителя. * При анализе функция strtok () использует статический буфер, поэтому не является безопасной для нитей. Используйте strtok_r () в этом случае.

ПРИМЕР

В программе, представленной далее, используются вложенные циклы, которые вызывают strtok_r () для разделения строки на составляющие её токены. В первом параметре командной строки задаётся анализируемая строка. Во втором параметре задаётся байт(ы)- разделитель, который используется для деления строки на «составные» токены. В третьем параметре указывается байт(ы)- разделитель, который используется для разделения «составных» токенов на подтокены.

Пример результата вывода программы:

$ ./a.out "a/bbb///cc;xxx:yyy:" ":;" "/" 1: a/bbb///cc --> a --> bbb --> cc 2: xxx --> xxx 3: yyy --> yyy

Исходный код программы

#include #include #include int main(int argc, char *argv) { char *str1, *str2, *token, *subtoken; char *saveptr1, *saveptr2; int j; if (argc != 4) { fprintf(stderr, "Использование: %s string delim subdelim\n", argv); exit(EXIT_FAILURE); } for (j = 1, str1 = argv; ; j++, str1 = NULL) { token = strtok_r(str1, argv, &saveptr1); if (token == NULL) break; printf("%d: %s\n", j, token); for (str2 = token; ; str2 = NULL) { subtoken = strtok_r(str2, argv, &saveptr2); if (subtoken == NULL) break; printf(" --> %s\n", subtoken); } } exit(EXIT_SUCCESS); }

Ещё один пример программы, использующей strtok (), можно найти в getaddrinfo_a (3).

4 ответа

Две вещи, которые нужно знать о 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 ^

Надеюсь, что это имеет смысл.

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

Точка, в которой был найден последний токен, хранится внутри с помощью функции, которая будет использоваться при следующем вызове (для предотвращения сбоев данных не требуется реализация конкретной библиотеки).

strtok поддерживает внутреннее состояние. Когда вы вызываете его с не-NULL, он повторно инициализирует себя, чтобы использовать строку, которую вы поставляете. Когда вы вызываете его с помощью NULL , он использует эту строку и любое другое состояние, которое оно в настоящее время получает, чтобы вернуть следующий токен.

В связи с тем, как работает strtok , вам необходимо убедиться, что вы связываетесь с многопоточной версией среды выполнения C, если вы пишете многопоточное приложение. Это гарантирует, что каждый поток получит собственное внутреннее состояние для strtok .

Функция strtok хранит данные во внутренней статической переменной, которая распределяется между всеми потоками.

Для обеспечения безопасности потоков вы должны использовать strtok_r

Взгляните на 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 */ }

поделиться

Функция strtok() возвращает указатель на следующую лексему в строке, адресуемой параметром str1. Символы, образующие строку, адресуемую параметром str2, представляют собой разделители, которые определяют лексему. При отсутствии лексемы, подлежащей возврату, возвращается нулевой указатель.

В версии С99 к параметрам str1 и str2 применен квалификатор restrict.

Чтобы разделить некоторую строку на лексемы, при первом вызове функции strtok() параметр str1 должен указывать на начало этой строки. При последующих вызовах функции в качестве параметра str1 нужно использовать нулевой указатель. В этом случае полная строка будет модифицироваться при каждом вызове функции.

При каждом обращении к функции strtok() можно использовать различные наборы разделителей лексем.

Функция strtok() предоставляет средство, позволяющее сократить строку до составляющих ее частей. Например, следующая программа разделяет на лексемы строку "One, two, and three".

#include #include int main(void) char *p; p = strtok("One, two, and three.", ","); printf(p); do { p = strtok(NULL, ",. "); if(p) printf("|%s", p); } while(p), return 0; }

Результат работы этой программы имеет следующий вид.

One | two | and | three

Обратите внимание, как функция strtok() сначала вызывается с исходной строкой, но в последующих ее вызовах в качестве первого аргумента используется значение NULL. Функция strtok() поддерживает внутренний указатель обрабатываемой строки. Если первый аргумент функции strtok() указывает на строку, внутренний указатель устанавливается в начало этой строки. Если первый аргумент равен значению NULL, функция strtok() продолжает процесс обработки предыдущей строки, начиная с позиции, запомненной на предыдущем шаге, и продвигает внутренний указатель по мере получения очередной лексемы. Таким образом, функция strtok() "проходит" всю строку. Также обратите внимание на то, как изменяется строка, задающая разделители, при первом и последующих вызовах функции. При каждом вызове разделители могут определяться по-разному.

char far * far _fstrtok(const char far *str1, const char far *str2)

Описание:

Функция strtok() возвращает указатель на следующую лексему в строке, на которую указывает str1. Символы из строки, на которую указывает str2, используются как ограничители, определяю­щие лексему. Если лексема не найдена, возвращается NULL.

Во время первого вызова функции strtok() в качестве указателя в самом деле используется str1. При последующих вызовах в качестве первого аргумента используется NULL. Таким образом вся строка может быть разбита на лексемы.

Важно понимать, что функция strtok() модифицирует строку, на которую указывает str1. Каж­дый раз, когда найдена лексема, на месте, где был найден ограничитель, помещается нулевой символ. Таким образом strtok() продвигается вдоль строки.

При каждом вызове strtok() можно варьировать набор ограничителей.

Функция _fstrtok() является FAR-версией рассматриваемой функции.

Следующая программа разбивает на лексемы строку «The summer soldier, the sunshine patriot», используя в качестве ограничителей пробелы и запятые. В результате работы программы будет сформирована строка следующего вида: «The | summer | soldier | the | sunshine | patriot».
#include
#include
int main(void )
{
char * p;
p = strtok ("The summer soldier, the sunshine patriot" , " " ) ;
printf (p) ;
do {
p= strtok ("\0 " , ", " ) ;
if (p) printf ("|% s" , p) ;
} while (p) ;
return 0 ;
}

Языки программирования могут включать специальные функции для работы со строками, избавляя тем самым программиста от необходимости писать собственные функции обработки строк. Например, часто требуется определить длину строки, и поэтому в языках предусмотрена функция, измеряющая ее длину.

В языке программирования C функции для работы со строками объявляются в заголовочном файле string.h, который надо не забывать подключать к своему исходному коду. Существует около двадцати функций для работы со строками. Среди них есть те, которые осуществляют поиск символов в строке, функции сравнения, копирования строк, а также более специфические. Перечень и описание большинства существующих на данный момент в языке C функций можно найти в приложении книги Б. Кернигана, Д. Ритчи "Язык программирования C. Второе издание".

Все функции, объявленные в string.h, в процессе своей работы могут изменять или не изменять одну из переданных по указателю строк. Это зависит от назначения функции. Однако большинство из них что-то возвращают: либо указатель на символ, либо целое. При этом если функция меняет один из своих параметров и ради этого была вызвана, тогда то, что она возвращает, можно проигнорировать (т.е. ничему не присваивать в вызывающей функции).

Например, функция strcpy() имеет такое объявление: char *strcpy (char *, const char*) . Она копирует строку, на которую указывает второй параметр, в строку, на которую указывает первый параметр. Таким образом первый параметр изменяется. Кроме того, функция возвращает указатель на первый символ строки:

char s1[ 10 ] , s2[ 10 ] ; char * s3; s3 = s2; gets (s1) ; s3 = strcpy (s2, s1) ; puts (s2) ; puts (s3) ; printf ("%p, %p\n " , s2, s3) ;

Здесь s2 и s3 указывают на один и тот же символ (printf() выводит одинаковые адреса). Однако то, что возвращает strcpy() , нельзя присвоить массиву. Результат работы этой функции обычно ничему не присваивают; бывает достаточно того, что она просто изменяет одну из переданных по указателю строк.

Другое дело, такие функции как strlen() или strcmp() , которые не изменяют параметры, а вызываются ради результата. Функция strcmp() сравнивает две строки-аргумента по буквам (лексикографически) и возвращает 0, -1 или 1. Например, вызов strcmp("boy", "body") вернет 1, т.к. код буквы "y" больше буквы "d". Вызов strcmp("body", "boy") вернет -1, т.к. первый аргумент лексикографически меньше второго.

Функция strtok()

С помощью функции strtok() можно разбить строку на отдельные части (лексемы). Объявление этой функции выглядит так char *strtok (char *, const char *) . При первом вызове функции в качестве первого параметра указывается строка, которую требуется разбить. Вторым параметром указывается строка-разделитель. При последующих вызовах функции для этой же строки первым параметром должен быть NULL, т.к. функция уже "запомнила" с чем работает. Рассмотрим пример:

char str = "one, two, three, four, five" ; char * sp; sp = strtok (str, ", " ) ; while (sp) { puts (sp) ; sp = strtok (NULL, ", " ) ; }

В результате выполнения данного кода на экран в столбик выводятся слова:

One two three four five

При первом вызове strtok() в функцию передается указатель на первый символ массива и строка-разделитель. После этого вызова массив str изменяется, в нем остается только слово "one", также функция возвращает указатель на это слово, который присваивается sp.

Хотя мы потеряли остаток массива в вызывающей функции, однако внутри strtok() сохраняется указатель на остаток массива. Когда передается NULL, функция "знает", что надо работать с этим "хвостом".

Копирование частей строк

Когда требуется просто соединить две строки, то проблема легко решается с помощью вызова функции strcat() , которая к концу первого аргумента присоединяет второй. Похожая функция strncat() присоединяет n символов второй строки к первой. n указывается в качестве третьего параметра.

Что если ситуация более сложная? Например, есть две непустые строки и надо соединить начало первой и конец второй. Сделать это можно с помощью функции strcpy() , если передавать ссылки не на первые символы строк:

char s1[ 20 ] = "Peter Smith" , s2 = "Julia Roberts" ; strcpy (s1+ 5 , s2+ 5 ) ; puts (s1) ;

В данном случае на экране будет выведено "Peter Roberts". Почему так произошло? В функцию strcpy() был передан указатель на шестой символ первой строки. Это привело к тому, что при копировании символы этой строки затираются только начиная с 6-го, т.к. strcpy() о предыдущих символах ничего не "знает". В качестве второго аргумента также передается только часть строки, которая и копируется в первую.

Как вставить одну строку в середину другой? Можно решить эту задачу, используя третью "буферную" строку, куда можно сначала скопировать первую строку, потом вторую, затерев конец первой, потом присоединить конец первой. Но можно поступить и так:

char s1[ 20 ] = "one three" , s2[ 20 ] = "two" ; strcpy (s2+ 3 , s1+ 3 ) ; strcpy (s1+ 4 , s2) ; puts (s1) ;

Здесь сначала во вторую строку копируется конец первой, получается "two three". Затем в первую строку, минуя ее начало, копируется вторая.

Описание некоторых функций для работы со строками

Задание
Ниже представлены описания некоторых функций, выполняющих операции над строками. Придумайте и напишите маленькие программы, иллюстрирующие работу этих функций.

  • char *strchr (const char *, int c) . Возвращает указатель на первое вхождение символа с в строку. Возвращает NULL, если такого символа в строке нет.
  • char *strstr (const char *s2, const char *s1) . Возвращает указатель на первое вхождение строки s1 в строку s2. Если совпадений нет, возвращает NULL.
  • char *strncpy (char *, const char *, size_t n) . Копирует n символов второй строки в первую.
  • size_t strspn (const char *, const char *) . Возвращает длину начала первой строки, в которую входят символы, из которых состоит вторая строка.

Если заметили ошибку, выделите фрагмент текста и нажмите Ctrl+Enter
ПОДЕЛИТЬСЯ: