【问题标题】:reading unknown number of integers from stdin (C)从标准输入(C)读取未知数量的整数
【发布时间】:2010-02-03 22:03:11
【问题描述】:

我需要读取如下输入文件:

1
19 20 41 23
2
41 52 43
3
90 91 941
4
512
5

6
51 61

每个奇数行都是一个整数。每个偶数行都是未知数量的整数。

在 C++ 中很容易

while( cin >> k ){
............
}

我不太习惯C,所以我不能用C。有什么办法吗?

【问题讨论】:

  • 你必须注意行,还是这只是一系列整数进来?在您的示例中,奇数行似乎是序列号,偶数行代表其他内容。
  • 大卫我需要注意台词。这就是为什么我不能这样做。我需要在每条偶数行的末尾停止获取新值。因为我有一个带有属性 id 和 list 的结构。每个奇数行是一个 id,每个偶数行是一个列表。我想用 2 行数据填充一个结构并移动到另一个结构并用另外 2 行数据等填充它......
  • 一次读取一整行,然后将其解析为字符串

标签: c stdin integer


【解决方案1】:

通过以下方式运行您的输入文件:

#include <stdio.h>

int main() {
        int k;
        while (scanf("%d", &k) == 1) {
                printf("read number: %d\n", k);
        }
        return 0;
}

结果:

读数:1 读数:19 读取数:20 读数:41 读数:23 读数:2 读数:41 读数:52 读数:43 读数:3 读数:90 读数:91 读数:941 读数:4 读数:512 读数:5 读数:6 阅读数量:51 读数:61

这是您在原始问题中引用的代码的 C 模拟。

【讨论】:

  • 我可以正确读取该行,但 scanf 循环会无限为我打印第一个数字。
【解决方案2】:

我的做法是将其分解为两个操作:读取一行,然后读取该行中的整数。这是一个使用标准 C 库的惰性实现:

char line[1024], *p, *e;
long v;
while (fgets(line, sizeof(line), stdin)) {
    p = line;
    for (p = line; ; p = e) {
        v = strtol(p, &e, 10);
        if (p == e)
            break;
        // process v here
    }
}

【讨论】:

  • 非常感谢。我现在就试试。但是如果我不能假设一个固定的最大行长呢?
  • 如果输入流包含超过 1024 - 2 个字符并且fgets() 将输入流拆分为数字文字怎么办?您最终将得到两个数值而不是一个。例如,从 12345 开始,您可以在 for 循环的最后一次迭代中获得 123,在下一次调用 fgets() 之后的 for 循环的第一次迭代中获得 45。因此,我更喜欢 scanf()fscanf() 来完成这项任务,如 Sean 所示。
  • @RobinKlose:Sean 发布的scanf() 解决方案并没有解决问题,因为它无法区分换行符和其他空格。
  • @DietrichEpp:好的,我明白你的意思了。提问者需要跟踪线路。但是,您有没有发现解决我上面描述的问题的简单方法?
  • 请注意,getline() 是 POSIX 标准的一部分,它在 Windows 上不可用。
【解决方案3】:

我会在不同的任务中打破程序。

第一步是能够读取一对行,第一行告诉您要读取的数字数量,然后第二行读取实际数字。为此,一个名为 read_set 之类的函数可能很有用。它应该能够返回读取的数字,以及文件结束的信号以及错误。为此,我们可以定义如下数据结构:

struct numbers {
    long *data; /* or choose a type depending upon your needs */
    size_t len;
};

然后我们可以用原型声明我们的函数:

int read_set(FILE *fp, struct numbers *num);

该函数将为num-&gt;data 分配内存,并将num-&gt;len 设置为正确的值。它返回 0 表示成功,否则返回一组错误条件。我们可能会喜欢并使用enum 稍后返回状态。现在,假设 0 = 成功,1 = 文件结束,其他一切都是错误。

然后调用者循环调用read_set()

struct numbers numbers;
int status;
while ((status = read_set(fp, &numbers)) == 0) {
    /* process numbers->data, and then free it */
}
if (status == 1) {
    /* hit end of file, everything is OK */
} else {
    /* handle error */
}

为了实现read_set():它必须读取两行。 implementations of reading a full line in C 有很多,所以你可以使用其中的任何一个,先读取一行,然后将 sscanf()/strtoul() 读取一个数字(检查它的返回值!)。一旦你有了数字的数量,n,你就可以读取内存中的下一行,然后:

num->data = malloc(n * sizeof *num->data);
num->len = n;

然后您可以反复调用sscanf()strtol() 将号码存储在num-&gt;data 中。您应该检查以确保 n 号码在该行上。

请注意,您也可以用其他方式编写read_set():逐个字符读取行,并在读取时解析数字。这样做的好处是只需遍历一次数据,不需要大缓冲区将整个输入行存储在内存中,但缺点是自己做低级的东西,逐个字符读取数据可能很慢。

【讨论】:

    【解决方案4】:

    我会做以下之一:

    • fgetc() 读取单个字符并自己解析它们(累积数字直到你点击空格并且你有一个整数可以用 atoi() 转换;如果空格是换行符,那么它会终止一个整数列表)

    • fgets() 一次读取一行,然后解析它返回的字符串(同样,寻找分隔值的空格)。

    【讨论】:

      【解决方案5】:

      我想出了一个这样的解决方案:

      #include <stdio.h>
      
      int main(void) 
      {
          int index = 0;
          char ch;
          int arr[1024];
          
          while(scanf("%d%c", &arr[index++], &ch)!=EOF)
          {
              if(ch=='\n')
              {
                  // One line is read into arr
                  // number of integers in this line = index
                  
                  // Rest of your code which can proces arr[]
                  
                  // Example
                  int i;
                  for(i = 0; i < index; i++) 
                  {
                      printf("%d ", arr[i]);
                  }
                  printf("\n");
                  
                  // Set the index back to 0 for the upcoming line
                  index = 0;
              }
          }
          
          return 0;
      }
      

      【讨论】:

        【解决方案6】:

        看看 getc(3) 或 scanf(3)

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2019-07-26
          • 1970-01-01
          • 2011-04-14
          • 1970-01-01
          • 1970-01-01
          • 2015-05-28
          • 2014-12-03
          相关资源
          最近更新 更多