Как strtok () разбивает строку на токены в C? - PullRequest
95 голосов
/ 08 октября 2010

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

Я добавил часы на str и *pch, чтобы проверить их работу, когда произошел первый цикл while, содержимое str было только"этот".Как выходные данные, показанные ниже, напечатаны на экране?

/* strtok example */
#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;
}

Вывод:

Splitting string "- This, a sample string." into tokens:
This
a
sample
string

Ответы [ 13 ]

190 голосов
/ 08 октября 2010

функция времени выполнения strtok работает следующим образом

при первом вызове strtok вы предоставляете строку, которую хотите токенизировать

char s[] = "this is a string";

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

char* p = strtok(s, " ");

, что происходит сейчас, когда выполняется поиск 's' до тех пор, пока не будет найден пробел, возвращается первый токен ('this') и p указывает на этот токен(строка)

для получения следующего токена и продолжения с той же строкой в ​​качестве первого аргумента передается NULL, поскольку strtok поддерживает статический указатель на вашу предыдущую переданную строку:

p = strtok(NULL," ");

p теперь указывает на 'is'

и так далее до тех пор, пока не будет найдено больше пробелов, тогда последняя строка будет возвращена как последняя строка 'токена'.

moreдля удобства вы можете написать это вместо этого, чтобы распечатать все токены:

for (char *p = strtok(s," "); p != NULL; p = strtok(NULL, " "))
{
  puts(p);
}

РЕДАКТИРОВАТЬ:

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

36 голосов
/ 08 октября 2010

strtok() делит строку на токены.то есть, начиная с любого из разделителей, следующий будет вашим единственным токеном.В вашем случае начальный токен будет от "-" и заканчивается следующим пробелом "".Тогда следующий токен начнется с "" и закончится ",".Здесь вы получите «Это» в качестве вывода.Точно так же остальная часть строки разбивается на токены из космоса в космос и, наконец, завершается последний токен на "."

22 голосов
/ 17 мая 2012

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

Это причина, по которой strtok не возвращается; как только вы передадите ему новый указатель, эта старая внутренняя ссылка будет засорена.

10 голосов
/ 17 мая 2012

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

со страницы POSIX strtok:

Эта функция использует статическое хранилище для отслеживания текущей позиции строки между вызовами.

Существует потокобезопасный вариант (strtok_r), который не использует этот тип магии.

7 голосов
/ 08 октября 2010

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

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

5 голосов
/ 08 октября 2010

strtok будет токенизировать строку, т.е. преобразовывать ее в серию подстрок.

Это делается путем поиска разделителей, которые разделяют эти токены (или подстроки).И вы указываете разделители.В вашем случае вы хотите «или», «или».или '-', чтобы быть разделителем.

Модель программирования для извлечения этих токенов состоит в том, что вы вручную передаете свою основную строку и набор разделителей.Затем вы вызываете его несколько раз, и каждый раз strtok возвращает следующий найденный токен.Пока он не достигнет конца основной строки, когда он возвращает ноль.Другое правило заключается в том, что вы передаете строку только в первый раз, а NULL - в последующие.Это способ сообщить strtok, начинаете ли вы новый сеанс токенизации с новой строкой или извлекаете токены из предыдущего сеанса токенизации.Обратите внимание, что strtok запоминает свое состояние для сеанса токенизации.И по этой причине он не является реентерабельным или поточно-ориентированным (вместо него следует использовать strtok_r).Еще одна вещь, которую нужно знать, это то, что она на самом деле изменяет исходную строку.Он пишет «\ 0» для разделителей, которые находит.

Один из способов вызвать strtok, кратко, таков:

char str[] = "this, is the string - I want to parse";
char delim[] = " ,-";
char* token;

for (token = strtok(str, delim); token; token = strtok(NULL, delim))
{
    printf("token=%s\n", token);
}

Результат:

this
is
the
string
I
want
to
parse
4 голосов
/ 08 октября 2010

strtok изменяет свою входную строку.Он помещает в него нулевые символы ('\ 0'), чтобы он возвращал биты исходной строки в качестве токенов.На самом деле strtok не выделяет память.Вы можете понять это лучше, если вы рисуете строку в виде последовательности блоков.

3 голосов
/ 18 февраля 2016

