[英]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.