0

I have to program a new file in which I have to have multiple student info (like: Student_name, student_Surname, school_subject and number of student) in one line and I have to type in new students until I input END.

I have to use printf and scanf. Name, surname and subject can be multiple words When I try to use scanf("[^\n]*c", name), I can only enter info for one student and loop just ignores rest and for other students I can just type in student number which is integer.

What is wrong with my code?

int main() {
    FILE *outputfile = NULL;

    struct imenik {
        char prezime[17 + 1];
        char ime[13 + 1];
        char predmet[20 + 1];
        int bodovi;
    } ucenik;

    outputfile = fopen("imenik.txt", "w");   

    printf("Ucitaj ime ucenika: ");
    scanf("%[^\n]%*c", ucenik.ime);

    printf("Ucitaj prezime ucenika: ");
    scanf("%[^\n]%*c", ucenik.prezime);

    printf("Ucitaj predmet: ");
    scanf("%[^\n]%*c", ucenik.predmet);

    printf("\nUcitaj broj bodova (0-50): ");
    scanf("%d", &ucenik.bodovi);

    fprintf(outputfile, "%s | %s | %s | %d\n", ucenik.ime, ucenik.prezime, ucenik.predmet, ucenik.bodovi);
    fclose(outputfile);    

}
Increasingly Idiotic
  • 5,700
  • 5
  • 35
  • 73
