【问题标题】:Reading the file with comma delimiter using fgets() and strtok()使用 fgets() 和 strtok() 读取带有逗号分隔符的文件
【发布时间】:2010-12-02 13:44:47
【问题描述】:

我有一个文本文件,其中包含用逗号分隔的三个字段。 我的文本文件内容示例:12345,真正的编程新手,BS ME 要将文件加载到程序中,我使用了以下代码....我的问题是有时代码有效,有时无效(没有出现错误消息,程序只是自行关闭并且无法继续)。我还观察到文本文件是空白的(没有写入任何内容)它会自动关闭并且不会继续。您的帮助将不胜感激。谢谢!

int read(){
     FILE *stream = NULL;
     int ctr;
     char linebuffer[45];
     char delims[]=", ";
     char *number[3];
     char *token = NULL;
     stream = fopen("student.txt", "rt");
     if (stream == NULL) stream = fopen("student.txt", "wt");
     else {
          printf("\nReading the student list directory. Wait a moment please...");
          while(!feof(stream)){            
                ctr=0;
                fgets(linebuffer, 46, stream);
                token = strtok(linebuffer, delims);
                while(token != NULL){
                  number[ctr] = linebuffer;
                  token = strtok(NULL, delims); 
                  ctr++;
                }
          recordCtr++;                      
          }                     
     recordCtr--;
     }
     fclose(stream);
}    

【问题讨论】:

  • 为什么在你的问题中使用这么多点?看起来很可怕。

标签: c fgets strtok


【解决方案1】:

一旦找到token,就永远不会复制它。您不能复制linebuffer,因为其中的数据将在下一行加载时被覆盖。

这一行:

number[ctr] = linebuffer;

应该引用token 来保存最近找到的令牌,但它没有。它可能应该读成类似1:

strcpy(number[ctr], token);

但是您必须更改声明以确保有空间:

char number[3][32];

显然,这会带来缓冲区溢出风险,如果有一个很长的令牌,它就不适合了。如何最好地处理剩下的作为练习。 :)

1 临时向量用来存放两个数字和一个字符串(名字)的时候,为什么叫“数字”呢?

【讨论】:

  • @unwind 抱歉。我不明白。你能给我更多的解释吗?谢谢
  • 他的意思是你得到了“strtok()”返回的“token”,然后把它扔掉了。相反,您将“linebuffer”分配给该字段。
  • @unwind 不错的答案,但我想我发现的 bug 和潜在 bug 比你多;)我太有竞争力了吗?
  • @alastairG 和@unwind。我认为你们俩都是非常优秀的程序员。我希望我将来能像你一样......所以我也可以在这个网站上回答问题。 :)
  • @Alastair:不,我想你更彻底。不过,我不能不接受。 :|
【解决方案2】:

您的fgets() 调用需要指定 45 作为大小,否则当 fgets 写入 NULL 终止符时,您会溢出缓冲区。这会将“delims”字符串设置为空字符串。

此外,即使函数声明声明它返回一个 int,您也没有返回任何值。

我不知道你的“struct student”的定义是什么,但是你在使用strcpy() 时可能会溢出缓冲区。你也减少“recordCtr”。为什么?如果无法打开文件进行写入,为什么要打开文件进行写入?为什么?如果这也失败了,您可以在 NULL 指针上调用 fclose。我怀疑这有多大帮助。

我刚刚注意到您没有初始化“数字”。如果您在第一行没有得到三个数字,那么您将使用未初始化的指针strcpy()。它可能有一个 NULL 值,所以程序会出现段错误。

你还有一个大小为 3 的数组,但如果你读取的行有超过 3 个逗号分隔的字段,你将溢出数组。

可能还有许多其他错误。

很多程序员都懒得去做所有好的编码实践,比如检查返回值、初始化变量等等。他们经常会得到这样的代码。如果你想成为一个真正优秀的程序员,试着养成做所有这些事情的习惯,或者至少总是考虑你是否需要这样做。

这段代码中有很多潜在的错误。如果一行超过 45 个字符会怎样?您不会删除换行符。您不会将字符串转换为数字(尽管 number[1] 似乎是字符串数据,那么为什么将其存储在名为“数字”的数组中?)或检查 fgets 是否实际返回任何数据或检查您有多少数据得到。

【讨论】:

  • 我将其设为 46 长,因为我只允许 5 个字符(用于 ID)、30 个字符(用于学生姓名)和 5 个字符(用于课程)。然后创建一个额外的 5。我的逻辑错了吗?谢谢
  • 我的函数声明是 int,因为当我将其更改为 void.. 时出现编译器错误
  • 可能是因为read() 是一个系统函数。尝试将其重命名为不同的名称。在工作中,我们有一个编码标准,所有公共函数必须以它们所属的模块/组件的名称开头。例如。你可以称之为 StudentDB_Read()。
  • 对于读取的长度,应该和缓冲区大小一样。 fgets() 的手册页指出该函数读取的字符比大小少一个。少一点是为 NULL 终止符留出空间。其他功能不太有用,您需要输入比缓冲区大小小一的大小值。通过为 45 个字符大的缓冲区指定 46,fgets 将从该行中读取最多 45 个字符的文本,然后添加一个空终止符,它将覆盖数组后面的内存。
  • 该内存与“delims”一致,因此使其成为有效的零长度字符串。我相信这意味着 strtok() 将返回每个单独的字符串作为令牌,但行为是未定义的。我刚刚阅读了 strtok() 的手册页,并意识到 strtok() 会在您的程序中将字符串拆分为逗号和空格,因此字符串“1, John Smith, Computer Science”将被解析为“1”, “John”、“Smith”、“Computer”、“Science”总共 5 个标记。这将溢出您的“数字”数组。
【解决方案3】:

让买家小心。

strtok 可能需要担心一些边缘情况。

“一、二、三”将产生 3 个令牌。

"one,,three" 将产生 2 个令牌。

【讨论】:

  • 这就是它所说的意思。你明白什么是“令牌”吗?如果没有,你不应该使用strtok;你认为“strtok”中的“tok”是什么意思?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-01-22
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多