如何使用输入验证循环从包含一行不同值的数据文件中获取特定值?

How do I obtain specific values from a data file containing a row of different values using an input validation loop?

提问人:Marc Viernes 提问时间:6/10/2023 最后编辑:OkaMarc Viernes 更新时间:6/11/2023 访问量:64

问:

我想在数据文件上获取一行不同的值,在分别读取前几个值后,然后使用 for 循环读取每个后续值。但是,程序似乎没有读取每个后续值,并且不显示任何内容。我应该如何解决这个问题?

假设这些值如下所示:

4.2 2.0 8.0 3.4 5.1 8.9 7.9

这是我目前拥有的代码:

#include <stdio.h>
int main (void)
{
    int i=0,count;
    FILE *ptr;
    ptr = fopen("datafile.data","r");
    fscanf(ptr,"%d ",&count);
    double arr[count];
    for(i=0;i<count;i++)
    {
        fscanf(ptr,"%lf ",&arr[i]);
        printf("%lf\n",arr[i]);
    }
    fclose (ptr);
    return(0);
}

编辑:%d 的开头应该有一个 5,因此该行看起来像 5 4.2 2.0 8.0 3.4 5.1 8.9 7.9

数组 C 文件 scanf

评论

1赞 Some programmer dude 6/10/2023
你确定吗?这与您显示的输入不匹配。fscanf(ptr,"%d ",&count);
2赞 Some programmer dude 6/10/2023
您也没有进行错误检查。例如,如果无法打开文件会发生什么情况?并始终检查返回的内容(或任何相关功能)。fscanfscanf
1赞 Shawn 6/10/2023
我会阅读整行,然后对其进行处理以提取您想要的内容。sekrit.de/webdocs/c/beginners-guide-away-from-scanf.html
2赞 Marc Viernes 6/10/2023
我相信我错过了一些东西。在数据文件上列出的所有值之前都应该有“5”,并且计数应该是针对该整数的,并扫描行末尾的 5 个值,同时跳过 4.2 和 2.0。我的错。
0赞 Some programmer dude 6/10/2023
在调用后立即添加 然后会发生什么?fopenif (!ptr) { perror("Could not open file"); return 1; }

答:

0赞 amd 6/10/2023 #1

我修复了您的代码中的一些错误,首先是检查文件是否存在,然后将文件的扩展名更改为其次,我从文件中读取值,如下所示。.txt

#include <stdio.h>


int main (void)
{
    int i=0,count;
    FILE *ptr;
    ptr = fopen("datafile.txt","r");

    if (ptr == NULL) // check if file exists
    {
        printf("Error!");
        return 1;
    }

    double buffer[20];


    while (fscanf(ptr, "%lf", &buffer[i]) == 1) // read file contents till end of file
    {
        printf("%.2lf\n", buffer[i]);
        i++;
    }


    return(0);
}

输出

4.20
2.00
8.00
3.40
5.10
8.90
7.90

评论

0赞 Some programmer dude 6/10/2023
不要将 的结果与进行比较,如果存在非数字输入,这将导致无限循环。请改用为条件。fscanfEOFfscanf(...) == 1
0赞 Oka 6/11/2023 #2

给定一个输入文件

5
4.2 2.0 8.0 3.4 5.1 8.9 7.9

首先要做的是确保文件已正确打开

FILE *file = fopen("datafile.data", "r");

if (!file) {
    perror("fopen");
    exit(1);
}

您必须做的第二件事是确保正确读取初始值,并且处于适当的范围内,以免在创建 VLA 时导致未定义行为。这意味着大于零,并且低于某个合理的限制,以免溢出程序的堆栈(即 等)。ulimit -s

应始终检查的返回值是否为预期的成功转换次数。在这里,我们转换一个值。fscanf

#define MAXSIZE 128

size_t size = 0;

if (1 != fscanf(file, "%zu", &size) || !size || size > MAXSIZE) {
    fprintf(stderr, "Invalid size.\n");
    return 1;
}

size_t 是用于内存大小和偏移量的最一般适用类型。


现在,上面的评论

[ ... ]并扫描行末尾的 5 个值,同时跳过 4.2 和 2.0。

引入了一个问题:

除非您的输入始终是静态大小,否则我们怎么知道要跳过多少个值?

如果流是可查找的,一种方法是简单地读取并计算有多少个值。从此计数中减去初始值即可得到要跳过的量。之后,倒带文件并对其进行正确的读取和存储传递。

另一种选择是仅存储读取的最新 N 个值,丢弃以前的值。这可以通过一种循环缓冲区/队列来实现。

一个基本的例子:

double data[size];
size_t total_read = 0;
size_t index = 0;

while (1 == fscanf(file, "%lf", data + index)) {
    total_read++;
    index = (index + 1) % size;
}

if (total_read < size)
    fprintf(stderr, "WARN: Did not read all [%zu] values.\n", size);

之后,如果需要(即顺序很重要和 ),可以通过缓冲区旋转 来将值返回到其读取顺序。total_read > sizeindex