复制.csv整数的C程序复制少一个元素,除非元素大小设置为+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_包含数字的文本表示。 –

注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