【问题标题】:How to fscaf unknown number of integers into an array如何将未知数量的整数 fscaf 放入数组中
【发布时间】:2019-11-20 18:22:54
【问题描述】:

我正在从标准输入读取具有以下格式的文本文件:

5

4 7 9 2 1

第一行是我每次要创建的数组的大小,第二行是元素

到目前为止我有这个

int main()
{
  int N;
  int i;
  FILE *fp;
  const char *mode="r";
  fp=fopen("input.txt", "r");

  if((fp=fopen("input.txt", "r"))==NULL) {
    printf("Error opening file %s\n");
    return -1;
  }

  fscanf(fp, "%d", &N);

  int a[N];
  int n;

  while (fscanf(fp, "%d %n", &a[i], &n) == 1){
    i++;
    fp += n;
  }

  fclose(fp);
  return 0;
}

它读取文件但它没有存储正确的值

【问题讨论】:

  • 本应是 c++ 的你为什么不使用任何 c++?
  • 巧合的是,您的代码是有效的 C 但不是有效的 C++。 int a[N]; 是可变长度数组,C 有,C++ 没有
  • 30 年前,C++ 是 C 的超集。(现在它们是完全不同的动物)
  • @Sailanarmo no. C++ 无法编译 C。除了 VLA(在 gcc 中作为非标准扩展也在 C++ 中存在)之外,有一个小的子集是正确的代码,这个代码属于那个子集 afaik,因此只有 OP 可以知道它们是什么用来编译它
  • 有一个子集在两者中都是正确的,并且只有该子集的一个子集在两者中具有相同的行为。

标签: c arrays integer scanf


【解决方案1】:

除非您不能使用来自 STL 的容器,例如 std::vector,否则您将通过使用它们变得更容易,更不容易出错。在这里,虽然您可以读取第一个值 N,但当您使用 std::vector 进行存储时,则不需要它。 STL 容器会自动处理所需的存储空间,因此您真正关心的是从文件中读取实际数据值,然后使用 .push_back() 成员函数将值添加到向量中。

不要在您的代码中硬编码文件名。这就是 int main (int argc, char **argv) 的参数的用途(或者您可以将文件名作为输入)。例如你可以这样做:

int main (int argc, char **argv) {

    int N, tmp;             /* int N and temporary int to use filling vector */
    std::vector<int> v;     /* vector of int */

    if (argc < 2) { /* valdate 1 argument given for filename */
        std::cerr << "usage: " << argv[0] << " filename\n";
        return 1;
    }
    std::ifstream f (argv[1]);  /* open file */

    if (!f.good()) {    /* validate file open */
        std::cerr << "error: file open failed.\n";
        return 1;
    }

无需硬编码您的文件名,只需将其作为参数传递给main()。

打开文件后,以最简单的形式,您可以简单地使用重载的&gt;&gt; 流运算符从文件中读取值。您必须验证每次读取以确保杂散的非数字字符确实会导致匹配失败并在输入流上设置失败的流状态。例如要检查N 的读取是否良好(这里不错),您可以这样做:

    if (!(f >> N)) {    /* read/validate N */
        std::cerr << "error: invalid integer input - N.\n";
        return 1;
    }

由于意图是退出,除非读取良好,因此使用了!(f &gt;&gt; N) 表单。 (这也降低了您在代码中构建的缩进级别)。你可以这样做:

    if (f >> N) {    /* read/validate N */
        /* rest of your code goes here on good read */
    }

验证是相同的,但是在第一种情况下,我们在读取 N 失败时退出,在第二种情况下,我们只是不进入包含其余代码的块(现在又移动了一个右边的缩进层...)

读取其余值并填充向量只需以相同方式读取临时值tmp,然后在确认读取良好后,您只需使用 @987654336 将值添加到向量中@成员函数,例如

    while (f >> tmp)    /* read remaining int values into vector v */
        v.push_back(tmp);

(注意: 读取所有数值 空格 被忽略。这意味着文件中是否还有 1 行或 1000 行整数值 - 全部将被读入您的向量。所有'\n' 都将被忽略,因为'\n' 是空白。如果您只需要读取一行)

读取第一个N 仅有助于与向量的.size() 进行比较,您现在可以使用它来确认您已将N 值读入向量中,例如

    if (v.size() != (size_t)N) {    /* valdate .size() == N */
        std::cerr << "error: number read does not equal N.\n";
        return 1;
    }

您可以对向量v 中的值做任何您想做的事情。在这里,它们只是使用 基于范围的 for 循环输出,并将 tmp 重新用作计数器以输出索引:

    tmp = 0;
    for (auto& n : v)   /* loop over all values in v outputting */
        std::cout << "v[" << tmp++ << "] : " << n << '\n';

总而言之,你可以这样做:

#include <iostream>
#include <fstream>
#include <vector>

int main (int argc, char **argv) {

    int N, tmp;             /* int N and temporary int to use filling vector */
    std::vector<int> v;     /* vector of int */

    if (argc < 2) { /* valdate 1 argument given for filename */
        std::cerr << "usage: " << argv[0] << " filename\n";
        return 1;
    }
    std::ifstream f (argv[1]);  /* open file */

    if (!f.good()) {    /* validate file open */
        std::cerr << "error: file open failed.\n";
        return 1;
    }

    if (!(f >> N)) {    /* read/validate N */
        std::cerr << "error: invalid integer input - N.\n";
        return 1;
    }

    while (f >> tmp)    /* read remaining int values into vector v */
        v.push_back(tmp);

    if (v.size() != (size_t)N) {    /* valdate .size() == N */
        std::cerr << "error: number read does not equal N.\n";
        return 1;
    }

    tmp = 0;
    for (auto& n : v)   /* loop over all values in v outputting */
        std::cout << "v[" << tmp++ << "] : " << n << '\n';
}

使用/输出示例

使用文件dat/nint.txt 中的数据,您将使用以下程序生成以下输出:

$ ./bin/readnint dat/nint.txt
v[0] : 4
v[1] : 7
v[2] : 9
v[3] : 2
v[4] : 1

故事的寓意 -- 请勿使用 scanf。对于新的 C/C++ 程序员来说,它充满了陷阱,并且它的不当使用会在本网站上产生如此多的问题,这无异于 programming-malpractice 提出scanf 解决方案。如果您还有其他问题,请仔细查看并告诉我。

【讨论】:

    猜你喜欢
    • 2017-04-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-14
    • 2022-06-14
    • 2013-12-25
    相关资源
    最近更新 更多