继续分段错误

时间:2017-11-12 06:47:17

标签: c segmentation-fault

当我运行代码时,它会产生分段错误。我只是试图打开一个文件并读取其中的数据行。我很确定有一些我不理解的东西。任何帮助将不胜感激。

    #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);

}

3 个答案:

答案 0 :(得分:3)

您的代码存在许多小但重要的问题。主要的一个是验证您的文件实际上是否已打开以供阅读。如果未验证fopen成功,则无法知道是否在下一次尝试从无效fin指针读取的调用中调用未定义行为。 / p>

您已被指向解释Why is “while ( !feof (file) )” always wrong?的链接。只需要验证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 */

这提出了一个更实际的问题,&#34;为什么要传递一个指针?&#34;为什么不只是使用有意义的get_num_lines返回并简单地将行数返回给调用者? size_t get_num_lines (FILE *fin)

注意:通常的做法是打开并验证文件是否已打开以便在调用函数(此处为main())中读取并传递FILE *指针作为参数而不是文件名。传递一个文件名并在函数中处理它并没有错,它只不是一般方法。

但是,您不能简单地调用fgets来计算文件中的行数。在增加行数之前,必须验证行是否适合缓冲区(例如,您读取整行而不是更长行的第一个254字符)。为此,您需要检查fgets读取的行的长度,并验证读取的最后一个字符是'\n'

还有一个(不幸的是常见的)问题会导致你的行计数太少,只有1 如果文件有一个非POSIX文件结尾(意味着它缺少最后一个) '\n')。这是正确验证最终字符的副作用'\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提供的第二个面向行的函数不需要文件结束检查是POSIX getline。它还具有分配足够存储空间的好处 - 无论线路长度如何。 (这也可以被视为一个缺点)。您可以使用与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 %s\n", nlines, argv[1]);
        else
            printf ("%zu\n", nlines);
    }

    if (fp != stdin) fclose (fp);   /* close file if not stdin */

    return 0;
}

仔细研究,考虑所涉及的问题,以及fgetsgetline处理非POSIX EOF的原因与原因之间的区别。如果您有任何其他问题,请与我们联系。

答案 1 :(得分:1)

char str1 = '.csv';               
单引号 应该仅限字符,但您要分配多个。它只会分配最后一个字符。所以它会像char str1 = 'v'那样对待;如果它服务于你的目的那么没有问题,否则修改为beelow

 char *str1 = ".csv";

而不是将 NULL 进行比较,将fgets()的返回值与NULL进行比较。因为当fin到达EOF时fgets()返回NULL。

ret = fgets(line, 255, fin);
        if(ret != NULL){
        rows++; 

答案 2 :(得分:0)

很少的事情: 1.检查argc并确保在程序exe旁边有列表1参数(argc&gt; 1) 2.检查fopen并在失败时退出