1

I want to scanf input like: "John, Surname, 9999" and commas should not be assigned to the scanned variable; spaces at the end and start of input deleted... Now to structure student_t in a shape of p->name, would be assigned "John," with this comma, instead of "John". How to do it in a way that the user inserts comma like in input example, but it's not assigned to p->name? I don't even know how to encloth with words my point. I'm hammering my head with this for about 50 hours now.

struct student_t
{
    char name[20];
    char surname[40];
    int index;
};

struct student_t* read(struct student_t* p, int *err_code)
{
*err_code=0;

printf("Please enter data in a format: '[Name], [Surname], [index]':\n");

int c=scanf("%s, %s, %i", &p->name, &p->surname, &p->index);
return p;
}

void show(const struct student_t* p)
{
printf("%s %s, %i\n", p->name, p->surname, p->index);
}

example input:

Name, Surname, 9999

output to this example:

Name Surname, 9999

instead, my output is something like:

Name, , 0

so first "comma" typed by the user is assigned to p->name, and whitespace is p->surname, second comma is probably p->index which is of 'int' type, maybe that's why it's 0. If I do:

char comma1, comma2, space1, space2;
int c=scanf("%c%s%c%s%c%i%c", &space1, &p->name, &comma1, &p->surname, &comma2, &p->index, &space2);

output is:

ame, Surname,, 9999

edit1: I thank everyone from the bottom of my heart. Now, I didn't mention that I also want to handle errors, though it might cast more light whether approach to decide.

*err_code=

0 -- everything is loaded to the structure properly
1 -- something went wrong, eg. user did not use commas when typing in format:'[Name], [Surname], [index]'
2 -- only name loaded properly
3 -- name and surname loaded properly (index went wrong)

Given that, I think that handling these errors using scanf would be quite problematic.
In the topic I used "how to scanf" because I don't know how to properly express scanning data from user to structure.
Now, following Stephan Lechner's wisdom, I attend this approach in a way:

char buffer[1024], *pch1, *pch2;

if (fgets(buffer,1024, stdin))
{
    pch1=strchr(buffer, ',');
    pch2=strrchr(buffer, ',');
    if (pch1!=pch2 && pch1!=NULL)
    {
        char *name = strtok(buffer,","); // returns pointer to the beginning of the token
        if (name) { //the place where "," is occured becomes a "NULL" character
            sscanf(name," %19s", p->name);  // skip leading spaces
            char *surname = strtok(NULL,",");
            if (surname) {
                sscanf(surname," %39s", p->surname); // skip leading spaces
                char *index = strtok(NULL,",");
                if (index) {
                    p->index = (int)strtol(index, NULL, 10);
                           } //else *err_code=3;
                         } //else *err_code=2;
                  } 
    } else *err_code=1;
}
Immo
  • 19
  • 6
  • 1
    This will be much easier if you _don't_ use `scanf`. Use `fgets` and `strtok` instead. – zwol Jul 14 '18 at 20:42
  • `struct student_t` -> Please provide the declaration – Ed Heal Jul 14 '18 at 20:52
  • Any special reason to use scanf over other methods? – klutt Jul 14 '18 at 21:13
  • No, no reason for scanf over other methods. (I need to handle errors: 0--all good, 1--sth went wrong eg. no commas when typing, 2-- surname&index loaded wrong, 3-- only index loaded wrong). I used scanf because I didn't know other methods (I thought fgets is only for files). Thanks for help! – Immo Jul 15 '18 at 12:04

3 Answers3

4

scanf has several pitfalls, especially when using it for scanning several separated input fields using a single statement / format string. For example, the new line character is treated as a white space just as a blank, and this can achieve weird results when reading in multiple lines. Further, you'd probably want to protect your code from being broken by "too long" user input; so you'd write "%19s ...", but then the remainder of the input will be passed to the next format specifier...

These pitfalls let people tend to say "use fgets and parse the input on your own, e.g. by using strtok. The code gets "longer", but you get much more control over edge cases.

See the following code using such an approach:

int main()
{
    struct student_t s;
    struct student_t *p = &s;

    char buffer[1000];
    if (fgets(buffer,1000, stdin)) {
        char *name = strtok(buffer,",");
        if (name) {
            sscanf(name," %19s", p->name);  // skip leading spaces
            char *surname = strtok(NULL,",");
            if (surname) {
                sscanf(surname," %39s", p->surname); // skip leading spaces
                char *index = strtok(NULL,",");
                if (index) {
                    p->index = (int)strtol(index, NULL, 10);
                }
            }
        }
    }

    return 0;
}
Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58
  • what if input is a name consisting of multiple words divided by whitespace, eg. _Arnold, G. Deep, 999_? If I understand, sscanf puts to p->surname what's under surname: _G. Deep_, however stops as the whitespace is approached? – Immo Jul 16 '18 at 16:11
  • ah, using sscanf(surname," %39[^\t\n]", p->surname); seems to change that; firstly I wrote that it does not but I had a mess in my code. Here're my attempts to further use this. Seems to work. https://stackoverflow.com/questions/51360247/how-to-detect-if-user-inserts-data-with-commas-in-a-desired-format-or-not/51360844#51360844 Hell yes, officialy it works :D – Immo Jul 16 '18 at 16:48
3

You can specify to match everything except commas:

int c = scanf("%[^,], %[^,], %i", &p->name, &p->surname, &p->index);
r3mus n0x
  • 5,954
  • 1
  • 13
  • 34
1

Now that I have the declaration of struct student_t (and a glass of wine!)

It should be

 int c = scanf("%19[^,], %39[^,], %i", p->name, p->surname, &p->index);

And then check if c is 3

Ed Heal
  • 59,252
  • 17
  • 87
  • 127