Чтение файла построчно и использование strtok() в C

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

stage 1
num_nodes 2
nonce 234567

Мне нужно токенизировать каждое значение в строке отдельно, поэтому, например, в первой строке «этап» используется для проверки, прочитал ли я значение этапа из файла конфигурации, а затем сохранил его значение в переменной. Моя токенизация работает правильно. Однако, когда я пытаюсь манипулировать своими переменными после токенизации, это дает мне ошибку сегментации. В лучшем случае я могу просто успешно манипулировать одной из переменных, то есть либо stage, либо num_nodes, либо nonce, но не их комбинацию. Даже если попытаться сделать что-то вроде

stage = stage + 1;
num_nodes = num_nodes + 1;

однако это дает ошибку сегментации, если я просто изменю одну переменную, например:

num_nodes = num_nodes + 1;

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

main(int argc, char *argv[]){
  int nonce;
  int num_nodes;
  int stage; 
  char filename[256];   
  char *token1, *token2, *str;  
  FILE* fp;
  char bufr[MAXLINE];  

  printf("Please enter config file name\n");
  scanf("%s",filename);
  printf("You entered %s\n", filename);

  if((fp = fopen(filename, "r")) != NULL){

        while(fgets(bufr, MAXLINE, fp) != NULL){
            if(bufr[0] == '#') // to skip comments
                continue;

            printf("This is bufr: %s",  bufr);
            str = bufr;

              for(str;  ;str = NULL){
                token1 = strtok(str, " ");

                if(strcmp(token2, "num_nodes") == 0){
                    num_nodes = atoi(token1); 
                    printf("num_nodes = %d\n", num_nodes);
                }

                if(strcmp(token2, "nonce") == 0){
                    nonce = atoi(token1);
                    printf("nonce = %d\n", nonce);
                }       

                if(strcmp(token2, "stage") == 0){
                    stage = atoi(token1);
                    printf("stage = %d\n", stage);
                }                   

                token2 = token1; // making a copy of pointer

                if(str == NULL){
                    break;
                }
          }//end of for loop

        }//end of while loop
        fclose(fp); //close the file handle
    }
    else{
        printf("failed, file not found!\n");
    }

/*      This is where the segmentation fault kicks in, try to uncomment two lines and it will give a segmentation fault, if uncomment just one, then it works fine.
    nonce = nonce + 2;  
    num_nodes = num_nodes + 1;
    printf("stage = %d\n", stage);
*/
}

person Zahaib Akhtar    schedule 22.09.2013    source источник


Ответы (2)


Ваш код содержит:

token1 = strtok(str, " ");

if (strcmp(token2, "num_nodes") == 0){
    num_nodes = atoi(token1); 
    printf("num_nodes = %d\n", num_nodes);
}

Вы только что установили token1, но продолжаете сравнивать token2? Это, вероятно, приведет к дампу ядра, по крайней мере, в первый раз, когда token2 никогда не устанавливалось.

В конце, после цикла, единственная причина появления дампов ядра заключается в том, что вы вышли за пределы выделенной памяти. Не сразу понятно, почему, но структура цикла… любопытна, скажем так.

Вот очищенная версия вашего кода, не вызывающая сбоев. Ваш оригинал был не так уж плох, но неопределенный статус token2 вызывал беспокойство. Одна версия вывода включала такую ​​информацию, как:

Please enter config file name
You entered config.file
This is bufr: # Comment
This is bufr: 
This is bufr: stage 1
token1 = <<stage>>; token2 = <<>>
token1 = <<1
>>; token2 = <<stage>>
stage = 1
This is bufr: num_nodes 2
token1 = <<num_nodes>>; token2 = <<des>>
token1 = <<2
>>; token2 = <<num_nodes>>
num_nodes = 2
This is bufr: nonce 234567
token1 = <<nonce>>; token2 = <<67
>>
token1 = <<234567
>>; token2 = <<nonce>>
nonce = 234567
This is bufr: 
stage = 1

Обратите внимание на оставшиеся обломки в token2. Я очистил это дальше в коде ниже:

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

enum { MAXLINE = 4096 };

