scanf("%[^\n]") gets skipped - c

I want to write a little program to learn C; here it is:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int total;
char suffix[3];
struct person {
char id[11];
char name[21];
char sex[7];
int age;
char phone[12];
};
char* number_suffix (int number) {
int mod;
mod = number % 10;
switch (mod) {
case 1:
strcpy(suffix, "st");
break;
case 2:
strcpy(suffix, "nd");
break;
case 3:
strcpy(suffix, "rd");
break;
default:
strcpy(suffix, "th");
break;
}
return suffix;
}
void input_info (struct person info[], int total_people) {
int counter;
for (counter=0; counter<total_people; counter++){
printf("%s%d%s%s\n","Please input the ID(10 digits) of ", (counter+1),
number_suffix(counter), " person: ");
scanf("%s", info[counter].id);
fflush(stdin);
printf("%s%d%s%s\n", "Please input the Name(20 chars) of ", (counter+1),
number_suffix(counter), " person: ");
scanf("%[^\n]", info[counter].name);
fflush(stdin);
printf("%s%d%s%s\n", "Please input the Sex(Male/Female) of ", (counter+1),
number_suffix(counter), " person: ");
scanf("%s", info[counter].sex);
fflush(stdin);
printf("%s%d%s%s\n", "Please input the Age(1~100) of ", (counter+1),
number_suffix(counter), " person: ");
scanf("%d", &info[counter].age);
fflush(stdin);
printf("%s%d%s%s\n", "Please input the Phone of ", (counter+1),
number_suffix(counter), " person: ");
scanf("%s", info[counter].phone);
fflush(stdin);
}
printf("%s\n%s\n%s\n%d\n%s\n", info[counter].id, info[counter].name, info[counter].sex, &info[counter].age, info[counter].phone);
}
int main (void) {
printf("%s\n", "Please input a number that how many people you want to record:");
scanf("%d", &total);
fflush(stdin);
struct person *person_info = malloc(sizeof(struct person)*total);
input_info(person_info, total);
free(person_info);
return 0;
}
I found something weird, when I run it.
Please input a number that how many people you want to record:
1
Please input the ID(10 digits) of 1th person:
A01
Please input the Name(20 chars) of 1th person:
Please input the Sex(Male/Female) of 1th person:
Male
Please input the Age(1~100) of 1th person:
32
Please input the Phone of 1th person:
1224464
[empty line]
[empty line]
[empty line]
1926234464
[empty line]
Is that program skip scanf("%[^\n]", info[counter].name); this line when it run?
Why, and what causes it?

fflush(stdin) is undefined as per the C standard, but it works on some implementations. But it is best to avoid it as it isn't portable and may invoke Undefined Behavior.
To fix the issue, replace all fflush(stdin)s with
int c; /* Declare it once */
while((c = getchar()) != '\n' && c != EOF); /* Discards everything until a newline character or EOF */
Another problem is with
printf("%s\n%s\n%s\n%d\n%s\n", info[counter].id, info[counter].name, info[counter].sex, &info[counter].age, info[counter].phone);
It should be
printf("%s\n%s\n%s\n%d\n%s\n", info[counter].id, info[counter].name, info[counter].sex, info[counter].age, info[counter].phone);
and should be placed inside the for loop. Otherwise, you invoke Undefined Behavior because
You passed an int* for %d which expects an int.
You access invalid memory location beyond the allocated memory segment.
Also, as others have said, pass counter + 1 to number_suffix.

Problem is with your scanf pattern. Use " %[^\n]" instead of "%[^\n]" to not catch \n (After previous data entry)
Pass counter + 1 to number_suffix
How to understand the relation between pointers, struct, malloc, functions parameters?
Read Understanding and Using C Pointers from O'Reilly Media

Related

fprintf function in code writes garbage data into .csv file

