繁体   English   中英

从csv文件读取并分成变量

[英]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;
}

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

  • 钢人队,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 ,您可能看起来它们都以等价形式char *argv[]char *envp[] ) 。 这里的重点是使用它们为您的程序采用参数,这样您就不会只局限于硬编码的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") 真实代码 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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM