如何使用fscanf()获取多种格式的字符串?

时间:2016-04-11 19:42:45

标签: c io scanf

说这是我想要阅读的文件:

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新手,我想知道使用标记化方法或基于行的格式搜索方法是否更好。

3 个答案:

答案 0 :(得分: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()呼叫完成后没有未使用的数据,等等。这很费劲,但远非不可能。

这涵盖了所请求的代码 - 它在分析行的其余部分之前识别请求的事务类型。

答案 1 :(得分:0)

问题在于您使用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。

     

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

答案 2 :(得分:0)

使用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 ...