【问题标题】:Reading file line by line and using strtok() in C逐行读取文件并在 C 中使用 strtok()
【发布时间】:2013-09-22 01:35:06
【问题描述】:

我正在尝试逐行读取配置文件,然后将结果标记化并将结果存储到单独的变量中。我的配置文件如下所示

stage 1
num_nodes 2
nonce 234567

我需要分别标记该行中的每个值,因此例如在第一行“stage”用于检查我是否已从配置文件中读取阶段值,然后将其值保存在变量中。我的标记化似乎工作正常。但是,当我在标记化后尝试操纵我的变量时,它会给我一个分段错误。最多我只能成功地操纵其中一个变量,即 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);
*/
}

【问题讨论】:

    标签: c strtok


    【解决方案1】:

    您的代码包含:

    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
    

    【讨论】:

    • 你对循环结构是正确的,它不是最干净的,但是在 for 循环的一次迭代之后,我复制了 token1,即 token2 = token1,然后在第二次迭代中使用它来检查这是在最后一次迭代中读取的令牌并相应地更新变量。
    • token2 未设置时,这是第一次让我担心的迭代。我正在探索代码;可以说,目前,我的版本运行起来没有太多麻烦——但我在去任何地方之前设置了token2 = ""(访问未初始化的变量是灾难的根源)。
    • 同意你的观点,你明白了。
    • 刚刚做了 token2="" 并且分段错误消失了。想知道究竟是什么原因造成的,除了未初始化之外,您还有其他想法吗?
    • 您正在使用准随机(或至少是不确定的)内存位置执行strcmp()。如果内存是可读的,它应该不会造成太大的伤害,但你正在如履薄冰。当我使用答案中显示的选项编译您的原始代码时,我收到警告,例如:printf("stage = %d\n", stage);cfg.c:69:11: warning: ‘stage’ may be used uninitialized in this function [-Wmaybe-uninitialized]if (strcmp(token2, "num_nodes") == 0) {cfg.c:35:27: warning: ‘token2’ may be used uninitialized in this function [-Wmaybe-uninitialized]。我修好了这些。
    【解决方案2】:

    老兄,你可以使用 libconfig 来读取配置文件。使用 libconfig 比编写算法更容易做到这一点。

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

    这里你可以看到一个例子:

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

    【讨论】:

    • 对于生产解决方案,libconfig 库很可能是更好的解决方案。然而,这个练习更有可能是“你可以使用strtok()”,而阅读配置文件只是测试这种学习的一种方式。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-25
    • 2011-12-13
    • 1970-01-01
    相关资源
    最近更新 更多