简体   繁体   中英

For loop doesn`t work when add [^\n] to a scanf_s

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);
}

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);
}

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.

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