int main(void)
{
    int nonce = -1;
    int num_nodes = -1;
    int stage = -1;
    char filename[256];
    char *token1, *token2, *str;
    FILE *fp;
    char bufr[MAXLINE];

    printf("Please enter config file name\n");
    scanf("%s", filename);
    printf("You entered %s\n", filename);

    if ((fp = fopen(filename, "r")) == NULL)
    {
        printf("failed, file not found!\n");
        return(1);
    }

    while (fgets(bufr, MAXLINE, fp) != NULL)
    {
        printf("This is bufr: %s", bufr);
        if (bufr[0] == '#' || bufr[0] == '\n')
            continue;

        token2 = "";
        for (str = bufr; (token1 = strtok(str, " \n\t")) != 0; str = NULL)
        {
            printf("token1 = <<%s>>; token2 = <<%s>>\n", token1, token2);
            if (strcmp(token2, "num_nodes") == 0) {
                num_nodes = atoi(token1);
                printf("num_nodes = %d\n", num_nodes);
            }
            if (strcmp(token2, "nonce") == 0) {
                nonce = atoi(token1);
                printf("nonce = %d\n", nonce);
            }
            if (strcmp(token2, "stage") == 0) {
                stage = atoi(token1);
                printf("stage = %d\n", stage);
            }

            token2 = token1;

            if (str == NULL)    /* Terminate after name/value */
                break;
        }

    }
    fclose(fp);

    nonce = nonce + 2;
    num_nodes = num_nodes + 1;
    printf("stage = %d\n", stage);
    printf("nonce = %d\n", nonce);
    printf("nodes = %d\n", num_nodes);

    return(0);
}

Этот код корректно компилируется в Mac OS X 10.8.5 с GCC 4.8.1 с помощью командной строки:

gcc -O3 -g -std=c11 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -Wold-style-definition cfg.c -o cfg

Учитывая входной файл с именем config.file:

# Comment

stage 1
num_nodes 2
nonce 234567
 

(с пустой строкой в ​​конце), вывод был:

Please enter config file name
You entered config.file
This is bufr: # Comment
This is bufr: 
This is bufr: stage 1
token1 = <<stage>>; token2 = <<>>
token1 = <<1>>; token2 = <<stage>>
stage = 1
This is bufr: num_nodes 2
token1 = <<num_nodes>>; token2 = <<>>
token1 = <<2>>; token2 = <<num_nodes>>
num_nodes = 2
This is bufr: nonce 234567
token1 = <<nonce>>; token2 = <<>>
token1 = <<234567>>; token2 = <<nonce>>
nonce = 234567
This is bufr: 
stage = 1
nonce = 234569
nodes = 3
person Jonathan Leffler    schedule 22.09.2013
comment
Вы правы насчет структуры цикла, она не самая чистая, но после одной итерации цикла for я делаю копию token1, то есть token2 = token1, которая затем используется во второй итерации для проверки того, какой токен был прочитан в последней итерацию и соответствующим образом обновить переменную. - person Zahaib Akhtar; 22.09.2013
comment
Меня беспокоит первая итерация, когда token2 не установлено. Я изучаю код; достаточно сказать, что на данный момент версия, которая у меня есть, работает без особых проблем, но я устанавливаю token2 = "" перед тем, как куда-либо идти (доступ к неинициализированным переменным — это рецепт катастрофы). - person Jonathan Leffler; 22.09.2013
comment
В этом согласен с вами, вы попали в точку. - person Zahaib Akhtar; 22.09.2013
comment
Только что сделал token2= и ошибка сегментации исчезла. Интересно, что именно было причиной этого, у вас есть какие-либо мысли, кроме неинициализации? - person Zahaib Akhtar; 22.09.2013
comment
Вы делали strcmp() с квазислучайным (или, по крайней мере, неопределенным) расположением памяти. Если память была читабельной, она не должна была причинить большого вреда, но вы ступаете по тонкому льду. Когда я скомпилировал ваш исходный код с параметрами, показанными в моем ответе, я получил такие предупреждения, как: cfg.c:69:11: warning: ‘stage’ may be used uninitialized in this function [-Wmaybe-uninitialized] в printf("stage = %d\n", stage); и cfg.c:35:27: warning: ‘token2’ may be used uninitialized in this function [-Wmaybe-uninitialized] в if (strcmp(token2, "num_nodes") == 0) {. Я исправил их. - person Jonathan Leffler; 22.09.2013
comment
да, исходный вывод выглядит довольно грязно, к сожалению, здесь работает виртуальная машина, это просто требование, а не большой поклонник виртуальных машин. Спасибо за быстрые ответы. Это было здорово! - person Zahaib Akhtar; 22.09.2013

Чувак, ты можешь использовать libconfig для чтения файлов конфигурации. Это проще сделать с помощью libconfig, чем писать алгоритм для этого.

http://www.hyperrealm.com/libconfig/

Здесь вы можете увидеть пример:

http://simplestcodings.blogspot.com.br/2012/02/libconfig-to-read-configuration-files.html

person Vitor Villar    schedule 22.09.2013
comment
Для производственного решения библиотека libconfig, скорее всего, является лучшим решением. Тем не менее, упражнение, скорее всего, «можете ли вы использовать strtok()», а чтение файла конфигурации — это просто средство проверки этого обучения. - person Jonathan Leffler; 22.09.2013