Why does my fgets method turn out to be like this? - c

I cannot input my "details" because it doesn't leave any space for me to.
int addTask(NODE **head){
//IGNORE NODES
NODE *p, *temp, *ptr, *a;
DATE d;
p = *head;
a = *head;
char x[32];
char y[32];
char name[32];
char details[128];
int month;
int day;
int year;
int priority;
int intDate;
int i;
printf("\n");
printf("Enter task name: ");
scanf("%s", name);
printf("Enter task details:");
fgets(details, 128, stdin); //PROBLEM LIES HERE
printf("[mm dd yyyy] Enter task deadline: ");
scanf("%d %d %d", &month, &day, &year);
printf("Enter priority: ");
scanf("%d", &priority);
/*code conditions here*/
}
In my terminal it turns out like this:
User#Lynn /cygdrive/c/users/user/academic/c
$ gcc -o a Yago_exer11.c
User#Lynn /cygdrive/c/users/user/academic/c
$ ./a
1. Add a task
2. Remove a task
3. Search for a task
4. View all tasks
5. View tasks by priority
6. Exit
Choice: 1
Enter task name: hello
Enter task details:[mm dd yyyy] Enter task deadline: 09 09 09 //PROBLEM
Enter priority: 1
Adding...
I tried using scanf(%^[\n]) as an alternative but it turns out to have the same output. Can someone tell me what's wrong with my code?

Try to put getchar(); after scanf("%s", name);

The functions fgets and scanf work differently. It's best not to mix them.
scanf scans the input. It doesn't know about lines; it treats all new-line characters, tabs and spaces simply as "white space". It also stops after conversions, so that after reading, the next file reading operations starts directly after that input, which is usually just before a new line. (There's also the pitfall of trying to parse decimal numbers over and over when the input gets stuck in something that is not a number.)
fgets reads lines from the input. That method goes more naturally with your prompt-and-input strategy: The program asks a question, the user answers and presses enter.
I suggest a two-step approach. Read lines first, then analyse these lines and extract the data. Depending on what the data looks like, you can tokenise the line with strtok for example and convert the tokens or you can use sscanf, scanf's sister function for scanning strings, to scan the data.
Here's a version of your example that shows several strategies of parsing input with that two-step spproach:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int main(void)
{
char line[128];
char name[32];
char details[128];
int month;
int day;
int year;
int priority;
int n;
// read lines until one of them contains a word
printf("Enter task name: ");
do {
if (fgets(line, sizeof(line), stdin) == 0) return 1;
n = sscanf(line, "%s", name);
} while (n != 1);
// details may be empty, so read them directly
printf("Enter task details: ");
if (fgets(details, sizeof(details), stdin) == 0) return 1;
strtok(details, "\n");
// prompt and read lines until one of them has a valid date
do {
printf("[mm dd yyyy] Enter task deadline: ");
if (fgets(line, sizeof(line), stdin) == 0) return 1;
n = sscanf(line, "%d %d %d", &month, &day, &year);
if (n == 3) {
if (month < 1 || month > 12) n = 0;
if (day < 1 || day > 31) n = 0;
}
if (n != 3) printf("Invalid date\n");
} while (n != 3);
// read priority until a number (any number) is given
printf("Enter priority: ");
do {
if (fgets(line, sizeof(line), stdin) == 0) return 1;
n = sscanf(line, "%d", &priority);
} while (n != 1);
// echo results
printf("name: %s\n", name);
printf("details: %s\n", details);
printf("data: %02d/%02d/%04d\n", month, day, year);
printf("prio: %d\n", priority);
return 0;
}
The example is far from perfect. For example, what should happen when the user presses Ctrl-Z or Ctrl-D to end the input stream? The standard input isn't particularly suited for manual input.

Related

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.

Scan a white space/whole line in c