I'm trying to create a C program which collect's an applicant's information. When a user is prompted to enter their written subjects, the program writes rubbish data into the .csv file when they wrote one. And sometimes does the same when the number of subjects written is two.
I've tried to clear the buffer stream, but it's no use. Strangely, using different compliers like DevC++, Embarcadero DevC and VS Code produces different results.
Edit: I've also noticed the chances of the rubbish values being written into the file are lowered when the grades of the subjects is lower than the number of subjects written.
Attached below is the code. And an image of the output.
// C libraries.
#include <stdio.h> // Contains function prototypes for the standard input/output library functions, and information used by them.
#include <conio.h> // Contains function prototypes for the console input/output library functions.
#include <stdlib.h> // Contains function prototypes for conversions of numbers to text and text to numbers, memory allocation, random numbers and other utility functions.
#include <string.h> // Contains function prototypes for string-processing functions.
#include <time.h> // Contains function prototypes and types for manipulating the time and date.
#include <stdbool.h> // Contains macros defining bool, true and false, used for boolean variables.
struct Applicant
{
int applicationID;
int dateOfApplication;
char lastName[21];
char firstName[21];
char middleName[21];
char dateOfBirth[21];
int age;
char gender;
char address[100];
char phoneNumber[21];
char emailAddress[51];
char mobileNumber[21];
int numSubjectsWritten;
char csecSubjects[20][100];
char grades[20];
char programmeSelection[10];
};
struct Applicant getApplicantData()
{
struct Applicant applicant;
int i = 0;
int numSubjects;
// Asking for applicant input for various fields.
printf("| Personal |");
printf("\nEnter Last Name: ");
scanf(" %20s", &applicant.lastName);
fflush(stdin);
printf("\nEnter First Name: ");
scanf(" %20s", &applicant.firstName);
fflush(stdin);
printf("\nEnter Middle Name (If you don't have a middle name, leave this field blank.): ");
gets(applicant.middleName);
fflush(stdin);
/*
printf("\nEnter Date of Birth: ");
scanf(" %s", &applicant.dateOfBirth);
fflush(stdin);
printf("\nEnter Gender. 'M' for male, 'F' for female, (M|F): ");
scanf(" %c", &applicant.gender);
fflush(stdin);
printf("\n\n| Contact Information |");
printf("\nEnter Address: ");
gets(applicant.address);
fflush(stdin);
printf("\nEnter Phone Number: ");
gets(applicant.phoneNumber);
fflush(stdin);
printf("\nEnter Email Address: ");
gets(applicant.emailAddress);
fflush(stdin);
printf("\nEnter Mobile Number: ");
gets(applicant.mobileNumber);
fflush(stdin);
*/
printf("\n\n| Education |");
printf("\nEnter Number of Subjects Written: ");
scanf("%d", &applicant.numSubjectsWritten);
fflush(stdin);
while (i < applicant.numSubjectsWritten)
{
printf("\nEnter the subject: ");
gets(applicant.csecSubjects[i]);
fflush(stdin);
printf("\nEnter the grade for that subject: ");
scanf(" %c", &applicant.grades[i]);
fflush(stdin);
i++;
}
return applicant;
}
int main(void)
{
FILE *file = fopen("Data.csv", "a+");
int i, j;
if (!file)
{
printf("\nError! Can not open data file.\nPlease contact the Program Addmission Manager as soon as possible with the error message.");
exit(1);
}
else
{
struct Applicant applicant = getApplicantData();
//fprintf(file, "%s:%s:%s:%s:%c:%s:%s:%s:%s", applicant.lastName, applicant.firstName, applicant.middleName, applicant.dateOfBirth, applicant.gender, applicant.address, applicant.phoneNumber, applicant.emailAddress, applicant.mobileNumber);
fprintf(file, "%s:%s:%s:", applicant.lastName, applicant.firstName, applicant.middleName);
for (i = 0; applicant.csecSubjects[i][0] != '\0'; i++)
{
fprintf(file, " %s", applicant.csecSubjects[i]);
fflush(stdout);
fflush(stdin);
fflush(file);
fprintf(file, " ( %c):", applicant.grades[i]);
fflush(stdout);
fflush(stdin);
fflush(file);
}
}
return 0;
}
First problems I see:
Remove the & from all instances where you scanf a string
Don't use gets, or mix scanf and fgets
Don't fflush(stdin)
Instead of scanf, consider using a custom-made input method with condition checking and anything you need. I will give an example.
#define BUFFER_SIZE 512
void input(char* buffer){
memset(buffer, 0, BUFFER_SIZE); // Initializing the buffer.
fgets(buffer, BUFFER_SIZE, stdin);
strtok(buffer,"\n");
}
How to take input using that?
void main(){
int username[BUFFER_SIZE];
input(username);
}
A way to write a structure to a file is shown below.
void Structure_Print(Applicant* applicant, FILE* stream, int no_of_applicant){
if(no_of_applicant==0){
fprintf(stdout, "No applicant yet.\n");
return;
}
fprintf(stream, "%s:%s:%s:", applicant.lastName, applicant.firstName, applicant.middleName);
for (i = 0; applicant.csecSubjects[i][0] != '\0'; i++)
{
fprintf(stream, " %s:", applicant.csecSubjects[i]);
fprintf(stream, " %c:", applicant.grades[i]);
}
return;
}
Also, I noticed how you tried to make it readable while saving it in subject(grade) format. I recommend you to not do that. Your .csv file is just for database. Nobody is going to read it. So just store the data by comma or any character separator. It will make it easier to extract data later.

