简体   繁体   中英

Input validation from redirected stdin in C

I am trying to write a programme in C for an assignment that detects CSV format eg. decimalcommadecimal. and gives a output as to if the file is in the required format or not. I have tried using various inputs from stdin and using isdigit etc. but to no success. I'm a mega noob and have barely done any C programming before, I attempted to use regexc but couldn't figure out the syntax for using it.

#include <ctype.h>
#include <stdio.h>
const char EOL = '\n';

int cbreak(void);
int check_dig(void);
int value =1;
char c;

int main()
{

    while((scanf("%c" ,&c)) !=EOF&& value !=0)
    check_dig();

    printf("\n%d\n",value);
}

int check_dig()
{
    if (c == EOL)
        scanf("%c", &c);
    if (c == isdigit)
        scanf("%c", &c);
    else if (c == ',')
        scanf("%c", &c);
    else value = 0;
}

Thanks Guys I'm now to this stage but stumped as how to finish, I need to printf either 1 or 0 depending on validation and I want to do this as suggested using a return value.

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

int check_digit(int);
int check_comma(int);
int skip_char(int);
int main()
{
    int c;

    while ((c = getchar()) !=EOF)
        if (check_digit(c))
            skip_char(c);
        else if (check_comma(c))
            skip_char(c);
        else return 0;
}

int check_digit(int c)
{   
    if (isdigit(c))
        return 1;
    else return 0;
}    

int check_comma(int c)
{
    if (c == ',')
        return 1;
    else return 0;
}

int skip_char(int c)
{
    c = getchar(); // will this skip 2 chars as i have a while loop that has c=getchar()??
    return c;
}

First off I'd recommend to not use that vast list of globals. Use parameters in functions and return values from functions.

Second isdigit does not work like that. It takes a parameter and return true or false. isdigit

Further I'd use getchar over scanf.

Your int check_dig() function is a bit strange. You keep reading characters inside the function.

I would perhaps do something like:

int valid_csv(char c)
{
    if (isdigit(c))
        return 1;
    /* etc. other checks */
    if all fails
    return 0;
}

int main(void)
{
    int c;

    /* read chars into c and call fun by something like */
    valid_svc(c);

    return 0;
}

Edit: As a rule of thumb. A function should do one thing, and one thing only, and do it well. The name of the function should reflect what it does.


Edit.2:

You do not need to "skip char". The way it goes, in your new code, is that you skip every other character.

Ie:

File: 12,33,66,14

In your code you'll get

  • c = getchar => c == 1
  • c is digit
    • getchar => you read 2 (and never validates it)
  • c = getchar => c == ,
  • c is comma
    • getchar => you read 3 (and never validates it)
  • ...

Further; I know I wrote "a function should do one thing" - but but not that literal. Ie your new check_digit is redundant. Use isdigit directly. If you have floats in your csv you'll have to expand or use a different approach.

To illustrate by example; easier then writing on here :)

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

int valid_csv_chr(int);
int valid_csv(); /* guess naming could be better. */

int main(void)
{
    if (valid_csv())
        puts("1");
    else
        puts("0");

    return 0; /* Main should return 0 if there was no "crash" scenario etc.
     * You could also return i.e. 1 if the file is not validated as csv. 
     * Do not think of 1 and 0 as boolean true / false here. */
}

int valid_csv()
{
    int c;

    while((c = getchar()) !=EOF) {
        if (!valid_csv_chr(c)) {
            return 0;
        }
    }

    return 1;
}

int valid_csv_chr(int c)
{
    if (isdigit(c))
        return 1;
    if (c == ',')
        return 1;
    if (c == '\n')
        return 1;
        /* add checks for space etc. */
    return 0;
}

Edit.3:

Code structure is something one has to learn just as much as the language itself. It is learning by doing and writing. That one realize that re-structure of code is necessary happens from time to time - but the more one think before writing, make a simple structure at first and expand it, etc. it is more avoidable.

Anyhow; practice, practice, practice. And keep these topics in mind at all time.

Even if it can "look" simple, it is not. I think often books, tutorials, courses etc. have all to little focus on this topic. It is all about for , if , functions etc. and all to little on how to stitch it all together in a good way.

There are several advantages of splitting up the code.

  • It makes it much more readable.
  • It makes it easier to maintain.
  • Bugs and errors can often be fixed by fixing a small function rather then a huge one.
    • On some cases I have seen code of thousands of lines where one have some monster functions that is buggy. Fixing by tweaking it is close to impossible and a complete re-write is only option.
  • It is easier to optimize a function that does one task, and is not to big.
  • It is easier to expand to cover more scenarios when using smaller functions.
    • Say for example in your program. You could change it to "validate data file" covering csv, tab delimited, aligned, etc.

The last point is actually a way I often think of it when writing; "How should I best implement this code so that, if, in the future, I'd like to expand it to cover more scenarios, it can be easily done."

Myself I use this as a base when writing in C combined with things I've learned in K & R's ANSI C book ++. See for example what is written about functions .

Also; being strict on coding style makes it much easier to read and maintain. I use in large part what is described in the document above. It is no law , but being conscious about it makes the coding life so much simpler.

I wouldn't recommend using regexc for this problem, especially if you're new to C. You should be able to solve it using some of the basic standard library features. You seem to be on the right general track. You are reading in characters and determining what class they fit in. Since you know that a valid input consists of only numbers and the comma character, you can immediately terminate the program if you encounter anything that's not one of those two things. Here are a few tips that might help.

If you're reading a character at a time, getchar might prove easier to use than scanf .

Also, isdigit is a function. Instead of saying if (c == isdigit) , you want to say if (isdigit(c)) .

Your function check_dig is defined to return an int , but there is no return statement in the function. The function needs to be modified to return a value, and main needs to do something with this value. A general rule of thumb is that a function returns zero when it completes successfully, or it returns non-zero on an error. In your case, "success" could mean "the character was valid" and "error" could mean "the character was invalid". Making this change should let you eliminate the global variable value . You can also eliminate the global c if you pass the input to check_dig as a parameter ( c would become a local variable inside main() ).

I recommend that you only use the check_dig function to check the digit, and that you remove the part that reads another character. You should make the decision whether or not to skip a character before calling check_dig . That way, you can separate your "read" code, your "skip" code, and your "check" code. This makes your program easier to read and debug.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM