[英]Read from csv file and separate into variable
我正在尝试将输入值分为2个不同的类别。 第一个数组调用团队名称将保留团队名称,第二个数组将保存该周的分数。 我的输入文件是.csv,其中的代码是将所有内容存储为字符串而不是2个单独变量的方式。 另外,我不会精通程序,只熟悉该库。
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#define FILEIN "data.csv"
#define FILEOUT "matrix.csv"
int main (void)
{
double nfl[32][32], teamscore[32];
char teamname[30];
int n;
FILE *filein_ptr;
FILE *fileout_ptr;
filein_ptr = fopen (FILEIN, "r");
fileout_ptr = fopen (FILEOUT, "w");
for (n = 1; n <= 32; n++) {
fscanf (filein_ptr, "%s %lf\n", &teamname, &teamscore[n]);
fprintf (fileout_ptr, "%s %f\n", teamname, teamscore);
}
fclose (filein_ptr);
fclose (fileout_ptr);
return 0;
}
我应该说输入文件的第一列包含团队名称,第二列包含团队得分。 任何帮助都会很棒。 谢谢! 这是一个示例输入文件
除了将&teamname
更改为teamname
,您还需要考虑其他一些注意事项。 首先,总是初始化变量。 尽管不是必需的,但这具有许多积极的好处。 对于数字数组,它将初始化所有元素,以防止意外读取未初始化的值。 对于字符数组,初始化为0
可以确保字符串的第一个副本(小于总长度)将以null-terminated
并且还可以防止尝试从未初始化的值进行读取。 这只是个好习惯:
double teamscore[MAXS] = {0.0};
char teamname[30] = {0};
int n = 0;
您已经为filein_ptr
和fileout_ptr
定义了默认值,您可以对数组大小执行相同的操作。 如果需要更改数组大小,只需提供一个值即可更新,从而使代码的维护更加容易。
接下来,这是一个尼特,但很重要。 main
接受参数,按标准将其定义为int argc, char **argv
(您可能还会在Unix系统上看到char **envp
,您可能看起来它们都以等价形式char *argv[]
和char *envp[]
) 。 这里的重点是使用它们为您的程序采用参数,这样您就不会只局限于硬编码的data.csv
和matrix.csv
文件名。 您可以使用硬编码的值,并且仍然向用户提供通过使用简单的ternary
运算符输入其选择的文件名的能力(例如test ? if true code : if false code;
):
FILE *filein_ptr = argc > 1 ? fopen (argv[1], "r") : fopen (FILEIN, "r");
FILE *fileout_ptr = argc > 2 ? fopen (argv[2], "w") : fopen (FILEOUT, "w");
此处, 测试 argc > 1
(表示用户至少提供了一个参数), 如果 open (argv[1], "r")
真实代码 open (argv[1], "r")
(打开了作为读取参数给出的文件名,并且返回了错误代码)如果没有给出文件名fopen (FILEIN, "r")
打开默认值。输出文件也是如此(必须以正确的顺序提供)。
然后,如果您打开一个文件,则在尝试读取该文件之前,必须验证该文件是否已实际打开。 虽然您可以分别测试输入和输出以判断哪个失败,但是也可以使用简单的||
检查是否打开失败的条件:
if (!filein_ptr || ! fileout_ptr) {
fprintf (stderr, "error: filein of fileout open failed.\n");
return 1;
}
最后,如果您知道需要读取的数据行数,则可以使用索引的for
循环就可以了,但是在使用之前您几乎不会知道数据文件中的行数。 即使使用for
循环,您仍然需要检查fscanf
的返回值,以验证您实际上进行了2次有效转换(因此获得了您期望的2个值)。 检查退货还提供了另一个好处。 它使您可以继续阅读,直到不再从fscanf
获得2次有效转换为止。 这提供了一种从文件读取未知数量的值的简便方法。 但是,您确实需要确保不要尝试将更多的值读入数组而无法容纳它们。 例如:
while (fscanf (filein_ptr, " %29[^,],%lf", teamname, &teamscore[n]) == 2) {
fprintf (fileout_ptr, "%s %f\n", teamname, teamscore[n++]);
if (n == MAXS) { /* check data doesn't exceed MAXS */
fprintf (stderr, "warning: data exceeds MAXS.\n");
break;
}
}
注意:当使用包含字符大小写的格式说明符(例如"%[^,], ..."
)时,请注意它将在转换为字符串的过程中读取并包括前导和尾随空格。 所以,如果你的文件有' Steelers ,..'
, teamname
将包括空白。 您可以通过在转换开始之前添加一个空格来修复前导空格(例如" %29[^,], ..."
),并通过指定最大字段宽度来限制可以读取的字符数。 (在这种情况下,尾部的空白将在读取后更容易修剪)
将所有部分放在一起,可以通过从用户处获取参数并验证文件和读取操作来使代码更灵活,更可靠:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#define FILEIN "data.csv"
#define FILEOUT "matrix.csv"
#define MAXS 32
int main (int argc, char **argv)
{
/* double nfl[MAXS][MAXS] = {{0}}; */
double teamscore[MAXS] = {0.0};
char teamname[30] = {0};
int n = 0;
FILE *filein_ptr = argc > 1 ? fopen (argv[1], "r") : fopen (FILEIN, "r");
FILE *fileout_ptr = argc > 2 ? fopen (argv[2], "w") : fopen (FILEOUT, "w");
if (!filein_ptr || ! fileout_ptr) {
fprintf (stderr, "error: filein of fileout open failed.\n");
return 1;
}
while (fscanf (filein_ptr, " %29[^,],%lf", teamname, &teamscore[n]) == 2) {
fprintf (fileout_ptr, "%s %f\n", teamname, teamscore[n++]);
if (n == MAXS) { /* check data doesn't exceed MAXS */
fprintf (stderr, "warning: data exceeds MAXS.\n");
break;
}
}
fclose (filein_ptr);
fclose (fileout_ptr);
return 0;
}
测试输入
$ cat ../dat/teams.txt
Steelers, 20
Patriots,25
Raiders, 15
Chiefs,35
注意:值之间的前导空白和空白的变化是有意的。
使用/输出
$ ./bin/teams ../dat/teams.txt teamsout.txt
$ cat teamsout.txt
Steelers 20.000000
Patriots 25.000000
Raiders 15.000000
Chiefs 35.000000
如果您还有其他问题,请告诉我。
如果要将团队名称存储在数组中,则应声明一个二维数组:
char team_names[N_OF_TEAMS][MAX_CHAR_IN_NAME];
然后,您声明得分的数组。 您正在使用双精度来存储分数,不是仅整数吗?
double scores[N_OF_TEAMS];
要读取这些值,可以使用:
int read_name_and_score( char * fname, int m, char nn[][MAX_CHAR_IN_NAME], double * ss)
{
FILE *pf;
int count = 0;
if (!fname) {
prinf("Error, no file name.\n");
return -1;
}
pf = fopen(fname,'r');
if (!pf) {
printf("An error occurred while opening file %s.\n",fname);
return -2;
}
while ( count < m && fscanf(pf, "%[^,],%d\n", nn[count], &ss[count]) == 2 ) count++;
if (!fclose(pf)) {
printf("An error occurred while closing file %s.\n",fname);
};
return count;
}
您需要[^,]来阻止scanf在找到时读取字符串,主要内容如下:
#define N_OF_TEAMS 32
#define MAX_CHAR_IN_NAME 30
int main(void) {
char team_names[N_OF_TEAMS][MAX_CHAR_IN_NAME];
double scores[N_OF_TEAMS];
int n;
n = read_name_and_score("data.csv",N_OF_TEAMS,team_names,scores);
if ( n != N_OF_TEAMS) {
printf("Error, not enough data was read.\n");
/* It's up to you to decide what to do now */
}
/* do whatever you want with data */
return 0;
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.