Попытка разделить на два разделителя, и это не работает - C - PullRequest
3 голосов
/ 13 ноября 2009

Я написал ниже код для чтения строки за строкой из стандартного ввода

city=Boston;city=New York;city=Chicago\n

, а затем разбить каждую строку на ';' разделитель и распечатать каждую запись. Затем в еще одном цикле я пытаюсь разделить запись разделителем '=', чтобы получить фактические значения. Но по какой-то причине основной (первый) цикл не выходит за пределы первой итерации, почему?

char*   del1 = ";";
char*   del2 = "=";
char    input[BUFLEN];

while(fgets(input, BUFLEN, fp)) {

        input[strlen(input)-1]='\0';
        char* record = strtok(input, &del1);

        while(record) {
                printf("Record: %s\n",record);

                char*  field = strtok(record, &del2);
                while(field) {
                     printf("Field: %s\n",field);
                     field = strtok(NULL, &del2);
                }

                record = strtok(NULL, &del1);
        }
}

Ответы [ 4 ]

3 голосов
/ 13 ноября 2009

Две вещи: во-первых, эта строка не очень хороша:

 input[strlen(input)-1]='\0';

fgets () всегда заканчивается на «\ 0», и это даст странные результаты, если ваш ввод не завершится точно на «\ n».

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

2 голосов
/ 13 ноября 2009

Похоже, вам нужно использовать возвращающуюся форму strtok, strtok_r, потому что когда вы вызываете strtok внутри вашего цикла, он стирает строку для внешнего цикла. Когда вы вызываете record = strtok (NULL), он пытается снова проанализировать вашу внутреннюю строку.

1 голос
/ 13 ноября 2009

Вот что происходит:

  • пользовательский ввод: a=1;b=2;c=3\n
  • после fgets : a=1;b=2;c=3\0
  • 1-й вызов strtok (не NULL, ...) : a=1\0b=2;c=3\0
  • 2-й вызов strtok (не NULL, ...) : a\01\0b=2;c=3\0

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

Когда вы снова вызываете strtok с аргументом, отличным от NULL, это единственное место, где хранится состояние, перезаписывается тем, что strtok воспринимает как новую строку из 3 символов и NULL (a=1\0), поскольку strtok больше не может запомнить исходные входные характеристики. Таким образом, record устанавливается в NULL в конце цикла на первой итерации, потому что strtok считает, что он находится в конце своей (намного короче, чем предполагалось!) Входной строки.

Выезд strtok_r.

1 голос
/ 13 ноября 2009

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

Вам потребуется выполнить цикл для разделения ;, сохранить эти результаты и один цикл, чтобы отделить = от ранее сохраненного результата.

char*   del1 = ";";
char*   del2 = "=";
char    input[BUFLEN];
char*   tokens[255]; // have to be careful not to go more then that
while(fgets(input, BUFLEN, fp)) {

    // input[strlen(input)-1]='\0'; // you don't need that fgets add the NULL
    char* record = strtok(input, del1);
    i = 0;
    while(record) {

            tokens[i++] = strdup(record);  
            record = strtok(NULL, del1);
    }
    for(v = 0; v < i; v++){
      char*  field = strtok(token[v], del2);
      while(field) {
          printf("Record: %s\n",token[v]);
          printf("Field: %s\n",field);
          field = strtok(NULL, del2);
      }
    }
}

обратите внимание, что вам нужно free все строки strdup после, иначе вы создадите утечку памяти.

также, пожалуйста, не подпись strtok является

char * strtok ( char * str, const char * delimiters );

так что вам не нужен &del, так как del уже является char *.

...