Char parameter in function is null or empty in C

I'm currently learning c, then I'm playing with functions and data types, specifically in this case char[]'s.
The following code I've written declares a function called verifyMessage() and receives two parameters, name and gender.
When I execute the function, I pass the two parameters that the user enters through the console, but when I print the name it doesn't print anything.
#include <stdio.h>
int main() {
int i = 0;
double controlNumber = 21200164;
double number = 0;
char name[50];
char gender[1];
int attempts = 5;
int aux = 0;
do {
printf("Introduzca el numero de control: ");
scanf("%lf", &number);
if (controlNumber == number) {
printf("\nWrite your name: ");
scanf("%s", name);
printf("\nWrite your gender (M/F): ");
scanf("%s", gender);
verifyMessage(name, gender);
break;
} else {
i++;
}
} while (i < attempts);
return 0;
}
void verifyMessage(char name[50], char gender[1]) {
if ('M' == gender[0]) {
printf("\n\Name: %s", name);//Here doesn´t print the name
printf("\nMen");
} else if ('F' == gender[0]) {
printf("\nWoman");
} else {
printf("\nInvalid gender");
}
}
Using char gender[1]; with %s is dangerous because gender has room for only one element, so it can accept only strings upto zero characters (the only room will be occupied by terminating null-character)
On the other hand, %s will read positive-length strings (it cannot read strings with zero characters), so it will cause out-of-range access on successful read.
Allocate enough elements and set the maximum length to read (upto the number of elements minis one for terminating null-character) to avoid buffer overrun.
char name[50];
char gender[2];
/* ... */
printf("\nWrite your name: ");
scanf("%49s", name);
printf("\nWrite your gender (M/F): ");
scanf("%1s", gender);
Checking results (return values) of scanf() to check if they successfully read desired things will improve your code more.

using fgets with structure

