-4

When I run the code, it gives a segmentation fault. I'm just trying to open a file and read the rows of data in it. I am pretty sure there is something I am not understanding. Any help would be appreciated.

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

}

jruo
  • 3
  • 1
  • The file I am trying to read is a .csv entered through the command line. – jruo Nov 12 '17 at 06:48
  • What if `fopen` can't open the file and returns a null pointer? And please take some time to read [How to debug small programs](https://ericlippert.com/2014/03/05/how-to-debug-small-programs/) by Eric Lippert, and learn how to use a debugger to catch crashes like this. – Some programmer dude Nov 12 '17 at 06:49
  • 2
    Also, you should check what [`fgets`](http://en.cppreference.com/w/c/io/fgets) *returns*. The array `line` will decay to a pointer to its first element and that will *never* be a null pointer. Lastly, please read [Why is “while ( !feof (file) )” always wrong?](https://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong) – Some programmer dude Nov 12 '17 at 06:50
  • The datafile and how you run the program and the error message if any are kind of relevant! – Antti Haapala -- Слава Україні Nov 12 '17 at 07:38

3 Answers3

3

There are a number of small but important issue with your code. The primary one is a failure to validate your file is actually open for reading. If you do not validate fopen succeeds, you have no way of knowing if you are invoking Undefined Behavior on the next call that attempts to read from an invalid fin pointer.

You have been pointed to the link explaining Why is “while ( !feof (file) )” always wrong? Validating the return of fgets is all that is required.

Next, while it is fine to pass a pointer to rows as a parameter to your function, you are not updating it correctly. In get_num_lines you attempt update with:

rows++;     /* this is wrong. this increments the address! (not the value) */

Since you passed a pointer, you must increment the value stored at that address, not the address itself, e.g.

(*rows)++;   /* note the use of (..) for correct C-operator precedence */

That brings up a more practical question, "Why pass a pointer at all?" Why not just make use of a meaningful return of get_num_lines and simply return the number of lines to the caller?, e.g. size_t get_num_lines (FILE *fin)

note: the general practice is to open and validate that the file is open for reading in the calling function (main() here) and pass a FILE * pointer as a parameter rather than a filename. Passing a filename and handling it all in the function is not wrong, it's just not the general approach.

However, you cannot simply call fgets to count the number of lines in a file. You must validate that the line fit in your buffer (e.g. that you read an entire line and just not the first 254 chars of a longer line) before you increment the line count. To do that you need to check the length of the line read by fgets and validate that the last character read was the '\n'.

There is one more (unfortunately common) problem that will cause your line-count to be too few by 1 if the file has a non-POSIX end of file (meaning it is missing the final '\n'). This is a side-effect of properly validating the final character is a '\n' -- required for the proper operation of your count function. If the file does not have a final '\n' your check for it will fail causing the last line to go uncounted. Thankfully this is handled simply by setting a flag indicating that no end of line was read, and then checking if the flag is set after leaving the fgets read loop.

Putting those pieces altogether, a function taking an open FILE* pointer, reading and returning the number of lines present could be:

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;
}

A second line oriented function provided by POSIX that does not require the end of file check is POSIX getline. It also has the benefit of allocating sufficient storage -- regardless of the length of the line. (which can also be viewed as a drawback as well). You can do the same thing with getline with something similar to:

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;
}

A short example program using either (you have to adjust the function names) could be written as follows. It takes the filename to read from as the first argument for the program (or reads from stdin by default if no argument is given) It provides output similar to wc -l on Linux, appending the filename read as part of the line-count output if the name was provided as an argument, or simply outputs the line-count alone if reading from stdin, e.g.

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

Look things over, think about the issues involved, and the difference between how fgets and getline handle a non-POSIX EOF and why. Let me know if you have any further questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
1
char str1 = '.csv';               

in single quotation there should be only character but you are assigning multiple. it will assign only last character. So it will treat like char str1 = 'v' ; if its serving your purpose then no problem otherwise modify as beelow

 char *str1 = ".csv";

and instead of comparing line with NULL, compare return value of fgets() with NULL. Because when fin reached EOF fgets() returns NULL.

ret = fgets(line, 255, fin);
        if(ret != NULL){
        rows++; 
Achal
  • 11,821
  • 2
  • 15
  • 37
0

Few things: 1. Check argc and make sure you have at list 1 argument beside program exe (argc >1) 2. Check fopen and exit upon fail

Elad Hazan
  • 331
  • 2
  • 7