如何将未知数量的整数fscaf到数组中

时间:2019-11-20 18:22:54

标签: c arrays integer scanf

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

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;
}

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

1 个答案:

答案 0 :(得分:0)

除非不能使用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()

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

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

由于除非阅读良好,否则打算退出,因此使用!(f >> N)格式。 (这也减少了构建到代码中的缩进级别)。您可以做到:

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

验证是相同的,但是在第一种情况下,我们在未能读取N的情况下退出,在第二种情况下,我们只是不输入包含其余代码的块(现在已将其移位右边还有一层压痕...)

读取其余值并填充向量,只需以相同的方式读取临时值tmp,然后在确认良好的读取之后,您只需使用.push_back()成员函数,例如

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

注意:会读取所有空白的数字值。这意味着您在文件中还保留了1行还是1000行整数值-全部将会被读入向量中,因为'\n' ,因此所有'\n'都将被忽略,这就是您使用getline()std::stringstream的地方如果您只需要读取一行中的值)

读取第一个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中的值进行任何操作。在这里,它们只是使用基于 range 的循环进行输出,并重新使用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解决方案。仔细研究一下,如果您还有其他问题,请告诉我。