0

I was attempting to read a CSV file with the readCsv() function defined below. I happened to observe that when the readCsv() function is called within the main() function, I end up experiencing a runtime error and the function readCsv() fails to work properly. By contrast, when I instead rename the readCsv() function to main() (of course having commented the main() function first), the program works perfectly. But now I'm stuck because I need to be able to call the readCsv() function within main().

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>

int readCsv(void)
{
    int n = 0;
    while(n == 0)
    {
        do{
            printf("Enter the index of the student you want to query:");
            scanf("%i",&n);

            while(getchar() != '\n')
            {
                continue;
            }            
        }while((isdigit(n)) != 0);
    }

    n++;
    FILE* fp;
    fp = fopen("students.csv","r+");
    if ((fp == NULL)) exit(1);

    char buffer[1024];
    
    int row,column = 0;

    while(fgets(buffer,1024,fp))
    {
        column = 0;
        row++;

        if(row != n)
        {
            continue;
        }
        else if (row == n)
        {
            char* value = strtok(buffer,",");

            while(value)
            {

                if (column == 0)
                {
                    printf("\nIndex:");
                }

                if (column == 1)
                {
                    printf("\tName:");
                }

                if (column == 2)
                {
                    printf("\tAge:");
                }

                if (column == 3)
                {
                    printf("\tphone:");
                }                        

                printf("%s",value);
                value = strtok(NULL,",");
                column++;
            }
            printf("\n");
            break;
        }

        else 
        {
            printf("None of the conditions are true");
            break;
        }
    }
    fclose(fp);

    return 0;
}

void main(void)
{

    readCsv();
} 
Eric 222
  • 1
  • 3
  • `isdigit(n)` is meaningless. `n` is a number, that was read using format specifier `%i`, not a character. If you want to check errors, you need to check the return value of `scanf`, and then clear the bad input using one of the methods described in [this question](https://stackoverflow.com/questions/7898215/how-to-clear-input-buffer-in-c). Or use a different input method (like `fgets/sscanf` comination) – Eugene Sh. Jul 22 '22 at 21:56
  • can you post your input file please. Also, what exactly goes wrong? WHat error do you see? – pm100 Jul 22 '22 at 22:02
  • 3
    Please pay attention to obvious compiler warnings: there is nothing unclear about *"uninitialized local variable 'row' used"* in `row++;` The code has undefined behaviour. The line `int row,column = 0;` only initialises `column`. – Weather Vane Jul 22 '22 at 22:03
  • What's that lonely `n;` statement for? – Jens Jul 22 '22 at 22:07
  • @WeatherVane - dang, missed that gcc doesnt report it b y default. I always think that the defaults should be -Wall -Werror, this would really help newcomers – pm100 Jul 22 '22 at 22:08
  • @pm100 that's from MS VC `-W3` I find anything higher a bit intrusive. – Weather Vane Jul 22 '22 at 22:10
  • The code asks for 'index', but then counts rows of input (with uninitialized int row). This might only work when the records' index values exactly match their row in the file. – Fe2O3 Jul 22 '22 at 22:12
  • Thank you, I appreciate your responses. Apparently the issue was caused by initializing int row,column = 0, which only initialized column to zero as pointed out by Weather Vane – Eric 222 Jul 22 '22 at 22:17
  • More about `while((isdigit(n)) != 0);` says "don't accept the numeric input if it is the character code (say ASCII) 48 to 57". Perhaps you wanted to check `while(n < 0 || n > ??)` – Weather Vane Jul 22 '22 at 22:18
  • The while((isdigit(n)) != 0); is to keep the loop running until and only until a digit is entered otherwise it will keep requesting for user input. – Eric 222 Jul 22 '22 at 22:22
  • The user input with `%i` or `%d` isn't taken as a **character digit**, but as **integer**. As explained, the input `1` to an integer passed to `isdigit` returns `false` not `true`. – Weather Vane Jul 22 '22 at 22:26
  • 1
    Aside: don't use `%i` for integer input unless you want to explictly allow octal and hexadecimal input. Otherwise always use `%d` to prevent uninformed users entering say `012` and expecting to get decimal 12 but actually being octal for decimal 10. – Weather Vane Jul 22 '22 at 22:30
  • Note that the loop `while (getchar() != '\n') { continue; }` is flawed; it will go infinite if `getchar()` returns EOF before encountering a newline (which can happen, though it requires some care to trigger the problem). Be safe: `int c; while ((c = getchar()) != EOF && c != '\n') { continue; }` would be better. – Jonathan Leffler Jul 22 '22 at 22:35
  • Sorry Weather Vane, would you please elaborate on your most recent response. – Eric 222 Jul 22 '22 at 22:36
  • 1
    Use `%d` to read a decimal integer; use `%x` to read a hexadecimal integer (possibly prefixed by `0x`); use `%o` to read an octal integer (possibly prefixed by `0`). Only use `%i` when you want to recognize `1234` as decimal, `01234` as octal, and `0x1234` as hexadecimal. Using `%i` gives maximum flexibility, but can surprise people when entering dates (for example). If you type `08/09/22` and use the `scanf()` format `"%i/%i/%i"`, you won't get much of use, whereas using `"%d/%d/%d"` will work sanely. – Jonathan Leffler Jul 22 '22 at 22:40
  • Thanks @JonathanLeffler. Eric222 did you get the point about `isdigit`? It is for testing a *character* not an integer (although it takes an integer agument - as characters are). This `isdigit(1)` returns `false` because `1` isn't a character digit value. But `isdigit('1')` or `isdigit(49)` returns `true` because that character is a digit (ASCII). – Weather Vane Jul 22 '22 at 22:45
  • Note that `isdigit(n)` will return a non-zero value when `n` is in the range `48..57`. That's because it expects to be given an integer that represents the ASCII code for a character, and on most systems (all the ones you're likely to encounter — unless you work on IBM mainframes, perhaps), the values for the digits are `48` (`'0'`) through `57` (`'9'`). – Jonathan Leffler Jul 22 '22 at 22:50
  • Okay, now I understand how isdigit() works. Thank you for the much clear explanation Jonathan. – Eric 222 Jul 22 '22 at 22:50
  • See also [What should `main()` return in C and C++?](https://stackoverflow.com/q/204476/15168) – Jonathan Leffler Jul 22 '22 at 22:58

1 Answers1

0

It's easy to get lost when one function tries to do everything: file open/close, user input, read from file, search for target, output results, handle problems.

Here's a snippet of your function modified to demonstrate how these operations may be handled in a more obvious (imho) sequence.

bool found = false;
while( !found && fgets(buffer,1024,fp) )
{
    if( atoi( buffer ) == n ) // 'Index' is first value of each row
        found = true;
}
fclose(fp);

if( !found )
{
    printf("None of the conditions are true");
    return 0;
}

int column = 0;
char* value = strtok(buffer,",");

/*
    output fields of this row that are in buffer[]
    Or, pass buffer[] to a separate function that prints the fields.
*/
return 1;
Fe2O3
  • 6,077
  • 2
  • 4
  • 20