2

This program should ask you to add member (people) to a struct and print them on a file but after the first for loop just stop working and jump over the name part. I just found that thing that allow you to add space to a string, tried it but no success... I tried to remove it and it work without any problem so the [^\n] make something go wrong. What is wrong ?

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

struct Staff {
    char Surname[100];
    char Name[100];
    int age;
    char spec[100];
    int id;
} person;

void write();
void leggi();
void trova();

int main() {
    write();
}

void write() {
    int i = 0;
    int n = 1;
    int r;

    FILE *fp;
    fopen_s(&fp, "index.txt", "w+");
    if (fp == NULL) {
        printf("Failed to open file\n");         
        exit(1);
    }

    fprintf(fp, "%d\n", i);

    for (i = 0; i < n; i++) {
        printf("Surame:\n");
        scanf_s("%[^\n]s", person.Surname, 100);
        fprintf(fp, "%s\t\t", person.Surname);
                                             //loop just get over the name part 
        printf("Name:\n");                   //after the first loop
        scanf_s("%s", person.Name, 100);               
        fprintf(fp, "%s\t", person.Name);

        printf("Age:\n");                  
        scanf_s("%d", &person.age);
        fprintf(fp, "%d\t", person.age);

        printf("Specialization\n");
        scanf_s("%s", person.spec, 100);
        fprintf(fp, "%s\n", person.spec);

        printf("Want to enter another? 1=yes  0=no...\n");
        scanf_s("%d", &r);
        if (r == 1)
            n = n + 1;
    }

    rewind(fp);
    fprintf(fp, "%d\n", i);    

    fclose(fp);
}
user438383
  • 5,716
  • 8
  • 28
  • 43
