从csv文件中读取并分成变量

我正在尝试将输入值分为两个不同的类别。 第一个数组调用teamname将保留团队名称,第二个数组将保留该周的分数。 我的输入文件是.csv,其代码的方式是将所有内容存储在一个字符串而不是两个单独的变量中。 此外,我不打算精通程序,只熟悉图书馆。

#define _CRT_SECURE_NO_WARNINGS #include  #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; } 

我应该说输入文件的第一列包含团队名称,第二列包含团队分数。 任何帮助都会很棒。 谢谢! 这是一个示例输入文件

  • 钢人队,20
  • 爱国者,25
  • 攻略,15
  • 酋长,35

除了将&teamname更改为teamname ,您还可以考虑其他一些注意事项。 第一个是,始终初始化变量。 虽然不是必需的,但这有许多积极的好处。 对于数值数组,它初始化所有元素,防止意外读取未初始化的值。 对于字符数组,初始化为0确保字符串的第一个副本(小于总长度)将以null-terminated并且还可以防止尝试从未初始化的值读取。 这是个好习惯:

  double teamscore[MAXS] = {0.0}; char teamname[30] = {0}; int n = 0; 

您已经为filein_ptrfileout_ptr定义了默认值,您可以对数组大小执行相同的操作。 如果您的数组大小需要更改,则通过提供单个值来更新,从而使代码更易于维护。

接下来,这是一个非常重要的因素。 main接受参数,由标准定义为int argc, char **argv (您可能还会看到Unix系统上的char **envp ,您可能看起来都是以等效formschar *argv[]char *envp[] ) 。 这里的要点是使用它们来为您的程序提供参数,这样您就不会data.csv硬编码的data.csvmatrix.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") (打开文件名作为参数读取, 如果是假代码如果没有给出文件名fopen (FILEIN, "r")打开你的默认值。输出文件也是如此。(你必须按照正确的顺序提供它们)。

然后,如果您打开文件,则必须在尝试从中读取文件之前validation该文件是否已实际打开。 虽然您可以单独测试输入和输出以确定哪一个失败,但您也可以使用简单的|| 条件检查是否打开失败:

  if (!filein_ptr || ! fileout_ptr) { fprintf (stderr, "error: filein of fileout open failed.\n"); return 1; } 

最后,如果您知道需要读取的数据行数,那么索引for循环就没问题,但您很少知道数据文件中的行数。 即使使用for循环,您仍然需要检查fscanf的返回以validation您实际上有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[^,], ..." ),还可以通过指定最大字段宽度来限制可以读取的字符数。 (在阅读后,案例中的尾部空格会更容易修剪)

将所有部分组合在一起,您可以通过从用户获取参数并validation文件和读取操作来使代码更加灵活和可靠:

 #define _CRT_SECURE_NO_WARNINGS 1 #include  #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在找到a时读取字符串,主要是这样的:

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