I am trying to use fgets with structure, since I have to insert in character array. But when I use fgets it's not working properly. I can not enter value for the char array. Please help. Below is a sample program::
#include <stdio.h>
#include<string.h>
struct Student
{
int roll;
char name[50];
int age;
char branch[50];
char gender[1]; //F for female and M for male
};
int main()
{
struct Student s1;
printf("enter roll number of the student: ");
scanf("%d", &s1.roll);
printf("Enter student name: ");
fgets(s1.name, 50, stdin); // NOT WORKING ...
printf("Enter age number: ");
scanf("%d", &s1.age);
printf("Enter branch number: ");
scanf("%d", &s1.branch);
printf("Enter Gender: ");
scanf("%d", &s1.gender);
return 0;
}
First of all you need different format specifiers for different datatypes. So you need to use %c for a character and %[^\n] for a string containing spaces.
You also need to remove leading whitespaces before scanning a string, because a newline \n is left in the input buffer which would otherwise be read by %c and %[], as Weather Vane pointed out in a comment.
#include <stdio.h>
#include <string.h>
struct student
{
int roll;
char name[50];
int age;
char branch[50];
char gender; // can be a single character
};
int main(void)
{
struct student s1;
printf("Enter roll number: ");
scanf("%d", &s1.roll);
printf("Enter name: ");
scanf(" %49[^\n]", s1.name); // use %[^\n] to scan a string containing spaces
printf("Enter age: ");
scanf("%d", &s1.age);
printf("Enter branch name: ");
scanf(" %49[^\n]", s1.branch);
printf("Enter gender: ");
scanf(" %c", &s1.gender); // %c is the format specifier for a char
return 0;
}
fgets is not being bypassed, it's actually working as it should, what happens is that it reads the newline character that remains in the input buffer from the previous scanf, if you access s1.name you will see that it has a string ("\n\0") in it.
For name I have to insert space character too, so I used fgets
You can use scanf with [^\n] specifier which can read spaces. Mixing scanf with fgets is trouble, it can be done, but you should avoid it.
You should either use scanf only, or fgets only, in the latter case, if you need to convert strings to ints use sscanf or better yet strtol.
Your code has other issues, detailed in the comments with corrections:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct Student
{
int roll;
char name[50];
int age;
char branch[50];
char gender; //F for female and M for male
};
For solution with scanf only it should, more or less, look like this:
void clear_buffer(){ // helper function to clear buffer
int c;
while((c = getchar()) != '\n' && c != EOF){}
if(c == EOF){
fprintf(stderr, "Fatal error!");
exit(EXIT_FAILURE);
}
}
int main()
{
struct Student s1;
printf("enter roll number of the student: ");
while (scanf("%d", &s1.roll) != 1){
fprintf(stderr, "Bad input, try again: ");
clear_buffer();
} // if bad input ask again
printf("Enter student name: "); // the space before % clears blanks
while (scanf(" %49[^\n]", s1.name) != 1){ // will read the line until
fprintf(stderr, "Bad input, try again: "); // enter is pressed, provided
clear_buffer(); // that it's not larger than 49
}
printf("Enter age number: ");
while(scanf("%d", &s1.age) != 1){
fprintf(stderr, "Bad input, try again: ");
clear_buffer();
}
printf("Enter branch number: ");
while (scanf(" %49[^\n]", s1.branch) != 1){ // branch is a string, %d
clear_buffer(); // specifier is for ints.
fprintf(stderr, "Bad input, try again: "); // note that I'm using width
} // limit (49) to avoid buffer overflow
printf("Enter Gender: ");
while(scanf(" %c", &s1.gender) != 1){ // only 1 character needed, use %c
fprintf(stderr, "Bad input, try again: ");
clear_buffer();
}
}
For a solution with fgets only which, I would argue is better, you can do something like this:
int main(){
struct Student s1;
char temp[50];
printf("enter roll number of the student: ");
if (fgets(temp, sizeof temp, stdin)){
if (sscanf(temp, "%d", &s1.roll) != 1){
fprintf(stderr, "Error parsing input!\n");
}
}
printf("Enter student name: ");
if (fgets(temp, sizeof temp, stdin)){
if (sscanf(temp, "%49[^\n]", s1.name) != 1){
fprintf(stderr, "Error parsing input!\n");
}
}
printf("Enter age number: ");
if (fgets(temp, sizeof temp, stdin)){
if (sscanf(temp, "%d", &s1.age) != 1){
fprintf(stderr, "Error parsing input!\n");
}
}
printf("Enter branch number: ");
if (fgets(temp, sizeof temp, stdin)){
if (sscanf(temp, "%49[^\n]", s1.branch) != 1){
fprintf(stderr, "Error parsing input!\n");
}
}
printf("Enter Gender: ");
if (fgets(temp, sizeof temp, stdin)){
if (sscanf(temp, " %c", &s1.gender) != 1){
fprintf(stderr, "Error parsing input!\n");
}
}
}
*scanf to parse ints still has a potencial flaw in case of overflow, there is no way of guarding against that, unless you use a more robust method like the aforementioned strtol.

Using scanf to create new struct object

