как эффективно использовать функцию strtok

Да, я тоже новичок. А у меня эта проблема уже давно. Я пытаюсь использовать strtok для разделения строки, но дело в том, что это не работает. Я просмотрел пример на справочных страницах, а также в Интернете, и у меня до сих пор нет ответа.

В приведенном ниже коде я попытался использовать пример кода, приведенный в качестве ответа на этом сайте. Оригинальный цикл while:

    char str[] = "hello world how are you?\n";
    char *res;

    res = strtok(str, " \n");
    puts(res);
    while (res != NULL)
    {
        res = strtok(NULL, " \n");
        if(res!=NULL)
            puts(res);
    }

но при изменении str на данные и соответствующие разделители (&=) это становится ошибкой сегментации. Как я могу это исправить? Что не так в коде? Вот полный код.

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

int main()
{
    char *data;

    data = "integer1=1&integer2=2&integer3=3&integer4=4";
    puts(data);

    char str[] = "hello world how are you?\n";
    char *res;

    res = strtok(data, "=&");
    puts(res);
    while (res != NULL)
    {
        res = strtok(NULL, "=&");
        if(res!=NULL)
            puts(res);
    }

    return 0;
}

кстати, функция strtok_r тоже не работает.


person Rex    schedule 05.07.2013    source источник
comment
Кроме того, вставьте пробел между директивой #include и именем файла, например: #include <string.h>, а не #include<string.h>.   -  person    schedule 05.07.2013


Ответы (3)


Этот:

char str[] = "hello world how are you?\n";

создает массив и инициализирует его содержимым строкового литерала. Однако это:

char *data;

data = "integer1=1&integer2=2&integer3=3&integer4=4";

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


Примечания:

  1. Вот почему вы объявляете указатели на строковые литералы как const char *, а явно не как char *, и если вы это сделаете, я найду вас и уточню const.

  2. Массивы не являются указателями, никогда ими не были и никогда не будут.

person Community    schedule 05.07.2013
comment
@Ryaminal Ты имеешь в виду, что я найду тебя и const оценю? :П - person ; 05.07.2013
comment
Да, тот. Будут кошмары о компиляторах, атакующих мир. Гораздо страшнее зомби. - person Ryaminal; 05.07.2013
comment
@Ryaminal Обратите внимание: если вы const, вы не боитесь никаких компиляторов, они не могут причинить вам вреда. - person ; 05.07.2013
comment
Но если вы уточните Rex как const, он не сможет изменить свои вредные привычки (например, попытки изменить строковые литералы). - person Jonathan Leffler; 05.07.2013

Поведение, которое вы наблюдаете, можно объяснить с помощью вопроса 1.32 в часто задаваемых вопросах com.lang.c. :

В чем разница между этими инициализациями?

char a[] = "string literal";
char *p  = "string literal";

Моя программа вылетает, если я пытаюсь присвоить новое значение p[i].

И ответ таков:

Строковый литерал (формальный термин для строки в двойных кавычках в исходном коде C) можно использовать двумя немного разными способами:

  1. Как инициализатор для массива char, как и в объявлении char a[] , он указывает начальные значения символов в этом массиве (и, при необходимости, его размер).
  2. В любом другом месте он превращается в безымянный статический массив символов, и этот безымянный массив может храниться в постоянной памяти и, следовательно, не обязательно может быть изменен. В контексте выражения массив сразу преобразуется в указатель, как обычно (см. раздел 6), поэтому второе объявление инициализирует p так, чтобы он указывал на первый элемент безымянного массива.
person devnull    schedule 05.07.2013

strtok сломать блок памяти. И буквальные строки не могут модифицироваться. Таким образом, вы не можете использовать strtoke для обоих. Попробуй это:

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

int main(){
    char *data;

    data = "integer1=1&integer2=2&integer3=3&integer4=4";
    char *cur, *res;

    cur = data;
    res = strpbrk(cur, "=&");
    while (res != NULL)
    {
        fwrite(cur, 1, res-cur, stdout);
        fputc('\n', stdout);

        cur = res + 1;
        res = strpbrk(cur, "=&");
    }
    fputs(cur, stdout);

    return 0;
}

Это не изменяет блок памяти.

person mattn    schedule 05.07.2013