【问题标题】:How to use fscanf() to get strings of multiple format?如何使用 fscanf() 获取多种格式的字符串?
【发布时间】:2016-04-11 19:42:44
【问题描述】:

说这是我要阅读的文件:

07983988 REMOVE String1
13333337 ADD String4 100
34398111 TRANSFER String5 String6 100

这些是仅有的 3 种有效的格式类型。

我正在使用以下代码块来检查解析的行的格式是什么:

// Read from file.
    while (!feof(fd)) {

        // Check for format.
        if (fscanf(fd, "%d %s %s %s %lf", &timestamp, transaction_type_str, company1, company2, &value)) {
            node_t *transaction = create_node((long int)timestamp, 1, company1, company2, value);
            add_node(transactions, transaction);
        } else if (fscanf(fd, "%d %s %s", &timestamp, transaction_type_str, company1)) {
            node_t *transaction = create_node((long int)timestamp, 1, company1, NULL, 0);
            add_node(transactions, transaction);
        } else if (fscanf(fd, "%d %s %s %lf", &timestamp, transaction_type_str, company1, &value)) {
            node_t *transaction = create_node((long int)timestamp, 1, company1, NULL, value);
            add_node(transactions, transaction);
        }

然而,这给了我一个无限循环。我是 C 文件 I/O 的新手,我想知道使用标记化方法还是基于行的格式搜索方法更好。

【问题讨论】:

  • fscanf() 的问题在于它具有破坏性。您几乎肯定需要使用fgets()getline() 来读取一行,然后使用sscanf() 进行分析。这允许您在第一次分析失败时重试。
  • 我使用的是 ANSI C,所以我认为fgets() 是我唯一的选择?
  • 这取决于您所说的“ANSI C”是什么意思。如果你坚持使用 C89/C90,那么我为你感到抱歉——你应该能够使用 C11 或 C99。这还取决于这是否意味着“仅由标准(相关版本)定义的功能”或其他内容。但是,作为起点,fgets() 是最可靠的可用工具。用它来读一行:char buffer[4096]; while (fgets(buffer, sizeof(buffer), fd) != 0) { …analyze buffer with sscanf() etc… }(不是不使用feof())。
  • 我正在尝试对sscanf() 做同样的事情。我使用了fgets() 并且工作正常,但是我的sscanf() 逻辑有问题,我无法弄清楚。

标签: c io scanf


【解决方案1】:

大纲:

char buffer[4096];

while (fgets(buffer, sizeof(buffer), fd) != 0)
{
    int offset;
    if (sscanf(buffer, "%d %s %n", &timestamp, transaction_type_str, &offset) == 2)
    {
         char *residue = buffer + offset;
         if (strcmp(transaction_type_str, "REMOVE") == 0)
         {
              if (sscanf(residue, "%s", company1) == 1)
                  …process remove…
              else
                  …report error, etc…
         }
         else if (strcmp(transaction_type_str, "ADD") == 0)
         {
             if (sscanf(residue, "%s %lf", company1, &value) == 2)
                 …process add…
             else
                 …report error, etc…
         }
         else if (strcmp(transaction_type_str, "TRANSFER") == 0)
         {
             if (sscanf(residue, "%s %s %lf", company1, company2, &value) == 3)
                 …process transfer…
             else
                 …report error, etc…
         }
         else
         {
             …report error and continue or break…
         }
     }
 }

您可以使分析更严格,例如,在第二次sscanf() 调用完成后坚持没有未使用的数据等。这很繁琐,但远非不可能。

这涵盖了请求的代码——它在分析该行的其余部分之前确定请求的事务类型。

【讨论】:

    【解决方案2】:

    问题是您使用 feof 测试作为 while 控制。以下是它不起作用的原因。

    Why is “while ( !feof (file) )” always wrong?

    FAQ > Why it's bad to use feof() to control a loop

    该函数测试文件结束指示符,而不是流本身。 这意味着另一个函数实际上负责设置 已达到表示 EOF 的指标。这通常是 由执行命中 EOF 的读取的函数完成。那么我们可以 将问题跟踪到该函数,我们发现大多数阅读 函数将在读取所有数据后设置 EOF,然后 执行最终读取,结果没有数据,只有 EOF。

    考虑到这一点,它是如何在我们的 sn-p 的代码?很简单......因为程序通过循环得到 最后一行数据,fgets()正常工作,无需设置EOF, 我们打印出数据。循环返回顶部,调用 to feof() 返回 FALSE,然后我们再次开始循环。 这一次, fgets() 看到并设置了 EOF,但感谢我们的穷人 逻辑上,我们继续处理缓冲区,却没有意识到 它的内容现在是未定义的(很可能从最后 循环)。

    【讨论】:

      【解决方案3】:

      使用fgets(),然后使用sscanf()" %n" 说明符来检测扫描完成。

      通过检测扫描是否到达末尾并且行中没有其他文本,我们进行了清晰且对称的解析检测。很像 OP 的原始代码。

      #define BUFSIZE 200
      char buf[BUFSIZE];
      
      while (fgets(buf, sizeof buf, fd) != NULL) {
        int n = 0; 
        if (sscanf(buf, "%d %s %s %s %lf %n", 
            &timestamp, transaction_type_str, company1, company2, &value, &n));
        if (n > 0 && buf[n] == '\0') {
          node_t *transaction = create_node((long int)timestamp, 1, company1, company2, value);
          add_node(transactions, transaction);
          continue;
        }
      
        n = 0; 
        sscanf(buf, "%d %s %s %n", 
            &timestamp, transaction_type_str, company1, &n));
        if (n > 0 && buf[n] == '\0') {
          node_t *transaction = create_node((long int)timestamp, 1, company1, NULL, 0);
          add_node(transactions, transaction);
          continue;
        }
      
        n = 0; 
        sscanf(buf, "%d %s %s %lf %n", 
            &timestamp, transaction_type_str, company1, &value, &n);
        if (n > 0 && buf[n] == '\0') {
          node_t *transaction = create_node((long int)timestamp, 1, company1, NULL, value);
          add_node(transactions, transaction);
          continue;
        }
      
        Handle_BadLine(buf);  // do not use transaction_type_str, company1, company2, value, n
      }
      

      注意:使用"%s" 是危险的。最好限制宽度

      char transaction_type_str[20];
      ...
      sscanf(buf, "%d %19s ...
      

      【讨论】:

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