Чтобы понять, как работает strtok(), нужно сначала узнать, что такое статическая переменная . Эта ссылка объясняет это довольно хорошо ...

Ключом к операции strtok() является сохранение местоположения последнего разделителя между скрытыми вызовами (поэтому strtok() продолжаетанализировать очень оригинальную строку, которая передается ей, когда она вызывается с null pointer в последовательных вызовах) ..

Посмотрите на мою собственную реализацию strtok(), называемую zStrtok(), которая имеетнемного отличная функциональность, чем та, которую предоставляет strtok()

char *zStrtok(char *str, const char *delim) {
    static char *static_str=0;      /* var to store last address */
    int index=0, strlength=0;           /* integers for indexes */
    int found = 0;                  /* check if delim is found */

    /* delimiter cannot be NULL
    * if no more char left, return NULL as well
    */
    if (delim==0 || (str == 0 && static_str == 0))
        return 0;

    if (str == 0)
        str = static_str;

    /* get length of string */
    while(str[strlength])
        strlength++;

    /* find the first occurance of delim */
    for (index=0;index<strlength;index++)
        if (str[index]==delim[0]) {
            found=1;
            break;
        }

    /* if delim is not contained in str, return str */
    if (!found) {
        static_str = 0;
        return str;
    }

    /* check for consecutive delimiters
    *if first char is delim, return delim
    */
    if (str[0]==delim[0]) {
        static_str = (str + 1);
        return (char *)delim;
    }

    /* terminate the string
    * this assignmetn requires char[], so str has to
    * be char[] rather than *char
    */
    str[index] = '\0';

    /* save the rest of the string */
    if ((str + index + 1)!=0)
        static_str = (str + index + 1);
    else
        static_str = 0;

        return str;
}

А вот пример использования

  Example Usage
      char str[] = "A,B,,,C";
      printf("1 %s\n",zStrtok(s,","));
      printf("2 %s\n",zStrtok(NULL,","));
      printf("3 %s\n",zStrtok(NULL,","));
      printf("4 %s\n",zStrtok(NULL,","));
      printf("5 %s\n",zStrtok(NULL,","));
      printf("6 %s\n",zStrtok(NULL,","));

  Example Output
      1 A
      2 B
      3 ,
      4 ,
      5 C
      6 (null)

Код взят из библиотеки обработки строк, которую я поддерживаю на Github, называется zString.Посмотрите код или даже внесите свой вклад :) https://github.com/fnoyanisi/zString

1 голос
/ 27 января 2018

strtok () сохраняет указатель в статической переменной, где вы в последний раз остановились, поэтому при втором вызове, когда мы передаем нуль, strtok () получает указатель из статической переменной.

Если вы задаете то же имя строки, оно снова начинается с начала.

Кроме того, strtok () разрушителен, т. Е. Вносит изменения в оригинальную строку. поэтому убедитесь, что у вас всегда есть оригинальная копия.

Еще одна проблема использования strtok () состоит в том, что, поскольку он хранит адрес в статических переменных, в многопоточном программировании вызов strtok () более одного раза вызовет ошибку. Для этого используйте strtok_r ().

1 голос
/ 08 мая 2017

Вот как я реализовал strtok, Не так уж и здорово, но после 2 часов работы он наконец заработал.Он поддерживает несколько разделителей.

#include "stdafx.h"
#include <iostream>
using namespace std;

char* mystrtok(char str[],char filter[]) 
{
    if(filter == NULL) {
        return str;
    }
    static char *ptr = str;
    static int flag = 0;
    if(flag == 1) {
        return NULL;
    }
    char* ptrReturn = ptr;
    for(int j = 0; ptr != '\0'; j++) {
        for(int i=0 ; filter[i] != '\0' ; i++) {
            if(ptr[j] == '\0') {
                flag = 1;
                return ptrReturn;
            }
            if( ptr[j] == filter[i]) {
                ptr[j] = '\0';
                ptr+=j+1;
                return ptrReturn;
            }
        }
    }
    return NULL;
}

int _tmain(int argc, _TCHAR* argv[])
{
    char str[200] = "This,is my,string.test";
    char *ppt = mystrtok(str,", .");
    while(ppt != NULL ) {
        cout<< ppt << endl;
        ppt = mystrtok(NULL,", ."); 
    }
    return 0;
}
...