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

时间:2015-11-17 15:56:43

标签: c file input

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

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

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

  • Steelers,20
  • 爱国者,25
  • 攻略,15
  • 首领,35

2 个答案:

答案 0 :(得分:1)

除了将&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,您可能看起来都是以等效形式char *argv[]和{{1}编写的}})。这里的要点是使用它们来为您的程序提供参数,这样您就不会遇到硬编码的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")打开你的默认文件,如果没有给出文件名。输出文件也是如此。(你必须提供正确的文件)顺序)。

然后,如果您打开文件,则必须在尝试从中读取文件之前验证该文件是否已实际打开。虽然您可以单独测试输入和输出以判断哪一个失败,但您也可以使用简单的fopen (FILEIN, "r")条件来检查是否打开失败:

||

最后,如果您知道需要读取的数据行数,那么您所拥有的索引 if (!filein_ptr || ! fileout_ptr) { fprintf (stderr, "error: filein of fileout open failed.\n"); return 1; } 循环就可以了,但您很少知道数据文件中的行数。即使使用for循环,您仍需要检查for的返回以验证您实际上有2次有效转换(因此得到了您期望的2个值)。检查退货还提供了另一个好处。它允许您继续阅读,直到您不再从fscanf获得2次有效转换。这提供了一种从文件中读取未知数量值的简便方法。但是,您确实需要确保不要尝试在数组中读取超过它们的值。 e.g:

fscanf

注意:当使用包含字符大小写的格式说明符(如 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

如果您还有其他问题,请与我们联系。

答案 1 :(得分:0)

如果要将团队名称存储在数组中,则应声明二维数组:

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