2017-02-26 44 views
1

我是新來學習C語言的人,我想編寫一個簡單的程序,用於從一個.csv文件複製數組整數文件複製到一個新的.csv文件。我的代碼按預期工作,但是當fread/fwrite的數組大小設置爲.csv數組中元素的精確數量(在本例中爲10)時,它只複製9個元素。複製.csv整數的C程序複製少一個元素,除非元素大小設置爲+1

當數組大小設置爲+1時,它將複製所有元素。

#include <stdio.h> 
#include <stdlib.h> 

#define LISTSIZE 11 
//program that copies an array of integers from one .csv to another .csv 

int main(int argc, char * argv[]) 
{ 
    if (argc != 2) 
    { 
     fprintf(stderr, "Usage ./file_sort file.csv\n"); 
     return 1; 
    } 
    char * csvfile = argv[1]; 
    FILE * input_csvile = fopen(csvfile, "r"); //open .csv  file and create file pointer input_csvile 
    if(input_csvile == NULL) 
    { 
     fprintf(stderr, "Error, Could not open\n"); 
     return 2; 
    } 
    unsigned int giving_total[LISTSIZE]; 
    if(input_csvile != NULL) //after file opens, read array from .csv input file 
    { 
     fread(giving_total, sizeof(int), LISTSIZE, input_csvile); 
    } 
    else 
    fprintf(stderr, "Error\n"); 


    FILE * printed_file = fopen("school_currentfy1.csv", "w"); 

    if (printed_file != NULL) 
    { 
     fwrite(giving_total, sizeof(int), LISTSIZE, printed_file); //copy array of LISTSIZE integers to new file 
    } 
    else 
    fprintf(stderr, "Error\n"); 

    fclose(printed_file); 
    fclose(input_csvile); 

    return 0; 




    } 

這是否與0索引的數組有關並且.csv文件是單索引的?我也有一個輸出,其中最後一個(10)元素顯示不正確,11的LISTSIZE; 480,而不是4800

http://imgur.com/lLOozrc輸出/輸入用10

http://imgur.com/IZPGwsA輸入/輸出具有11

+2

** CSV格式是文本,而不是二進制。**您正在複製可能因系統而異的字節數,但可能是11x4 = 44。由於您的測試數據恰好有3位數和4位數,因此44個字節就足夠了;嘗試一個包含11個重複1234567890的文件,它只會複製其中的4個文件。如果你想處理CSV,你必須把它看作變長文本,其中_sometimes_包含數字的文本表示。 –

回答

2

注LISTSIZE的LISTSIZE:如在評論指出,freadfwrite是用於讀取和寫入二進制數據,而不是文本。如果你正在處理一個.csv(逗號分隔值 - 例如從MS Excel中打開/ LibreOffice的鈣輸出),您將需要使用之後sscanffgets(或任何其它字符/字符串功能導向的)(或strtol,strtoul)以文本形式讀取值並執行轉換爲int值。要將值寫入輸出文件,請使用fprintf。 (fscanf也可用於輸入文本處理和轉換,但是你在處理輸入格式的變化失去彈性)

但是,如果你的目標是要讀二進制數據10整數(如數據40-bytes),然後freadfwrite都不錯,但是與全部爲輸入/輸出例程一樣,您需要驗證讀取和寫入的字節數,以確保您正在處理代碼中的有效數據。 (並且當你完成時你有一個有效的輸出數據文件)

根據格式,有很多方法可以讀取.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