【问题标题】:Strtok on text file leads to seg fault文本文件上的 Strtok 导致 seg 错误
【发布时间】:2015-03-07 02:51:51
【问题描述】:

我有一个文本文件,内容如下:

5f6
2f6
4f6

我想从该文本文件中获取数字(作为字符,然后使用 atol() 将它们转换为整数

我有代码:

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

int main(int argc, char * argv[])
{
    initscr();
    cbreak();
    noecho();

    char buf[100];
    char * ptr;
    char * ptr2;
    char * ptr3;
    char * ptr4;
    int a;
    int b;
    int c; 
    int d;

    FILE* file;
    if (strcmp(argv[1],"test.txt") == 0)
    {
        file = fopen("test.txt","r");

        while (fgets(buf,100,file) )

        ptr = strtok(str,"f");
        ptr2 = strtok(NULL," ");
        ptr3 = strtok(NULL,"f");
        ptr4 = strtok(NULL," ");
        a = atol(ptr);
        b = atol(ptr2);
        c = atol(ptr3);
        d = atol(ptr4);
    }

    refresh();
    getchar(); 
    endwin();
    return (0);
}

然而,程序段错误但编译。我将如何更有效地执行此操作(通过不分段错误的方法)?

【问题讨论】:

  • 那是准确的代码吗?请复制并粘贴。
  • 正如你所说,它编译后编译的代码/函数的完整版本
  • 如果您不想发布完整的程序,请创建Minimal, Complete, and Verifiable Example 并展示给我们。
  • 另外请注意,如果输入中的所有行都遵循示例所示的确切格式,那么您不需要strtokstrtol,只需读取一行,然后获取第一个和第三个字符来获取数字。不要把事情复杂化!
  • 您认为while 循环中会发生什么?它只在循环中执行对strtokfirst 调用。哦,那个代码不会编译!请复制粘贴代码,不要重写。

标签: c file pointers segmentation-fault


【解决方案1】:

你有几个问题

  1. 你不检查文件是否打开。

    • fopen()之后一定要保证可以从文件中读取,如果fopen()失败,就返回NULL这么简单

      if (file == NULL)
          return -1;
      

    将防止程序的其余部分出现问题。

  2. 您的 while 循环仅包含一条语句,因为它缺少大括号。

    • 没有大括号,你的 while 循环相当于

      while (fgets(buf, sizeof(buf), file))
       {
          ptr = strtok(buf, "f");
       }
      
  3. 您没有检查 strtok() 是否返回了非 NULL 值。

    • 如果在字符串中找不到标记,strtok() 返回NULL,并且您将返回的指针传递给atol

      这会导致未定义的行为,并且可能会导致分段错误。

  4. 您不检查是否有参数传递给程序,但您仍然尝试将其与字符串进行比较。还有潜在的未定义行为。

我不知道下面的程序是否能满足你的需要,因为它是你自己的程序,我只是让它更安全,避免了未定义的行为

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

int main(int argc, char * argv[])
{
    char buf[100];
    char *ptr;
    char *ptr2;
    char *ptr3;
    char *ptr4;
    int   a;
    int   b;
    int   c;
    int   d;
    FILE *file;

    initscr();
    cbreak();
    noecho();

    if (argc > 1)
     {
        file = fopen(argv[1], "r");
        if (file == NULL)
            return -1;
        while (fgets(buf,100,file) != NULL)
         {
            ptr  = strtok(str,"f");
            ptr2 = strtok(NULL," ");
            ptr3 = strtok(NULL,"f");
            ptr4 = strtok(NULL," ");
            if (ptr != NULL)
                a = atoi(ptr);
            if (ptr2 != NULL)
                b = atoi(ptr2);
            if (ptr3 != NULL)
                c = atoi(ptr3);
            if (ptr4 != NULL)
                d = atoi(ptr4);
         }
     }
    refresh();
    getchar();
    endwin();

    return 0;
}

【讨论】:

  • 这行得通,但由于某种原因 c = a 和 d = b,值似乎没有改变。
  • 如果你想打印它们,你应该初始化它们,因为如果不改变它意味着ptr* 变量在NULL 和因此c = atoi(...) 没有达到。您需要检查文件中的数据,如果ptr == NULL 打印buf 的值以查看原因。
  • 好的,我已经解决了这个问题,但是现在发生了一个更奇怪的问题。我的程序完全跳过了文本文件“5f6”的第一行,所以最终不会读入 5 和 6,而是读入包含 2 和 6 的第二行。
  • 好的,我刚刚注意到,如果我将文本文件更改为“5f6 2f6”,即将 2f6 移动到同一行,代码将按照它的姿势运行。但我当然不希望它从这个意义上阅读,而是逐行阅读。
  • 你需要删除strtok(NULL, " ");,为什么它仍然存在?
【解决方案2】:

例如,如果您想从 e.g. 中获取数字

5f6

作为数字,这比您想象的要容易得多。只需读取第一个和第三个字符,然后使用简单的算术将它们转换为数字。

例子:

char line[8];
while (fgets(line, sizeof line, file) != NULL)
{
    int firstDigit = line[0] - '0';
    int secondDigit = line[2] - '0';

    printf("%d %d\n", firstDigit, secondDigit);
}

对于您显示的示例输入

5f6 2f6 4f6

输出将是

5 6 2 6 4 6

如果您对算法感到疑惑,它可以工作,因为大多数字符编码(如最常见的ASCII)将数字字符保持在连续范围内。在 ASCII 的示例中,例如的值'5'53'0' 的值是 48。这意味着减去'5' - '0' 得到53 - 48 等于5

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-02-16
    • 1970-01-01
    • 2012-07-02
    • 1970-01-01
    • 1970-01-01
    • 2020-09-28
    • 2013-07-04
    相关资源
    最近更新 更多