当我运行代码时,它会出现分段错误。我只是想打开一个文件并读取其中的数据行。我很确定有什么我不理解的地方。如有任何帮助,我们将不胜感激。
#include <stdio.h>
#include <string.h>
void get_num_lines(const char *fname, int *rows);
int main (int argc, char* argv[]) {
int rows = 0;
char *ptr1 = NULL;
char str1 = '.csv';
ptr1 = strchr(argv[1], str1);
if(ptr1 != NULL){
const char *fname = argv[1];
get_num_lines(fname, &rows);
}
return(0);
}
void get_num_lines(const char *fname, int *rows)
{
FILE *fin;
fin = fopen(fname, "r");
printf("the input file name is %s", fname);
char line[256] = {0x0};
while(!feof(fin)){
fgets(line, 255, fin);
if(line != NULL){
rows++;
}
}
}
fclose(fin);
}
您的代码存在许多小但重要的问题。主要的问题是无法验证您的文件是否已打开以供阅读。如果未成功验证fopen
,则无法知道是否在下一次尝试读取无效fin
指针的调用中调用Undefined Behavior。
你被指向了解释为什么"while(!feof(file))"总是错的链接?只需验证fgets
的返回即可。
接下来,虽然将指向rows
的指针作为函数的参数传递是可以的,但您没有正确更新它。在get_num_lines
中,您尝试使用进行更新
rows++; /* this is wrong. this increments the address! (not the value) */
由于您传递了一个指针,您必须增加存储在该地址的值,而不是地址本身,例如
(*rows)++; /* note the use of (..) for correct C-operator precedence */
这就引出了一个更实际的问题,"为什么要传递指针?"为什么不直接使用get_num_lines
的有意义的返回,并简单地将行数返回给调用者?,例如size_t get_num_lines (FILE *fin)
注意:通常的做法是在调用函数(此处为main()
)中打开并验证文件是否已打开以供读取,并将FILE *
指针作为参数而不是文件名传递。传递文件名并在函数中处理所有文件并没有错,只是这不是一般的方法。
但是,不能简单地调用fgets
来计算文件中的行数。在增加行数之前,您必须验证该行是否适合您的缓冲区(例如,您读取了整行,而不是较长行的第一个254
字符)。为此,您需要检查fgets
读取的行的长度,并验证最后读取的字符是否为'n'
。
还有一个(不幸的是,这是一个常见的)问题,如果文件具有非POSIX文件结尾(意味着它缺少最终的'n'
),则会导致行数少1。这是正确验证最后一个字符是'n'
的副作用——这是计数函数正确操作所必需的。如果文件没有最终的'n'
,您对它的检查将失败,导致最后一行未计数。值得庆幸的是,这可以简单地通过设置一个标志来处理,该标志指示没有读取行尾,然后在离开fgets
读取循环后检查该标志是否已设置。
把这些部分放在一起,一个取一个打开的FILE*
指针,读取并返回当前行数的函数可以是:
size_t fgets_nlines (FILE *fp)
{
int noeof = 0;
size_t n = 0;
char buf[BUF_SIZE] = "";
while (fgets (buf, BUF_SIZE, fp)) { /* read until EOF */
size_t len = strlen (buf); /* get buf length */
if (len && buf[len-1] != 'n') { /* if not complete line */
noeof = 1; /* set flag no EOL found */
continue; /* read until all chars in line are read */
}
noeof = 0;
n++;
}
if (noeof) /* handle non-POSIX EOF (add 1 to count) */
n++;
return n;
}
POSIX提供的第二个面向行的函数是POSIXgetline
,它不需要文件末尾检查。它还具有分配足够的存储空间的优点——无论线路的长度如何。(这也可以被视为一个缺点)。你可以用类似的东西对getline
做同样的事情:
size_t getline_nlines (FILE *fp)
{
size_t lines = 0, n = 0;
char *buf = NULL;
while (getline (&buf, &n, fp) != -1)
lines++;
free (buf);
return lines;
}
使用其中一个(您必须调整函数名)的简短示例程序可以编写如下。它将要读取的文件名作为程序的第一个参数(如果没有给定参数,则默认情况下从stdin
读取)。它提供类似于Linux上的wc -l
的输出,如果将名称作为参数提供,则将读取的文件名将作为行计数输出的一部分附加,或者如果从stdin
读取,则仅输出行计数,例如
#include <stdio.h>
#include <stdlib.h> /* for free if using getline */
#include <string.h>
#ifndef BUF_SIZE /* fgets buffer size */
#define BUF_SIZE 8192
#endif
size_t fgets_nlines (FILE *fp); /* comment/uncomment as required */
// size_t getline_nlines (FILE *fp);
int main (int argc, char **argv) {
size_t nlines = 0;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) { /* validate file open for reading */
perror ("file open failed.");
return 1;
}
nlines = fgets_nlines (fp);
// nlines = getline_nlines (fp); /* same note, comment/uncomment */
if (nlines) {
if (argc > 1)
printf ("%zu %sn", nlines, argv[1]);
else
printf ("%zun", nlines);
}
if (fp != stdin) fclose (fp); /* close file if not stdin */
return 0;
}
仔细查看,思考所涉及的问题,以及fgets
和getline
如何处理非POSIX EOF之间的区别以及原因。如果你还有其他问题,请告诉我。
char str1 = '.csv';
在单引号中,应该只有字符r,但您正在分配多个字符。它将只分配最后一个字符。所以它会像char str1 = 'v'
一样处理;如果它符合你的目的,那么没有问题,否则修改为beelow
char *str1 = ".csv";
而不是将行与NULL进行比较,而是将fgets()的返回值与NULL进行比较。因为当fin到达EOF时,fgets()返回NULL。
ret = fgets(line, 255, fin);
if(ret != NULL){
rows++;
几件事:1.检查argc,并确保在程序exe旁边的列表1中有参数(argc>1)2.检查打开并在失败时退出