DinoCro
  • 1
  • 3
  • 5
    `I have to program a new file`...well, thate's a problem... – Sourav Ghosh Jun 01 '17 at 07:53
  • Possible duplicate of [Why am I getting a segmentation fault from scanf loop?](https://stackoverflow.com/questions/40327254/why-am-i-getting-a-segmentation-fault-from-scanf-loop) – Badda Jun 01 '17 at 07:56
  • 1
    Show your code or we can't help you. – Badda Jun 01 '17 at 07:56
  • try using scanf(" %[^\n]",name); – Pushan Gupta Jun 01 '17 at 07:59
  • 3
    Looking at the code, I would suggest you need to put a loop in. – JeremyP Jun 01 '17 at 08:02
  • 2
    Using english variable names makes it a lot easier for users of SO to help you – Mangs Jun 01 '17 at 08:03
  • If I use scanf(" %[^\n]",name); it allows me to enter multiple names but prints just first row I've entered... – DinoCro Jun 01 '17 at 08:04
  • 1
    I've had infinite for loop which breaks when I input end, but it didn't worked as it should so I temporararily removed it – DinoCro Jun 01 '17 at 08:05
  • 1
    Why don't you use `fgets()` to read a line, instead of `scanf()`? – Barmar Jun 01 '17 at 08:10
  • 1
    @DinoCro Post the code that has the problem. – Barmar Jun 01 '17 at 08:14
  • 2
    @DinoCro There is no loop in your code. I think you are missing on several things. First, a loop. Second, an array of your struct. I mean you want to store the values of different students into the same array? Please update the question making it real clear on what you actually need. By looking at your question, I think that you want to input the info of different students and store them into a file. Is that right? – Pushan Gupta Jun 01 '17 at 08:15
  • @DinoCro Also please when you want to poke someone from the comments, use the @"username". Its hard to get notified else – Pushan Gupta Jun 01 '17 at 08:17
  • 1
    Note that you should (1) limit the amount of space that is written by using, for example, `%17[^\n]%*c` to limit the input to 17 characters plus null byte, (2) you should test the result of `scanf()`, (3) you could check that you get a newline by changing `%*c` to `%c` and passing a suitable `char *` to receive the data and then testing the result, removing extra data if you don't get a newline with `int c; while ((c = getchar()) != EOF && c != '\n') ;`. However, it rapidly reaches the point where you're better off using `fgets()` (or POSIX `getline()`) to read a line and then parse that. – Jonathan Leffler Jun 01 '17 at 09:09
  • `scanf()` returns a value. Why does code not check it like with `if (scanf("%[^\n]%*c", ucenik.ime) != 1) { puts("Fail"); return 1; }`? – chux - Reinstate Monica Jun 01 '17 at 15:59

3 Answers3

1

The problem is here:

scanf("%d", &ucenik.bodovi);

This reads the number, but it doesn't read the newline after it. So when the loop repeats, It reads that newline as an empty line of input for the next student name.

You can change it to:

scanf("%d ", &ucenik.bodovi);

The space tells it to skip over any whitespace after the number.

But actually, it's better to put the space at the beginning of each scanf, rather than ignoring the newline at the end. See http://stackoverflow.com/questions/19499060/what-is-difference-between-scanfd-and-scanfd for the explanation. So change it to:

printf("Ucitaj ime ucenika: ");
scanf(" %[^\n]", ucenik.ime);

printf("Ucitaj prezime ucenika: ");
scanf(" %[^\n]", ucenik.prezime);

printf("Ucitaj predmet: ");
scanf(" %[^\n]", ucenik.predmet);

printf("\nUcitaj broj bodova (0-50): ");
scanf("%d", &ucenik.bodovi);
Barmar
  • 741,623
  • 53
  • 500
  • 612
  • There seems to be no loop in OP's question. First problem is that. – Pushan Gupta Jun 01 '17 at 08:18
  • @VidorVistrom He said in a comment that he removed it from the post. – Barmar Jun 01 '17 at 08:18
  • He shouldn't have. Its like... You go to a doctor and tell him my wife is having a toothache. Prescribe some medicine please – Pushan Gupta Jun 01 '17 at 08:20
  • @VidorVistrom I know he shouldn't have. I said so in my comment above: "Post the code that has the problem". – Barmar Jun 01 '17 at 08:24
  • 2
    See [Trailing blank in `scanf()` format string wreaks havoc on UI](http://stackoverflow.com/questions/19499060/what-is-difference-between-scanfd-and-scanfd). – Jonathan Leffler Jun 01 '17 at 09:03
  • @JonathanLeffler Good point. I've updated the answer to show using a space at the beginning instead. – Barmar Jun 01 '17 at 09:07
  • …but a `%d` format (indeed, any conversion specification except `%c`, `%[…]` (scan set) or `%n`) skips leading space, so an explicit leading blank isn't needed except before one of the trio. – Jonathan Leffler Jun 01 '17 at 09:13
  • Good point, I was just doing it for consistency, but it's not needed there. – Barmar Jun 01 '17 at 09:20
1

I suggest you an implementation like this:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define S_SIZE 32
#define T_SIZE 128
int main(void) {
    FILE *outputfile = NULL;

    struct imenik {
        char prezime[S_SIZE];
        char ime[S_SIZE];
        char predmet[S_SIZE];
        int bodovi;
    } ucenik;

    outputfile = fopen("imenik.txt", "a");
    if (outputfile == NULL) {
        perror("Fopen");
        exit(EXIT_FAILURE);
    }
    char tmp[T_SIZE];

    while (1) {
        printf("Enter info separated with spaces: ");
        fgets(tmp, T_SIZE, stdin);
        if (strcmp(tmp, "END\n") == 0) {
            break;
        }
        sscanf(tmp, "%s %s %s %d", ucenik.ime, ucenik.prezime, ucenik.predmet, &ucenik.bodovi);
        fprintf(outputfile, "%s | %s | %s | %d\n", ucenik.ime, ucenik.prezime, ucenik.predmet, ucenik.bodovi);
    }
    fclose(outputfile);

    return 0;
}
Overflow 404
  • 482
  • 5
  • 20
  • Is the use of `strncmp` instead of `strcmp` intentional? In case you don't know it would mean that entering "ENDD\n", "END1654\n" or even "END31d6546esf16sdf687\n" would make no difference with "end" since only the 3 first characters are evaluated. Shouldn't such cases be evaluated as errors? If yes, i recommend using `strcmp(tmp, "end\n)"` – nounoursnoir Jun 01 '17 at 09:13
  • Agree that `fgets()` and `sscanf()` are a better option, but OP said "I have to use printf and scanf." And doesn't this solution only allow for single words? OP says, "Name, surname and subject can be multiple words." – ad absurdum Jun 01 '17 at 09:32
  • @nounoursnoir yes you're right, I wrote in hurry. Edited. – Overflow 404 Jun 01 '17 at 10:08
  • Better to use `"%31s %31s %31s %d"`. Still the `fgets()` approach is best for reading _lines_ of information like in this answer. Note: OP did not say `fgets()` could not be used. ;-) – chux - Reinstate Monica Jun 01 '17 at 15:55
0

Your line:

scanf("%d", &ucenik.bodovi);

leaves a newline in the input stream. This gets picked up by the next call to scanf(), which immediately exits, also leaving the newline behind, and so on. Do not try adding a trailing whitespace character to the format string, as some suggest: "%d ". This will consume the newline at the end of your input, and wait for more input, until a non-whitespace character or EOF is encountered.

The easiest solution is to do what you have already been doing to discard newlines:

scanf("%d%*c", &ucenik.bodovi);

Note that you should specify a maximum width in format strings when using scanf() to read into a string to avoid buffer overflow:

scanf("%13[^\n]%*c", ucenik.ime);

Also, you should be checking outputfile to be sure that the file has opened successfully.

One way to implement the loop would be to place the first call to scanf() outside of the loop, then use strcmp() in the while statement to check for "END". At the end of the loop, duplicate the first call to scanf():

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

int main(void) {
    FILE *inputfile = NULL;
    FILE *outputfile = NULL;

    struct imenik {
        char prezime[17 + 1];
        char ime[13 + 1];
        char predmet[20 + 1];
        int bodovi;
    } ucenik;

    outputfile = fopen("imenik.txt", "w");

    /* Did file open successfully? */
    if (outputfile == NULL) {
        perror("Unable to open file:");
        exit(EXIT_FAILURE);
    }

    /* Specify maximum widths in calls to scanf() */
    printf("Ucitaj ime ucenika: ");
    scanf("%13[^\n]%*c", ucenik.ime);

    while (strcmp(ucenik.ime, "END") != 0) {

        printf("Ucitaj prezime ucenika: ");
        scanf("%17[^\n]%*c", ucenik.prezime);

        printf("Ucitaj predmet: ");
        scanf("%20[^\n]%*c", ucenik.predmet);

        printf("\nUcitaj broj bodova (0-50): ");
        scanf("%d%*c", &ucenik.bodovi);

        fprintf(outputfile, "%s | %s | %s | %d\n",
                ucenik.ime, ucenik.prezime, ucenik.predmet, ucenik.bodovi);

        printf("Ucitaj ime ucenika: ");
        scanf("%13[^\n]%*c", ucenik.ime);
    }

    fclose(outputfile);

    return 0;
}
ad absurdum
  • 19,498
  • 5
  • 37
  • 60