【问题标题】:How to use fscanf() format string如何使用 fscanf() 格式字符串
【发布时间】:2019-04-11 06:28:27
【问题描述】:

我正在使用 fscanf() 从文件中读取输入(我知道我应该使用 fgets() 但我不被允许)并且我不知道如何正确使用格式字符串。

输入格式为: M 03f8ab8,1

我需要将字母、地址和数字分别保存到一个变量中。到目前为止,这是我所得到的:

while(fscanf(file, " %s %s, %d", operation, address, &size) != -1)

正如所写,它将字母放入正确的 var(操作)中,但将 ,number 添加到地址的末尾,然后将未定义的内容分配给 size 变量。

应该将每个变量放入各自的变量中(并忽略逗号)

如何设置 fscanf() 以正确获取此信息?

【问题讨论】:

  • 请发minimal reproducible example,我们需要查看所有声明等
  • 地址的前导“0”可能有问题,如果/当您尝试读取为 int 时。
  • 如果地址是十六进制数,可以使用 %x 将其存储到无符号整数中

标签: c scanf


【解决方案1】:

这里的问题是"%s" 格式读取的是空格 分隔的字符串,并且由于03f8ab8,1 中没有空格,所以它将被读取为单个字符串。

您可以使用"%[" 格式解决这个问题,它允许您进行一些非常简单的模式匹配。例如,您可以使用它告诉fscanf 阅读所有内容,直到(但不包括)逗号。喜欢

fscanf(file, "%s %[^,], %d", operation, address, &size)

参见例如this scanf (and family) reference 了解更多详情。

另外,您不应该将fscanf 的结果与-1 进行比较,而是通过将返回值与3 进行比较来检查它是否解析了正确数量的序列:

while (fscanf(file, "%s %[^,], %d", operation, address, &size) == 3) ...

请注意,上述格式不会对其将读取的字符串施加任何限制。这可能会导致您的字符串溢出。如果您的字符串具有固定大小(即它们是数组),则使用格式最大字段宽度来限制 fscanf 将读取并放入您的数组的字符数。

例如(对你的实际字符串/数组一无所知):

while (fscanf(file, "%1s %8[^,], %d", operation, address, &size) == 3) ...

有了上述,第一个字符串不能超过一个字符,第二个字符串不能超过八个字符。请注意,这些数字包含字符串空终止符(您的数组中需要超出上述大小的空间)。

【讨论】:

  • 这完全有效,非常感谢您的帮助。我不得不承认这个让我卡了很长时间。
  • 应注意缺少宽度说明符。
【解决方案2】:
fscanf(input_fp, "%30[^ ,\n\t]%30[^ ,\n\t]%30[^ ,\n\t]", ...

不使用文本文件中的 ',' 和 '\n'。随后的 fscanf() 尝试也失败并返回值 0,这不是 EOF,会导致无限循环。


fscanf() 解决方案,fgets()/sscanf() 更好地处理潜在的 IO 和解析错误:

main()
{
    FILE *input_fp;
    FILE *output_fp;
    char buf[100];
    while (fgets(buf, sizeof buf, input_fp) != NULL) 
    {
      char name[30];  // Insure this size is 1 more than the width in scanf format.
      char age_array[30];
      char occupation[30];
      #define VFMT " %29[^ ,\n\t]"
      int n;  // Use to check for trailing junk

      if (3 == sscanf(buf, VFMT "," VFMT "," VFMT " %n", 
          name, age_array, occupation, &n) && buf[n] == '\0') 
      {
        // Suspect OP really wants this width to be 1 more
        if (fprintf(output_fp, "%-30s%-30s%-30s\n", name, age_array, occupation) < 0)
          break;
      } else
        break;  // format error
    }
    fclose(input_fp);
    fclose(output_fp);
}

不调用 ferror(),而是检查 fgets()、fprintf() 的返回值。

怀疑 OP 的未声明字段缓冲区为 [30] 并相应调整了 scanf()。

关于if (3 == sscanf(buf, VFMT "," ...的详细信息

if (3 == sscanf(...) &amp;&amp; buf[n] == '\0') { 在以下情况下变为真:

1) 正是 3 个 "%29[^ ,\n\t]" 格式说明符,每个 scanf 至少有 1 个字符。

2) buf[n] 是字符串的结尾。 n 通过“%n”说明符设置。 "%n" 中的前面的 ' ' 会导致使用最后一个 "%29[^ ,\n\t]" 之后的任何后续空格。 scanf() 看到“%n”,它指示它设置从扫描开始的当前偏移量,以分配给 &n 指向的 int。

"VFMT "," VFMT "," VFMT " %n" 被编译器连接到

" %29[^ ,\n\t], %29[^ ,\n\t], %29[^ ,\n\t] %n".

我发现前者比后者更容易维护。

" %29[^ ,\n\t]" 中的第一个空格指示 sscanf() 扫描(使用而不是保存)0 个或多个空格(' '、'\t'、'\n' 等)。其余部分指示 sscanf() 使用并保存除 ','、'\n'、'\t' 之外的任何 1 到 29 个字符,然后附加一个 '\0'。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-12-19
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多