Gab1Crazy
  • 23
  • 2
  • 5
    Who taught you to put `s` after `%[^\n]`? `[^\n]` is not a prefix modifier of `%s`, it's its own format specifier. – Barmar May 26 '22 at 22:02
  • 2
    You need a space before `%[^\n]` so that it will skip over the newline after the `0/1` answer to the `another?` question. – Barmar May 26 '22 at 22:05
  • 1
    Also don't use the non standard `scanf_s`, use `scanf` instead. – Jabberwocky May 26 '22 at 22:07
  • 1
    There must be lots of duplicates of this question, someone please close this as a dup. – Barmar May 26 '22 at 22:07
  • 4
    Just use `fgets` to get a line of user input. The `scanf` family is terrible at processing user input and should typically be avoided. – Cheatah May 26 '22 at 22:12
  • @Barmar Trouble is, there are 17 different things you can do wrong, so it's tough to know which of the 131,071 dups to link to. Is the problem here the missing ' ' in `% [^\n]`, or the extra `s`, or the proliferating extra confusions due to `_s`, or what? :-\ – Steve Summit May 26 '22 at 22:18
  • @Gab1Crazy The problem here — it's a cruel conspiracy against you and every other C learner; it's not your fault — is that `scanf` is merely *superficially* useful-looking as a way to read inputs from the user. Actually it's a shuddering pile of unseemly kludges, and it's nearly impossible to use correctly, *and* there's a universally-honored pact among all beginning C instructors *not* to teach you what to watch out for. As a renegade among those instructors, though, I'm willing to reveal the secrets: read [here](https://stackoverflow.com/questions/72178518#72178652). – Steve Summit May 26 '22 at 22:25
  • @SteveSummit In this case I'm certain it's the missing space. The extra `s` would be a problem if it were a format string with additional specifiers after the `s`. But `scanf()` doesn't keep parsing state between calls, so the `s` at the end of the first scan doesn't break later ones. I already have a saved dupe for missing space before `%c`, I hoped there would be a similar one for `%[^\n]`. – Barmar May 26 '22 at 22:34
  • @Jabberwocky "don't use the non standard scanf_s" --> `scanf_s()` is in the C17 spec, annex K. – chux - Reinstate Monica May 27 '22 at 02:12

2 Answers2

2

There are multiple problems in your code:

  • you use the so called secure functions fopen_s, scanf_s etc, but you do not check the return values to detect invalid input. You should instead use standard functions, pass the appropriate arguments and check the return values.

  • using scanf_s is actually non portable: the scanf_s function defined in Annex K of the C Standard requires the length argument after the pointer to have size_t type, whereas the function with the same name in the Microsoft library uses type UINT, which has a different representation on 64-bit versions of their Windows OS. A classical case of the Embrace, enhance and extinguish strategy. In Standard C, one should write: scanf_s("%s", person.Name, (size_t)100) or better:

    scanf_s("%s", person.Name, sizeof person.Name)

  • there is no need to open the output file for update with "w+", just use "w".

  • you rewind the stream pointer back to the beginning of file and overwrite the number of entries at the start of the file. This works as long as you have less than 10 entries, but beyond that, the number has more digits so some characters in the file will be corrupted. You could use a format with padding such as "%6d\n" which would allow for up to 1 million records without risks.

  • "%[^\n]s" is not a correct scanf format: you should just write "%[^\n]" or better " %99[^\n]" to skip initial white space and limit the input to 99 characters.

Here is a modified version:

#ifdef _MSC_VER
#define _CRT_SECURE_NO_WARNINGS
#endif

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

struct Staff {
    char Surname[100];
    char Name[100];
    int age;
    char spec[100];
    int id;
};

void write(void);
void leggi(void);
void trova(void);

int main() {
    write();
}

int flush_input(void) {
    int c;
    while ((c = getchar()) != EOF && c != '\n')
        continue;
    return c;
}

void write(void) {
    int n = 0;
    int r;

    FILE *fp = fopen("index.txt", "w");
    if (fp == NULL) {
        fprintf("Failed to open file index.txt: %s\n",
                strerror(errno));
        exit(1);
    }

    fprintf(fp, "%6d\n", n);

    for (;;) {
        struct Staff person = { 0 };

        printf("Surname:\n");
        if (scanf(" %99[^\n]", person.Surname) != 1)
            break;
        flush_input();
        fprintf(fp, "%s\t\t", person.Surname);
                                             //loop just get over the name part 
        printf("Name:\n");                   //after the first loop
        scanf(" %99[^\n]", person.Name);               
        flush_input();
        fprintf(fp, "%s\t", person.Name);

        printf("Age:\n");                  
        scanf("%d", &person.age);
        flush_input();
        fprintf(fp, "%d\t", person.age);

        printf("Specialization\n");
        scanf(" %99[^\n]", person.spec, 100);
        flush_input();
        fprintf(fp, "%s\n", person.spec);
        n++;

        printf("Want to enter another? 1=yes  0=no...\n");
        if (scanf("%d", &r) != 1 || r != 1) {
            flush_input();
            break;
        }
        flush_input();
    }

    rewind(fp);
    // update the entry count on 6 characters
    fprintf(fp, "%6d\n", n);

    fclose(fp);
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 2
    I don't know why, but I've noticed that almost all calls to `fopen()` in C questions and `open()` in Python questions use `+` in their modes, even though they mostly don't switch between reading and writing. There must be lots of bad teachers/tutorials that overuse this. – Barmar May 26 '22 at 22:36
  • @Barmar: I am appalled too, and given the peculiar semantics of update mode, this is very error prone. – chqrlie May 27 '22 at 12:55
0

Change the call of scanf below for entering strings by inserting a space in the beginning of the format string. For example instead of this call

scanf_s("%[^\n]s", person.Surname, 100);

(where the letter s must be removed from the format string) write

scanf_s(" %[^\n]", person.Surname, ( rsize_t )100);
        ^^^^^^^^

This allows to skip leading white space characters in the input buffer.

Pay attention to that changing the condition or the for loop the was as you are doing

for (i = 0; i < n; i++) {
//...
    if (r == 1)
        n = n + 1;
}

makes the code unclear. Instead of the for loop you could use do-while loop.

Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335