簡體   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