Токенизация строк в C

Я пытался разметить строку, используя ПРОБЕЛ в качестве разделителя, но это не сработало. Есть ли у кого-нибудь предложения о том, почему это не работает?

Обновлено: токенизация с использованием:

strtok(string, " ");

Код выглядит следующим образом

pch = strtok (str," ");
while (pch != NULL)
{
  printf ("%s\n",pch);
  pch = strtok (NULL, " ");
}

ваш пример получит первый токен, посмотрите ответы gbjbaanb или мои для правильного использования.

Evan Teran 05.11.2008 23:02

В ПОРЯДКЕ. Теперь мы кое-что получим. Какого поведения вы ожидаете, чего не получаете?

dmckee --- ex-moderator kitten 05.11.2008 23:08

Ваш код правильный, сообщите нам, какова ваша строка ввода и результат.

Evan Teran 05.11.2008 23:08

Кстати, комбо. Многие люди, которые работают в справочных службах или преподают, видят фразу «это не работает» как обозначение пользователя, который не читал предоставленное руководство, или не знает, чего он на самом деле хочет, или глубоко сбит с толку. Вам нужна форма: «Я делаю X, и я ожидал Y, но у меня Z. Что не так?»

dmckee --- ex-moderator kitten 05.11.2008 23:17

@dmckee: хороший момент. Каноническая ссылка на x-ref: catb.org/~esr/faqs/smart-questions.html

Jonathan Leffler 06.11.2008 01:46

Вы используете strtok или что-то, что вырастили сами? cplusplus.com/reference/clibrary/cstring/strtok.html Если вы используете strtok, вы пытаетесь сделать это с постоянной строкой?

Edward KMETT 05.11.2008 22:48
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
25
6
138 794
8

Ответы 8

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

Делай это так:

char s[256];
strcpy(s, "one two three");
char* token = strtok(s, " ");
while (token) {
    printf("token: %s\n", token);
    token = strtok(NULL, " ");
}

Примечание. strtok модифицирует строку, в которой используется токенизация, поэтому это не может быть const char*.

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

char *p = strtok(str, " ");
while(p != NULL) {
    printf("%s\n", p);
    p = strtok(NULL, " ");
}

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

Как strtok() узнает, когда получить следующий токен из строки, которую он ранее токенизировал? Мне кажется, что strtok() должен токенизировать значение NULL. Я ожидал ошибки.

polemon 27.02.2012 13:25
strtok имеет внутреннюю переменную состояния, отслеживающую токенизируемую строку. Когда вы передадите ему NULL, strtok продолжит использовать эту переменную состояния. Когда вы передаете ненулевое значение, переменная состояния сбрасывается. Другими словами: передача NULL означает «продолжить токенизацию той же строки».
Evan Teran 27.02.2012 19:51

Спасибо за разъяснения. Но в любом случае это кажется ужасным дизайном.

polemon 28.02.2012 09:52

вы правы, поэтому многие реализации предлагают strtok_r, которые в наименьшей степени предлагают способ его использования в потокобезопасном режиме.

Evan Teran 28.02.2012 09:53

Разве char *p не следует сначала выделить немного места? На что указывает указатель, возвращаемый strtok? Просто случайное место в памяти? О ... указывает ли это на место в данной строке?

LazerSharks 18.07.2014 10:06

@Gnuey, p будет указывать на символы в токенизируемой строки. Кроме того, strtok заменяет найденный разделитель на символ '\0', так что p фактически будет действительной строкой с завершением NUL. Итак, если бы вы запускали его на char[] s = "hello world";, первый вызов вернул бы указатель на символ h, и тогда буфер содержал бы "hello\0world".

Evan Teran 21.10.2014 21:56

Как можно сохранить это значение «p» в массиве для сравнения ??????????? положение в положение

user3402040 31.10.2016 20:47

@delive, я не уверен, что вы на самом деле спрашиваете (и несколько "?" не улучшают читаемость). Я рекомендую написать новый вопрос, чтобы сообщество попыталось ответить.

Evan Teran 31.10.2016 21:28

Да, например, дата: это дата 19/09/2016, с разделением можно получить 19/09/2016, теперь я хочу поместить значение по значению в массив ints [] = {1,9,0,9,2,0 , 1,6} Я знаю, что [0] и [1] == день, вы меня понимаете? и вы можете сравнивать дни, месяцы и годы с массивом из 8 позиций

user3402040 31.10.2016 21:30

@delive, я думаю, что понимаю вас, но комментарии - это нет место для вопросов и ответов, поэтому, пожалуйста, создайте новый вопрос, и сообщество ответит как можно лучше.

Evan Teran 31.10.2016 22:40

Вы можете упростить код, добавив дополнительную переменную.

#include <string.h>
#include <stdio.h>

int main()
{
    char str[100], *s = str, *t = NULL;

    strcpy(str, "a space delimited string");
    while ((t = strtok(s, " ")) != NULL) {
        s = NULL;
        printf(":%s:\n", t);
    }
    return 0;
}

