注LISTSIZE的LISTSIZE:如在評論指出,fread
和fwrite
是用於讀取和寫入二進制數據,而不是文本。如果你正在處理一個.csv
(逗號分隔值 - 例如從MS Excel中或打開/ LibreOffice的鈣輸出),您將需要使用之後sscanf
fgets
(或任何其它字符/字符串功能導向的)(或strtol
,strtoul
)以文本形式讀取值並執行轉換爲int
值。要將值寫入輸出文件,請使用fprintf
。 (fscanf
也可用於輸入文本處理和轉換,但是你在處理輸入格式的變化失去彈性)
但是,如果你的目標是要讀二進制數據10
整數(如數據40-bytes
),然後fread
和fwrite
都不錯,但是與全部爲輸入/輸出例程一樣,您需要驗證讀取和寫入的字節數,以確保您正在處理代碼中的有效數據。 (並且當你完成時你有一個有效的輸出數據文件)
根據格式,有很多方法可以讀取.csv
文件。一種通用的方法是簡單地用fgets
來讀取每行文本,然後重複呼叫sscanf
來轉換每個值。 (這與相比,在處理','
周圍不同間距時具有許多優點)您只需讀取每行,將指針指向由fgets
讀取的緩衝區的開頭,然後調用sscanf
(用%n
返回字符數由每次調用處理),然後將該指針向前移動到緩衝區中,直到遇到下一個'-'
(用於負值)或數字。 (使用%n
和向前掃描可以讓fscanf
以類似的方式使用。)例如:
/* read each line until LISTSIZE integers read or EOF */
while (numread < LISTSIZE && fgets (buf, MAXC, fp)) {
int nchars = 0; /* number of characters processed by sscanf */
char *p = buf; /* pointer to line */
/* (you should check a whole line is read here) */
/* while chars remain in buf, less than LISTSIZE ints read
* and a valid conversion to int perfomed by sscanf, update p
* to point to start of next number.
*/
while (*p && numread < LISTSIZE &&
sscanf (p, "%d%n", &giving_total[numread], &nchars) == 1) {
numread++; /* increment the number read */
p += nchars; /* move p nchars forward in buf */
/* find next digit in buf */
while (*p && *p != '-' && (*p < '0' || *p > '9'))
p++;
}
}
我們創建輸出文件,只需寫numread
值回了逗號分隔值格式。(你可以調整你的每行有多少寫在需要時)
for (i = 0; i < numread; i++) /* write in csv format */
fprintf (fp, i ? ",%d" : "%d", giving_total[i]);
fputc ('\n', fp); /* tidy up -- make sure file ends with '\n' */
然後,它僅僅是一個關閉你的輸出文件,並檢查任何流錯誤(總是在接近檢查時寫值到文件的事)
if (fclose (fp)) /* always validate close after write to */
perror("error"); /* validate no stream errors occurred */
把它完全,你可以做類似如下的內容:
#include <stdio.h>
#include <stdlib.h>
#define LISTSIZE 10
#define MAXC 256
int main(int argc, char *argv[])
{
if (argc < 3) {
fprintf(stderr, "Usage ./file_sort file.csv [outfile]\n");
return 1;
}
int giving_total[LISTSIZE]; /* change to int to handle negative values */
size_t i, numread = 0; /* generic i and number of integers read */
char *csvfile = argv[1],
buf[MAXC] = ""; /* buffer to hold MAXC chars of text */
FILE *fp = fopen (csvfile, "r");
if (fp == NULL) { /* validate csvfile open for reading */
fprintf(stderr, "Error, Could not open input file.\n");
return 2;
}
/* read each line until LISTSIZE integers read or EOF */
while (numread < LISTSIZE && fgets (buf, MAXC, fp)) {
int nchars = 0; /* number of characters processed by sscanf */
char *p = buf; /* pointer to line */
/* (you should check a whole line is read here) */
/* while chars remain in buf, less than LISTSIZE ints read
* and a valid conversion to int perfomed by sscanf, update p
* to point to start of next number.
*/
while (*p && numread < LISTSIZE &&
sscanf (p, "%d%n", &giving_total[numread], &nchars) == 1) {
numread++; /* increment the number read */
p += nchars; /* move p nchars forward in buf */
/* find next digit in buf */
while (*p && *p != '-' && (*p < '0' || *p > '9'))
p++;
}
}
if (numread < LISTSIZE) /* warn if less than LISTSIZE integers read */
fprintf (stderr, "Warning: only '%zu' integers read from file", numread);
fclose (fp); /* close input file */
fp = fopen (argc > 2 ? argv[2] : "outfile.csv", "w"); /* open output file */
if (fp == NULL) { /* validate output file open for writing */
fprintf(stderr, "Error, Could not open output file.\n");
return 3;
}
for (i = 0; i < numread; i++) /* write in csv format */
fprintf (fp, i ? ",%d" : "%d", giving_total[i]);
fputc ('\n', fp); /* tidy up -- make sure file ends with '\n' */
if (fclose (fp)) /* always validate close after write to */
perror("error"); /* validate no stream errors occurred */
return 0;
}
就像我說的,還有很多,人y方法來解決這個問題。這個想法是爲了儘可能的提高你的閱讀靈活性,這樣它就可以處理輸入格式的任何變化而不會窒息。處理讀取的另一個非常穩健的方法是使用strtol
(或對於無符號值使用strtoul
)。這兩個允許都會提示一個指針,指向轉換整數後面的下一個字符,以便您可以從那裏開始掃描下一個數字。
以下顯示了這兩種方法中的任何一種讀取靈活性的示例。讀取任意行數的文件,用任何分隔符分隔值,並將遇到的每個整數轉換爲數組中的值,例如
例輸入
$ cat ../dat/10int.csv
8572, -2213, 6434, 16330, 3034
12346, 4855, 16985, 11250, 1495
示例程序使用
示例輸出文件
$ cat dat/outfile.csv
8572,-2213,6434,16330,3034,12346,4855,16985,11250,1495
嚕如果你有任何問題,請告訴我。如果您的意圖是以二進制形式閱讀40-bytes
,請讓我知道,我很樂意爲您提供示例幫助。
如果您想要真正通用讀取文件中的值,您可以調整找到輸入文件中的數字以便在文件中向前掃描的代碼,並驗證任何'-'
後面都有數字。這允許讀取任何格式並簡單地從文件中挑選整數。例如用下面的輕微變化:
while (*p && numread < LISTSIZE) {
if (sscanf (p, "%d%n", &giving_total[numread], &nchars) == 1)
numread++; /* increment the number read */
p += nchars; /* move p nchars forward in buf */
/* find next number in buf */
for (; *p; p++) {
if (*p >= '0' && *p <= '9') /* positive value */
break;
if (*p == '-' && *(p+1) >= '0' && *(p+1) <= '9') /* negative */
break;
}
}
您可以輕鬆地處理以下文件並獲得相同的結果:
$ cat ../dat/10intmess.txt
8572,;a -2213,;--a 6434,;
a- 16330,;a
- The Quick
Brown%3034 Fox
12346Jumps Over
A
4855,;*;Lazy 16985/,;a
Dog.
11250
1495
示例程序使用
$ ./bin/fgetscsv ../dat/10intmess.txt dat/outfile2.csv
輸出示例文件
$ cat dat/outfile2.csv
8572,-2213,6434,16330,3034,12346,4855,16985,11250,1495
** CSV格式是文本,而不是二進制。**您正在複製可能因系統而異的字節數,但可能是11x4 = 44。由於您的測試數據恰好有3位數和4位數,因此44個字節就足夠了;嘗試一個包含11個重複1234567890的文件,它只會複製其中的4個文件。如果你想處理CSV,你必須把它看作變長文本,其中_sometimes_包含數字的文本表示。 –