So, I know this question has been asked before, but I can't seem to make anything work. What I have right now is this:
#include<stdio.h>
struct ClothingCustomer{
char name[20];
int age;
double inseam;
};
struct ClothingCustomer createACustomer(){
struct ClothingCustomer aCustomer;
printf("Enter Customer Name: ");
scanf("%s",aCustomer.name);
printf("Age: ");
scanf("%d",&aCustomer.age);
printf("Inseam: ");
scanf("%lf",&aCustomer.inseam);
return aCustomer;
};
int main(){
FILE* customersFile = fopen("customers.txt","w");
for (int i = 0; i < 5; i++){
struct ClothingCustomer aCustomer = createACustomer();
fprintf(customersFile, "%s %d %lf\n", aCustomer.name, aCustomer.age, aCustomer.inseam);
}
fclose(customersFile);
return 0;
}
No matter what I do to try to make it scan more than one word, like a first/last name or something, it works, but here's what I get in the console while running this(with the scan options to try to get past a white space listed below; the above code functions correctly, but doesn't allow white space):
Enter Customer Name:
Age:
Inseam:
Enter Customer Name: Age:
Inseam: Enter Customer Name: Age:
Inseam:
Enter Customer Name: Age:
Inseam:
Enter Customer Name: Age:
Inseam:
How can I make it not do this? I've tried using:
[^\n]
fgets(name, sizeof(name), stdin);
and the same thing happens every time.
This Will Work
#include<stdio.h>
#include<string.h>
struct ClothingCustomer createACustomer(void);
struct ClothingCustomer{
char name[20];
int age;
double inseam;
};
struct ClothingCustomer createACustomer(void){
struct ClothingCustomer aCustomer;
{ //From Here Starts The Part in Which You Are Having Problems.
char c;
int i;
printf("Enter Customer Name: ");
scanf("%s",aCustomer.name);
i = strlen(aCustomer.name); // length of user input till first space
do{
scanf("%c", &c);
aCustomer.name[i++] = c; // reading characters after first space (including it)
}while (c != '\n'); // until user hits Enter
aCustomer.name[i - 1] = 0; // string terminating
}
printf("Age: ");
scanf("%d",&aCustomer.age);
printf("Inseam: ");
scanf("%lf",&aCustomer.inseam);
return aCustomer;
};
int main(){
FILE* customersFile = fopen("customers.txt","w");
int i = 0;
for (i = 0; i < 5; i++){
struct ClothingCustomer aCustomer = createACustomer();
fprintf(customersFile, "%s %d %lf\n", aCustomer.name, aCustomer.age,aCustomer.inseam);
}
fclose(customersFile);
return 0;
}
I Highly Recommend You To Take A Look on this answer , it will help you a lot , the method I used in here is mentioned in the above answer.Please Give That answer Credit If this method works for you.
Here is the explanation for the part which you were having problem in , how is it working now.
How this works? When user inputs characters from standard input, they will be stored in string variable until first blank space. After that, rest of entry will remain in input stream, and wait for next scanf. Next, we have a for loop that takes char by char from input stream (till \n) and appends them to end of string variable, thus forming a complete string same as user input from keyboard.
Unclear why scanf(" %19[^\n], aCustomer.name) failed for OP.
Rather than use scanf() for complex input, separate user input from parsing.
Drop use of scanf() completely and use fgets() to fetch user input. Use sscanf(), strtod(), strtol(), strtok() etc. for parsing.
Be sure to check the result of user input and success of parsing functions.
OP has not indicated how to handle troublesome input. The below returns a zero-ed ClothingCustomer in that case. Additional error codes or error messages may be useful.
struct ClothingCustomer createACustomer(void) {
// Suggest initializing
struct ClothingCustomer zero = { 0 };
struct ClothingCustomer aCustomer = { 0 };
char buffer[100];
printf("Enter Customer Name: ");
fflush(stdout); // insure prompt is seen before asking for input
if (fgets(buffer, sizeof buffer, stdin) == NULL) return zero;
buffer[strcspn(buffer, "\r\n")] = '\0'; // lop off potential line ending
if (strlen(buffer) >= sizeof aCustomer.name) return zero; // too long
strcpy(aCustomer.name, buffer);
printf("Age: ");
fflush(stdout);
if (fgets(buffer, sizeof buffer, stdin) == NULL) return zero;
if (sscanf(buffer, "%d", &aCustomer.age) != 1) return zero;
// Let us do some range checking
// https://en.wikipedia.org/wiki/List_of_the_verified_oldest_people
if (aCustomer.age < 0 || aCustomer.age > 122) return zero;
printf("Inseam: ");
fflush(stdout);
if (fgets(buffer, sizeof buffer, stdin) == NULL) return zero;
if (sscanf(buffer, "%lf", &aCustomer.inseam) != 1) return zero;
return aCustomer;
}

Restrict User To Enter Integers?

Hello I Want To Ask A Question About That How i Restrict User From Enter Integers And Enter Only String OR Characters.
If You Know The Answer Can You Fit That In My Code Below that Would be great if you do that btw forget the date part its just other thing.
void checkin()
{
char comp_choice,more_choice,in_comp_choice;
int comp_amount;
int date_month[] = {31,28,31,30,31,30,31,31,30,31,30,31};
int date_month1[] = {31,28,31,30,31,30,31,31,30,31,30,31};
int charges_per_room_per_day = 5000,bill;
struct info user;
system("cls");
printf("\t\tCHECK IN FORM\n");
printf("Please Fill Following Information\n");
FILE *fp;
fp = fopen("checkin.txt","a");
time_t t;
time(&t);
printf("First Name : ");
fflush(stdin);
gets(user.first_name);
printf("Last Name : ");
fflush(stdin);
gets(user.last_name);
fflush(stdin);
printf("Contact Number : ");
gets(user.contact_no);
fflush(stdin);
printf("\nGuests : ");
scanf("%d",&user.guest);
printf("Rooms : ");
scanf("%d",&user.rooms);
fprintf(fp,"%s %s %s %d %d\n",user.first_name,user.last_name,user.contact_no,user.guest,user.rooms);
Label2:
printf("Today date and time is %s\n",ctime(&t));
printf("Check In date (DD-MM-YYYY) : ");
scanf("%d %d %d",&user.date,&user.month,&user.year);
printf("Check out date (DD-MM-YYYY) : ");
scanf("%d %d %d",&user.date1,&user.month1,&user.year1);
This Is Image of i am entering Integers And Program Doesn't Say Any Thing
A way to enforce a user entering a valid integer is to read in whatever the user enters (e.g. into a char[..]-buffer), and then to interpret/check the result as required. For this check, you can then either write your custom logic, or use the logic of built in functions, like, for example, strol.
The following sample makes use of strtol. The signature of strtol is long int strtol(const char *nptr, char **endptr, int base). Basically, after a successful scan, endptr will point to the first character of nptr after the (successfully) scanned number; if we do not accept any characters after a (valid) number, we check if endptr actually points to string terminator '\0'; in the case of an unsuccessful scan, endptr is equal to nptr.
Here you go:
#include <stdio.h>
#include <stdlib.h>
int enterIntegerValue(const char *message) {
char inputBuffer[21];
char *endOfScan;
bool error;
int result;
do {
printf("%s", message);
scanf("%20s", inputBuffer);
result = (int)strtol(inputBuffer,&endOfScan,10);
error = (endOfScan == inputBuffer) || (*endOfScan != '\0');
if (error)
printf("Invalid number. Please enter a valid integer number.");
}
while (error);
return result;
}
int main()
{
int rooms = enterIntegerValue("Rooms : ");
printf("input: %d", rooms);
return 0;
}

How to add strings and integers to an array then print to a text file

Goodday, when I get to entering the first name of the student, the program immediately crashes. Also I'm not exactly sure how to add the names and the ID into an array to print to the text file. May I have some assistance please?
struct records{
int id;
char fname[15];
char lname[15];
};
struct records student;
int max=1000;
int i;
srand( time(NULL) ); //random numbers generated
ATND= fopen("Student Record.txt","a");
if(ATND== NULL){
printf("ERROR!");
exit(1);
}
for(i=0; i<100; i++){
printf("Enter student\'s first name: ");
scanf("%s", student.fname[i]);
printf("\n\n");
printf("Enter student\'s last name: ");
scanf("%s", student.lname[i]);
/*randomnumber*/student.id[i]=rand() %max + 39048543;
fprintf(ATND,"%s %s %d", student.fname[i], student.lname[i], student.id[i]);
}
fclose(ATND);
Code only provides data space for 1 record whereas it appears to need 1000 records. Number of other issues. Suspect after 10 hours, OP has worked a number of them
//Definition - good
struct records {
int id;
char fname[15]; // IMO 15 is too limiting for first and last
char lname[15];
};
// Only 1 student, need many more
// struct records student;
#define STUDENT_N 1000
struct records student[STUDENT_N];
void read_records(void) {
// avoid magic numbers
// int max = 1000;
int max = STUDENT_N;
int i;
srand(time(NULL)); //random numbers generated
// ATND not declared
FILE *ATND;
ATND = fopen("Student Record.txt", "a");
if (ATND == NULL) {
printf("ERROR!");
exit(1);
}
char buf[100];
// avoid magic numbers
// for (i = 0; i < 100; i++) {
for (i = 0; i < STUDENT_N; i++) {
printf("Enter student\'s first name: ");
// don't use scanf()
// scanf("%s", student.fname[i]);
if (fgets(buf, sizeof buf, stdin) == NULL) break;
if (sscanf(buf, "%14s", student[i].fname) != 1) break;
printf("\n\n");
printf("Enter student\'s last name: ");
// Add flush to insure buffered prompts that do not end in \n are sent
fflush(stdout);
// scanf("%s", student.lname[i]);
if (fgets(buf, sizeof buf, stdin) == NULL) break;
if (sscanf(buf, "%14s", student[i].lname) != 1) break;
// /*randomnumber*/student.id[i] = rand() % max + 39048543;
/*randomnumber*/student[i].id = rand() % max + 39048543;
// Do not index the name, index the structure
// fprintf(ATND, "%s %s %d", student.fname[i], student.lname[i], student.id[i]);
fprintf(ATND, "%s %s %d", student[i].fname, student[i].lname, student[i].id);
}
fclose(ATND);
}
Assuming student.fname is a char array of sufficient size
scanf("%s", student.fname[i]);
should be
scanf("%s", student.fname);
You need to pass a pointer to the beginning of the array, NOT the value of the chars, one by one. scanf will enter the entire name in one call.

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