strtok может быть очень опасным. Это не потокобезопасный. Его предполагаемое использование должно вызываться снова и снова в цикле, передавая результат предыдущего вызова. Функция strtok имеет внутреннюю переменную, в которой хранится состояние вызова strtok. Это состояние не уникально для каждого потока - оно глобально. Если какой-либо другой код использует strtok в другом потоке, у вас возникнут проблемы. Не из тех проблем, которые вы хотите отследить!

Я бы рекомендовал поискать реализацию регулярного выражения или использовать sscanf для разделения строки.

Попробуй это:

char strprint[256];
char text[256];
strcpy(text, "My string to test");
while ( sscanf( text, "%s %s", strprint, text) > 0 ) {
   printf("token: %s\n", strprint);
}

Примечание. Текстовая строка уничтожается по мере разделения. Это не может быть предпочтительным поведением =)

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

Will Dean 05.11.2008 23:37

strtok_r - это поточно-ориентированная версия strtok pubs.opengroup.org/onlinepubs/009695399/functions/strtok.htm‌ l

Massimo Fazzolari 05.09.2012 22:47

Я согласен с первым абзацем, но предложение после него ужасное. scanf трудно использовать должным образом, как показано в вашем примере; вы забыли передать размер (%255s).

S.S. Anne 15.02.2020 21:01

Я сделал несколько строковых функций для разделения значений, используя как можно меньше указателей, потому что этот код предназначен для работы на процессорах PIC18F. Эти процессоры не очень хорошо справляются с указателями, когда у вас мало свободной оперативной памяти:

#include <stdio.h>
#include <string.h>

char POSTREQ[255] = "pwd=123456&apply=Apply&d1=88&d2=100&pwr=1&mpx=Internal&stmo=Stereo&proc=Processor&cmp=Compressor&ip1=192&ip2=168&ip3=10&ip4=131&gw1=192&gw2=168&gw3=10&gw4=192&pt=80&lic=&A=A";

int findchar(char *string, int Start, char C) {
    while((string[Start] != 0)) { Start++; if (string[Start] == C) return Start; }
    return -1;
}

int findcharn(char *string, int Times, char C) {
   int i = 0, pos = 0, fnd = 0;

    while(i < Times) {
       fnd = findchar(string, pos, C);
        if (fnd < 0) return -1;
        if (fnd > 0) pos = fnd;
       i++;
   }
   return fnd;
}

void mid(char *in, char *out, int start, int end) {
    int i = 0;
    int size = end - start;

    for(i = 0; i < size; i++){
        out[i] = in[start + i + 1];
    }
    out[size] = 0;
}

void getvalue(char *out, int index) {
    mid(POSTREQ, out, findcharn(POSTREQ, index, '='), (findcharn(POSTREQ, index, '&') - 1));
}

void main() {
   char n_pwd[7];
   char n_d1[7];

   getvalue(n_d1, 1);

   printf("Value: %s\n", n_d1);
} 
int not_in_delimiter(char c, char *delim){

    while(*delim != '\0'){
            if (c == *delim) return 0;
            delim++;
    }
    return 1;
}

char *token_separater(char *source, char *delimiter, char **last){

char *begin, *next_token;
char *sbegin;

/*Get the start of the token */
if (source)
  begin = source;
else
  begin = *last;

sbegin = begin;

/*Scan through the string till we find character in delimiter. */
while(*begin != '\0' && not_in_delimiter(*begin, delimiter)){
       begin++;
}

/* Check if we have reached at of the string */
if (*begin == '\0') {
/* We dont need to come further, hence return NULL*/
   *last = NULL;
    return sbegin;
}
/* Scan the string till we find a character which is not in delimiter */
 next_token  = begin;
 while(next_token != '\0' && !not_in_delimiter(*next_token, delimiter))    {
    next_token++;
 }
 /* If we have not reached at the end of the string */
 if (*next_token != '\0'){
  *last = next_token--;
  *next_token = '\0';
   return sbegin;
}
}

 void main(){

    char string[10] = "abcb_dccc";
    char delim[10] = "_";
    char *token = NULL;
    char *last = "" ;
    token  = token_separater(string, delim, &last);
    printf("%s\n", token);
    while(last){
            token  = token_separater(NULL, delim, &last);
            printf("%s\n", token);
    }

}

Подробный анализ вы можете прочитать в блоге, упомянутом в моем профиле :)

Хорошо, @jitsceait, но что произойдет, если у меня на входе будет два разделителя? Немного изменю твой код.

Nilo Paim 19.11.2013 17:13

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

jitsceait 12.01.2014 17:57

Вот еще одна реализация strtok(), которая может распознавать последовательные разделители (в стандартной библиотеке strtok() этого нет)

Функция является частью лицензированной строковой библиотеки BSD и называется zString. Приглашаем вас внести свой вклад :)

https://github.com/fnoyanisi/zString

char *zstring_strtok(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;
}

Как упоминалось в предыдущих сообщениях, поскольку strtok() или тот, который я использовал выше, полагается на переменную static *char для сохранения положения последнего разделителя между последовательными вызовами, следует проявлять особую осторожность при работе с многопоточными приложениями.

Другие вопросы по теме