Hi I'm using to create a simple program that contains a list of Users (based on the struct below), and I'm trying to create new users based on user input, where I ask for each property seperately using scanf. But I'm having trouble with the structs limitations, for example id should be at max 10 chars and nome can have a max of 25 chars. Here's my code for more context:
struct user {
char id[10];
char name[25];
char group;
float score;
};
struct user list[25];
int registered = 0;
void createNewUser() {
struct user *userPtr, newUser;
userPtr = &newUser;
printf("\nId: ");
scanf("%10s", &(*userPtr).id);
printf("\nName: ");
scanf("%25s", &(*userPtr.name);
printf("\nGroup: ");
scanf("%c", &(*userPtr).group);
printf("\nScore: ");
scanf("%f", &(*userPtr).score);
insert(newUser);
printf("%10s\n", list[0].id);
printf("%25s\n", list[0].name);
printf("%c\n", list[0].group);
printf("%.1f\n", list[0].score);
}
void insert(struct user newUser) {
if (registered < 25){
list[registered] = newUser;
registered += 1;
}
}
With the code I presented above, if I type more than 10 chars for the first input, the next 2 are ignored. And my 3rd scanf is always ignored, the one for group. Can anyone here help me out with this?
The problem with scanf is that when it stops converting characters and there are
more in the input buffer (because the user entered more than you anticipated), then scanf will leave those characters there.
Specially the newline character (inputed when the user presses ENTER)
remains in the input buffer, which causes problems to subsequent calls of
scanf that read characters or strings. So in this case you have to "clean" the input buffer,
so that the next scanf does not consume the left overs of the previous scanf calls.
You can use this function after every scanf:
void clean_stdin(void)
{
int c;
while((c = getchar()) != '\n' && c != EOF);
}
Then you can do:
printf("\nId: ");
scanf("%10s", (*userPtr).id); // no need for &, id is char[]
clean_stdin();
printf("\nName: ");
scanf("%25s", (*userPtr).name); // same here
clean_stdin();
printf("\nGroup: ");
scanf("%c", &(*userPtr).group);
clean_stdin();
printf("\nScore: ");
scanf("%f", &(*userPtr).score);
Also note that the way you are if the maximal length of the ID is 10, then the
buffer must be of length 11, because in C you need to terminate the strings with
the '\0'-terminating byte. So change your structure to this:
struct user {
char id[11];
char name[26];
char group;
float score;
};
Also bear in mind, using a pointer like this
struct user *userPtr, newUser;
userPtr = &newUser;
printf("\nId: ");
scanf("%10s", (*userPtr).id);
...
is not necessary, it actually makes the code harder to read. You can do:
void createNewUser() {
struct user newUser;
printf("\nId: ");
scanf("%10s", newUser.id);
clean_stdin();
...
printf("\nScore: ");
scanf("%f", &newUser.score);
...
}

User Input to File

I'm creating a program that should create a structure of a list of people entered by the user; the only problem I'm having is getting the user input data to appear in the text file. Anyone know how to do this? Here is the code:
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
struct person{
char name[20];
int age;
struct person *next_ptr;
} PERSON;
int main (void){
struct person PERSON;
FILE *fp;
char ans, ch;
int ppl=0;
fp=fopen("person_struct", "w");
if(fp != NULL){
while(ppl<25){
printf("Would you like to add a person to the list? [y/n] ");
scanf("%c", &ans);
if(ans == 'y') {
printf("\nEnter a name:\n");
scanf("%s", PERSON.name);
fprintf(fp, "%s",PERSON.name);
printf("\nEnter age:\n");
scanf("%i", &PERSON.age);
fprintf(fp, " %i\n", PERSON.age);
}
else {
ppl=25;
}
ppl++;
}
fclose(fp);
}
printf("\n\n\n");
system("pause");
return 0;
}
Youe scanf statement is wrong you forgot ampersand & operator before PERSON.age its int
scanf("%i", PERSON.age);
^ & missing
correct is:
scanf("%i", &PERSON.age);
You have two scanf stamens in your code to inputs from user one for string to scan name.
scanf("%s", PERSON.name);
This is correct and No need of & before string. But age is int and to scan int.float you need to add & before variable that is why added ampersand & before PERSON.age.
ref: scanf
Second:
fputs(PERSON.age, fp); is wrong syntax of fputs is:
int fputs( const char *str, FILE *stream );
^ you are passing int
first argument should be const char* but your are passing int
fputs(PERSON.age, fp);
^ wrong , age is int not char*
When you need formatting input/output prefer printf and scanf functions, My suggestion change your read/write like: (read comments)
printf("Enter a name:\n");
scanf("%s", PERSON.name); // here is No & because `name` is string
scanf("%i", &PERSON.age); // age is `int` so & needed
fprintf(fp,"%s %i\n",PERSON.name, PERSON.age);
EDIT: Because you commented, your code is working after these rectifications, see
$ gcc x.c -Wall
$ ./a.out
Would you like to add a person to the list? [y/n]y
Enter a name:
yourname
14
Would you like to add a person to the list? [y/n]y
Enter a name:
firendName
15
Would you like to add a person to the list? [y/n]n
sh: 1: pause: not found
$ cat person_struct.txt
yourname 14
firendName 15
In addition to Grijesh's answer:
Please explain scanf("%s", &ans);. How many characters can you store in ans? How many characters does the string "y" require to store? Verify your beliefs: printf("sizeof ans: %zu\n" "sizeoof \"y\": %zu\n", sizeof ans, sizeof "y");
Perhaps you meant: if (scanf("%c", &ans) != 1) { /* assume stdin has closed or reached EOF */ }. Note the %c, which will read only one character into ans.
Alternatively, if you change ans to an int, you can use: ans = getchar();
edit: In short, I think your loop should look something like this:
for (size_t ppl = 0; ppl < 25; ppl++){
int ans;
printf("Would you like to add a person to the list? [y/n]");
do {
ans = getchar();
while (ans >= 0 && isspace(ans));
if (ans != 'y') {
break;
}
printf("Enter a name:\n");
if (scanf("%s", PERSON.name) != 1 || scanf("%i", &PERSON.age) != 1) {
break;
}
fprintf(fp, "%s %i\n", PERSON.name, PERSON.age);
}

Resources