复制.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
注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_包含数字的文本表示。 –