我是C编程的初学者。我需要使用文件中的struct有效地从文件中读取数百万个数据。以下是输入文件的示例。
2,33.1609992980957,26.59000015258789,8.003999710083008
5,15.85200023651123,13.036999702453613,31.801000595092773
8,10.907999992370605,32.000999450683594,1.8459999561309814
11,28.3700008392334,31.650999069213867,13.107999801635742
我有下面显示的当前代码,它给出了错误“文件错误” 建议文件为NULL,但文件包含数据。
#include<stdio.h>
#include<stdlib.h>
struct O_DATA
{
int index;
float x;
float y;
float z;
};
int main ()
{
FILE *infile ;
struct O_DATA input;
infile = fopen("input.dat", "r");
if (infile == NULL);
{
fprintf(stderr,"\nError file\n");
exit(1);
}
while(fread(&input, sizeof(struct O_DATA), 1, infile))
printf("Index = %d X= %f Y=%f Z=%f", input.index , input.x , input.y , input.z);
fclose(infile);
return 0;
}
我需要有效地从输入文件中读取和存储数据以进行进一步处理。任何帮助将非常感激。提前感谢。
〜
〜
〜
答案 0 :(得分:1)
if (infile == NULL);
{ /* floating block */ }
以上if
是一个完整的语句,无论infile
的值是什么都不做。无论infile
包含什么内容,都将执行“浮动”块。
删除分号以将“浮动”块“附加”到if
if (infile == NULL)
{ /* if block */ }
答案 1 :(得分:1)
;
测试后,您的if (infile == NULL)
错误,请尝试删除该错误...
[编辑:第二秒9秒! :-)]
答案 2 :(得分:1)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct my_data
{
unsigned int index;
float x;
float y;
float z;
};
struct my_data *
deserialize_data(struct my_data *data, const char *input, const char *separators)
{
char *p;
struct my_data tmp;
if(sscanf(input, "%d,%f,%f,%f", &data->index, &data->x, &data->y, &data->z) != 7)
return NULL;
return data;
}
deserialize_data(struct my_data *data, const char *input, const char *separators)
{
char *p;
struct my_data tmp;
char *str = strdup(input); /* make a copy of the input line because we modify it */
if (!str) { /* I couldn't make a copy so I'll die */
return NULL;
}
p = strtok (str, separators); /* use line for first call to strtok */
if (!p) goto err;
tmp.index = strtoul (p, NULL, 0); /* convert text to integer */
p = strtok (NULL, separators); /* strtok remembers line */
if (!p) goto err;
tmp.x = atof(p);
p = strtok (NULL, separators);
if (!p) goto err;
tmp.y = atof(p);
p = strtok (NULL, separators);
if (!p) goto err;
tmp.z = atof(p);
memcpy(data, &tmp, sizeof(tmp)); /* copy values out */
goto out;
err:
data = NULL;
out:
free (str);
return data;
}
int main() {
struct my_data somedata;
deserialize_data(&somedata, "1,2.5,3.12,7.955", ",");
printf("index: %d, x: %2f, y: %2f, z: %2f\n", somedata.index, somedata.x, somedata.y, somedata.z);
}
只是此处的主要功能(插入上一个示例中的其余功能)
int
main(int argc, char *argv[])
{
FILE *stream;
char *line = NULL;
size_t len = 0;
ssize_t nread;
struct my_data somedata;
if (argc != 2) {
fprintf(stderr, "Usage: %s <file>\n", argv[0]);
exit(EXIT_FAILURE);
}
stream = fopen(argv[1], "r");
if (stream == NULL) {
perror("fopen");
exit(EXIT_FAILURE);
}
while ((nread = getline(&line, &len, stream)) != -1) {
deserialize_data(&somedata, line, ",");
printf("index: %d, x: %2f, y: %2f, z: %2f\n", somedata.index, somedata.x, somedata.y, somedata.z);
}
free(line);
fclose(stream);
exit(EXIT_SUCCESS);
}
答案 3 :(得分:1)
您已经对语法/ structs / etc有了可靠的答复,但是我将提供另一种读取文件本身中数据的方法:我喜欢Martin York的CSVIterator解决方案。这是我处理CSV的首选方法,因为它需要更少的代码来实现,并且具有易于修改的额外好处(即,您可以根据需要编辑CSVRow和CSVIterator定义)。
这是一个使用Martin未经编辑的代码而不包含结构或类的最完整的示例。在我看来,尤其是对于初学者而言,以更简单的方法开始开发代码更加容易。随着代码开始成形,您将更清楚为什么以及在何处需要实现更多抽象/高级设备。
请注意,由于我使用std::stod(从技术上讲,这将需要使用C ++ 11或更高版本进行编译)(也许我也忘记了其他一些东西),因此请考虑以下因素:
//your includes
//...
#include"wherever_CSVIterator_is.h"
int main (int argc, char* argv[])
{
int index;
double tmp[3]; //since we know the shape of your input data
std::vector<double*> saved = std::vector<double*>();
std::vector<int> indices;
std::ifstream file(argv[1]);
for (CSVIterator loop(file); loop != CSVIterator(); ++loop) { //loop over rows
index = (*loop)[0];
indices.push_back(index); //store int index first, always col 0
for (int k=1; k < (*loop).size(); k++) { //loop across columns
tmp[k-1] = std::stod((*loop)[k]); //save double values now
}
saved.push_back(tmp);
}
/*now we have two vectors of the same 'size'
(let's pretend I wrote a check here to confirm this is true),
so we loop through them together and access with something like:*/
for (int j=0; j < (int)indices.size(); j++) {
double* saved_ptr = saved.at(j); //get pointer to first elem of each triplet
printf("\nindex: %g |", indices.at(j));
for (int k=0; k < 3; k++) {
printf(" %4.3f ", saved_ptr[k]);
}
printf("\n");
}
}
编写时很少大惊小怪,但更危险(如果save []超出范围,则有麻烦)。还存在一些不必要的复制,但是我们受益于使用std :: vector容器,而不是确切知道我们需要分配多少内存。
答案 4 :(得分:0)
不提供输入文件的示例。 至少在纸上或评论中指定您的输入file format ,例如以EBNF表示法(因为您的示例是 text ...它不是 一个binary file)。确定数字是否必须位于不同的行中(或者您是否可以接受由百万字节组成的单行大文件;请阅读Comma Separated Values格式)。然后,为该格式编写一些parser。对于您来说,很简单的recursive descent parsing就足够了(并且您的特定解析器甚至不会使用recursion)。
详细了解<stdio.h>
and its routines。花时间仔细阅读该文档。由于您输入的是文本,而不是 binary ,因此您不需要fread。请注意,输入例程可能会失败,因此您应该处理失败情况。
当然,fopen
可能会失败(例如,因为您的working directory不是您认为的那样)。您最好使用perror或errno来查找有关失败原因的更多信息。所以至少是代码:
infile = fopen("input.dat", "r");
if (infile == NULL) {
perror("fopen input.dat");
exit(EXIT_FAILURE);
}
请注意,分号(或分号的不存在)在C语言中非常重要(if
后没有分号)。再次阅读C language的基本语法。了解有关How to debug small programs的信息。编译时启用所有警告和调试信息(使用GCC,至少使用gcc -Wall -g
进行编译)。编译器警告非常有用!
请记住,fscanf对行尾(换行符)的处理与空格字符不同。因此,如果输入必须具有不同的行,则需要分别阅读每一行。
您可能会使用fgets(或getline)读取每条行,并分别解析每行。您可以在sscanf的帮助下进行解析(也许%n
可能有用)-并且您想使用sscanf
的返回计数。您也可以使用strtok和/或strtod进行这样的解析。
请确保您的解析和整个程序正确无误。对于当前的计算机(速度非常快,并且大多数时候您的输入文件位于page cache中),它很可能足够快。一百万行可以很快读取(如果在Linux上,您可以将解析时间与wc用来计算文件行的时间进行比较)。在我的计算机上(具有AMD2970WX处理器的功能强大的Linux桌面-它具有很多内核,但是您的程序仅使用一个,64 GB的RAM和SSD磁盘),可以用更少的时间(wc
)读取一百万行少于30毫秒,所以我想如果给定一百万行输入,并且进一步的处理很简单(线性时间),则整个程序应在不到半秒的时间内运行。
您可能会填充struct O_DATA
的大型数组,并且该数组可能应该动态分配,并在需要时重新分配。进一步了解C dynamic memory allocation。仔细阅读有关C memory management routines的信息。它们可能会失败,因此您需要处理该失败(即使不太可能发生)。您当然不想在每个循环中重新分配该数组。您可能可以在一些geometrical progression中分配它(例如,如果该数组的大小为size
,则您将为某些{{1}调用realloc
或新的malloc
}仅当旧的int newsize = 4*size/3 + 10;
太小时)。当然,您的数组通常会比实际需要的大一些,但是内存非常便宜,您可以“丢掉”其中的一些。
但是StackOverflow不是 一个“做我的作业”网站。我在上面给出了一些建议,但是您应该做功课。