【问题标题】:fgets() gets more than it should in file reading - Cfgets() 在文件读取中得到的比它应该得到的多 - C
【发布时间】:2020-09-18 07:33:58
【问题描述】:

目前我正在做一个实际的工作来拼贴我必须从文件中读取数据。

文件数据结构为:“id name sex”

示例:

nm0025630 维卡斯·阿南德 M nm0418131 维克多·詹森 M nm0411451 迪克以色列 M nm0757820 莱奥波尔多·萨尔塞多 M

要阅读当前我正在使用的代码:

    fh = NULL;
    fh = fopen(ACTORS, "r");
    if (!fh) {
        exit(1);
    }
    while (!feof(fh)) {
        char sex, name[100], id[10];

        fgets(id, 10, fh);
        fgets(name, 100, fh);
        fgetc(sex);

        if (!feof(fh)) {
            hash_update_node(hash, get_id_num(id), name, sex);
            count++;
        }
    }

问题在于它会同时读取名称和性别。 任何帮助表示赞赏。

【问题讨论】:

  • 那是因为你给了它100个字节的空间,所以当它找到行尾时它会停止。由于名称可以具有可变大小,并且如果您的项目允许,请在每行中的数据项之间使用分隔符,例如制表符或逗号或其他字符,一次将整行读取到足够大的缓冲区中,然后使用strtok 拆分行的每个字段。
  • 你不必使用 fgets()。您可以使用 fscanf() 并使用转换说明符来限制和确定您正在阅读哪些数据
  • 我不能使用 fscanf() 因为单词的数量并不总是相同的。有的用一个词,有的用4个。
  • fgetc(sex) 不正确。应该是:sex = fgetc(fh)。您应该注意编译器给您的警告。另请注意,while (!feof(fh)) is not good.
  • @Mabs2001 所以使用fgets 读取 100 然后性别将是其中的最后一个字符(没有单独读取它),并从名称中删除最后一个字符和空格之前

标签: c file fgets


【解决方案1】:

fgets(name, 100, fh); 最多读取 99 个字符,当名称少于 98 个字符时,如果之前只有一个空格,也会读取性别。

因为名称可能由空格分隔的几个单词组成,一种方法是读取所有行然后提取性别。

警告您第一次执行while (!feof(fh)) { 之前没有任何读取,因此feof 无法知道文件是否为空,然后您是否达到 EOF。我鼓励您检测读取结果的 EOF。

也因为你只保存if (!feof(fh)){时读取的数据,你没有记住最后一行的信息。

还要注意fgets如果有足够的地方保存换行符,使用fscanf更实用。

所以一种方法可以是:

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

#define ACTORS "/tmp/actors"

int main()
{
  FILE * fh = fopen(ACTORS, "r");

  if (!fh) {
    perror("cannot read " ACTORS);
    exit(1);
  }

  char name[100],id[10];

  while (fscanf(fh, "%9s %99[^\n]", id, name) == 2) {
    size_t sz = strlen(name);
    char sex = name[--sz];

    for (;;) {
      if (sz == 0) {
        puts("empty name");
        exit(2);
      }
      if (!isspace((unsigned char) name[--sz]))
        break;
    }

    name[sz+1] = 0;

    /*
    hash_update_node(hash, get_id_num(id) , name, sex);
    count++;
    */
    printf("id='%s', name='%s', sex=%c\n", id, name, sex);
  }

  fclose(fh);
  return 0;
}

编译和执行:

pi@raspberrypi:/tmp $ gcc -Wall r.c
pi@raspberrypi:/tmp $ ./a.out
cannot read /tmp/actors: No such file or directory
pi@raspberrypi:/tmp $ cat > actors
nm0025630 Vikas Anand M
nm0418131 Victor Janson M
nm0411451 Dick Israel M
nm0757820 Leopoldo Salcedo M
pi@raspberrypi:/tmp $ ./a.out
id='nm0025630', name='Vikas Anand', sex=M
id='nm0418131', name='Victor Janson', sex=M
id='nm0411451', name='Dick Israel', sex=M
id='nm0757820', name='Leopoldo Salcedo', sex=M
pi@raspberrypi:/tmp $ 

【讨论】:

  • 您好 bruno,文件中的字段似乎由 TAB 字符分隔。
  • 嗨@chqrlie,如果我们确定可以以fscanf 格式使用这种方式,而不必为性做这些事情。无论如何,即使使用制表符而不是 ' ' 我的答案也有效,因为我使用 isspace 而不是与 ' ' 进行比较。
  • @chqrlie 不,检查字符是否为空格时,减量没有问题。注意字符串不能为空并且至少包含性别,因为fscanf 返回了 2
  • 一些 C 库确实尝试使用 384 个标志字的数组来处理有符号字符和无符号字符值,因此允许使用介于 -128 和 255 之间的参数,但 C 标准并未强制执行此操作,并且可以做的事情是有限制的:char-1 无法与 EOF 区分开来(通常定义为 (-1))。因此,对于使用 ISO8859-1 编码和 char 默认签名的法语语言环境,无法同时使用 isalpha('ÿ') == 1isalpha(EOF) == 0
  • 这些问题根源的根本不一致是char 不应该默认签名。许多 C 库函数仅处理无符号字符:&lt;ctype.h&gt; 中的所有函数仅处理 unsigned char 的值和特殊的负值 EOFgetchar() 将字节值返回为 unsigned char 值(尽管名称!), strcmp() 将字符串作为unsigned char 的序列进行比较,(就像memcpy),ungetc()-1 上失败,但任何其他charint 值都只是转换为unsigned char,多么令人困惑'\xFF' 的值可能为 -1!
【解决方案2】:

文件中的字段似乎由 TAB 字符分隔。如果正确,可以使用fscanf()解析文件:

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

int local_file(void) {
    char sex, name[100], id[10];
    int count = 0;

    FILE *fh = fopen(ACTORS, "r");
    if (!fh) {
        exit(1);
    }
    while (fscanf("%9[^\t]%*1[\t]%99[^\t]%*1[\t]%c", id, name, &sex) == 3) {
        hash_update_node(hash, get_id_num(id), name, sex);
        count++;
    }
    return count;
}

但请注意,如果任何字段为空,此代码将失败。

【讨论】:

  • 所以只需在测试中添加&amp;&amp; *name &amp;&amp; *id(无论如何都是UV)
  • @bruno:如果fscanf() 返回3,则nameid 都不能是空字符串。我的意思是fscanf() 不能解析带有%[^\t] 的空字段,因为必须至少有一个不同于TAB 和'\0' 的字符才能成功转换。
猜你喜欢
  • 2013-02-25
  • 2013-01-13
  • 1970-01-01
  • 2019-11-19
  • 2016-12-22
  • 1970-01-01
  • 1970-01-01
  • 2018-06-16
  • 2015-06-23
相关资源
最近更新 更多