How to do input validation using a struct variable array in C - arrays

I've made a small program to write Student names and final grades to a file and then read that file and print it. I'm still new to C and I'm playing with input validation, but I'm running into a problem when it comes to using it with a struct. When I run my function it checks for cases just fine, but it does not store the right input into studentFinalGrades. So when I go to print the file, it never stored the input for final grades. Any ideas on how I can fix this? Or advice on how to do input validation another way?
Here is my code:
#include <stdio.h>
#include <stdlib.h>
//struct of students
struct students
{
char name[50];
char lastName[50];
char studentFinalGrade[50];
};
//myfunctions (at bottom of main)
char check_letterGrade(void);
void clear_input_buffer(void);
int main()
{
// in check_letterGrade
char input;
if (input != '\n')
{
clear_input_buffer();
}
//dynamic array
struct students a[2],b[2];
FILE *fptr;
int i;
//opens file.txt
fptr=fopen("file.txt","wb");
//Loop to enter in all student data and grades at once (only did 5 students because it takes forever to fill it out)
for (i=0;i<2;++i)
{
fflush(stdin);
printf("Enter Student Name: ");
scanf("%s",&a[i].name);
printf("Enter Student Last Name: ");
scanf("%s",&a[i].lastName);
printf("What is students Final Grade?:");
check_letterGrade();
// scanf("%s",&a[i].studentFinalGrade);
}
//writes user input to a file and then closes file
fwrite(a,sizeof(a),1,fptr);
fclose(fptr);
//opens and reads file
fptr=fopen("file.txt","rb");
fread(b,sizeof(b),1,fptr);
//Printing all Studens names and grades entered into the file.
int j = 0;
for (i=0;i<2;++i)
{
j ++;
printf("\nStudent %d :\n",j);
printf("Name: %s\nLast Name: %s\nFinal Grade: %s\n\n", b[i].name,b[i].lastName,b[i].studentFinalGrade);
}
fclose(fptr);//close file when done reading.
}//end main
//function to check user validation on correct letter grades entered.
char check_letterGrade(void)
{
char input;
int ch;
//If input is not all of these things then it will tell user to please enter a correct letter grade
// it will then check for new line (which should clear from the buffer and allow new entry) hopefully
while (scanf("%s", &input) != 1 || (input != 'A' && input != 'B'&& input != 'C'&& input != 'D'&& input != 'F'))
{
if (input != '\n') // only take leftover input if there is leftover input
{
while ((ch = getchar()) != '\n') ;
}
printf("Please enter a correct letter grade: ");
}
return input;
}
//clears buffer
void clear_input_buffer(void)
{
int ch;
while ((ch = getchar()) != '\n' && ch != EOF);
}

You don't get studentFinalGrades because you never store it anywhere. You made a function called check_letterGrade which return a single char that contains the grade. However, when you call that function from main, you never do anything with that return value. I suspect you meant to write something like:
a[i].studentFinalGrade = check_letterGrade();
You also need to change the definition of your struct. There you define studentFinalGrade as char[50], but you probably just mean char since you only need a single letter, right?
I realize you are new to C, but your program really contains a lot of strange stuff. Actually, I'm surprised the above code even compiled for you, as scanf("%s",&a[i].name) ought to have given you a syntax error. You may want to invest in a book to learn C and work out the examples first.

Related

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

Storing user input whilst still asking questions

Firstly, i know the source code below is long and you're not really supposed to post code like this, but i really don't understand why it's not working or how i could explain my issue without posting it like this.
Im trying to store the answer to each of the questions asked in it and display them at the end of the script. The biggest problem im having is that i get the error
"subscripted value is neither array nor pointer nor vector scanf("%s", a[i].incdest);"
The program doesn't accept a[i].incdest. it does this for all of the array values.
it is also saying that in function main variable "comp1" is undeclared.
#include<stdio.h>
#include<string.h>
int c_s(char*, char*);
typedef struct
{
char constab[30];
char vicwit[15];
char witdet[200];
char incdest[300];
char comp1[15];
char comp2[200];;
} sheetstore;
#define ARRAYLEN 2
sheetstore a[ARRAYLEN];
FILE *fp;
int main()
{
int i, a;
char wit[10] = "witness";
char yes[10] = "yes";
char comp1[10];
fp = fopen("sheetstore.dat","a+");
printf("Hate crime reporting system\n\n\n");
printf("If the crime you are reporting is an emergency,\nplease call 999, do not proceed any further with this form\n\n\n\nPlease press enter to confirm you have read the above and continue\n");
char enter = 0;
while (enter != '\r' && enter != '\n') { enter = getchar(); }
for( i=0; i<ARRAYLEN ; i++)
{
printf("Which police constabulary did the offence take place in?\n\n");
scanf("%s", a[i].constab);
printf("Are you a victim or witness of the crime?\nPlease answer victim/witness\n\n");
scanf("%s", a[i].comp1);
int res1 = (strcmp (comp1, wit));
if(res1 == 0){
printf("Please enter the details including phone number and address of any other witnesses that were present\n");
}
scanf("%s", a[i].witdet);
else{
printf("Where did the incident take place?\nIf in a house please provide the full address including postcode\n");
scanf("%s", a[i].incdest);
}
fwrite(&a[i], sizeof(a), 1, fp);
}
fclose(fp);
fopen("sheetstore.dat", "r");
for(i=0; i<ARRAYLEN; i++)
{
fread(&a[i], sizeof(a), 1, fp );
printf("Which police constabulary did the offence take place in? : %s\n", a[i].constab);
printf("Are you a victim or witness of the crime? : %s\n", a[i].comp1);
printf("Please enter the details including phone number and address of any other witnesses that were present : %s\n", a[i].witdet);
printf("Where did the incident take place? : %s\n", a[i].incdest);
}
fclose(fp);
return 0;
}
the main issue that i saw in your code, is that you shadowed the array a (sheetstore a[ARRAYLEN];) by declaring an int with the same name in your main().
you also had a scanf stament mislocated.
i fixed your code and put in a comment for every change - now, i don't persume to check the functionality of your code but at least this will compile and hopefully will give you a better understanding where you were wrong - from here it is up to you:
#include<stdio.h>
#include<string.h>
int c_s(char*, char*);
typedef struct
{
char constab[30];
char vicwit[15];
char witdet[200];
char incdest[300];
char comp1[15];
char comp2[200];;
} sheetstore;
#define ARRAYLEN 2
sheetstore sheetArr[ARRAYLEN]; //change this 'a' to avoid shadowing by the decleration on 'int a' in main
FILE *fp;
int main()
{
int i, a;
char wit[10] = "witness";
char yes[10] = "yes";
char comp1[10];
fp = fopen("sheetstore.dat","a+");
printf("Hate crime reporting system\n\n\n");
printf("If the crime you are reporting is an emergency,\nplease call 999, do not proceed any further with this form\n\n\n\nPlease press enter to confirm you have read the above and continue\n");
char enter = 0;
while (enter != '\r' && enter != '\n') { enter = getchar(); }
for( i=0; i<ARRAYLEN ; i++)
{
printf("Which police constabulary did the offence take place in?\n\n");
scanf("%s", sheetArr[i].constab);//change name from 'a'
printf("Are you a victim or witness of the crime?\nPlease answer victim/witness\n\n");
scanf("%s", sheetArr[i].comp1);//change name from 'a'
int res1 = (strcmp (sheetArr[i].comp1, wit));//compare with the value set in the struct
if(res1 == 0){
printf("Please enter the details including phone number and address of any other witnesses that were present\n");
/*this scanf should be inside the brackets of 'if(res1 == 0)' */
scanf("%s", sheetArr[i].witdet);//change name from 'a'
}
else{
printf("Where did the incident take place?\nIf in a house please provide the full address including postcode\n");
scanf("%s", sheetArr[i].incdest);//change name from 'a'
}
fwrite(&sheetArr[i], sizeof(sheetstore), 1, fp);//write a single struct
}
fclose(fp);
fopen("sheetstore.dat", "r");
for(i=0; i<ARRAYLEN; i++)
{
fread(&sheetArr[i], sizeof(sheetstore), 1, fp );//read a single struct
printf("Which police constabulary did the offence take place in? : %s\n", sheetArr[i].constab);
printf("Are you a victim or witness of the crime? : %s\n", sheetArr[i].comp1);
printf("Please enter the details including phone number and address of any other witnesses that were present : %s\n", sheetArr[i].witdet);
printf("Where did the incident take place? : %s\n", sheetArr[i].incdest);
}
fclose(fp);
return 0;
}
In addition to some useful comments posted above (meaningful variable names, line width not to be too long for readability), in general sense, your problem is due to two thing: in the body of the main function you declare an int a which is used instead of your global array a[ARRAYLEN]. Hence, you cannot refence a[i].incdet etc. Just comment or remove the int a from the main function. Secondly, your scanf("%s", a[i].witdet); should be in the body of the if statement above according to the program logic. I you do these two changes, the code will compile. However if you pass -Wall -Werror to the compiler, it will yields an error like error: unused variable ‘yes’ [-Werror=unused-variable]
char yes[10] = "yes";. Just comment this line to avoid using uncessary memory space on the stack.
The following proposed code:
corrects the first 50+ lines of your code.
Please correct your posted code so it cleanly compiles
for flexibility, separates the definition of the struct from the typedef of the struct
properly checks for error indications from system functions
honors the right margin (usually 72 or 80 characters)
replaces the CPU cycle intensive calls to printf() with calls to puts() when appropriate
This is only a guide for the first 50 lines of the OPs code. It is not a complete code replacement.
and now the proposed code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// prototypes
int c_s(char*, char*);
struct sSheet
{
char constab[30];
char vicwit[15];
char witdet[200];
char incdest[300];
char comp1[15];
char comp2[200];
};
typedef struct sSheet sheetstore;
#define ARRAYLEN 2
sheetstore a[ARRAYLEN];
FILE *fp;
int main( void )
{
int i;
char wit[10] = "witness";
//char yes[10] = "yes";
char comp1[10];
fp = fopen("sheetstore.dat","a+");
if( !fp )
{
perror( "fopen for appending to 'sheetstore.dat' failed" );
exit( EXIT_FAILURE );
}
// implied else, fopen successful
puts("Hate crime reporting system\n\n\n");
puts("If the crime you are reporting is an emergency,\n"
"please call 999, "
" do not proceed any further with this form\n\n\n\n"
"Please press enter"
" to confirm you have read the above and continue\n");
int enter = 0;
while (enter != '\r' && enter != '\n')
{
enter = getchar();
}
for( i=0; i<ARRAYLEN ; i++)
{
puts("Which police constabulary did the offence take place in?\n\n");
if( scanf("%29s", a[i].constab) != 1)
{
fprintf( stderr, "scanf for which police constabulary failed" );
fclose( fp ); // cleanup
exit( EXIT_FAILURE );
}
// implied else, scanf successful

C Program: Do-while loop to run program again not working properly

My program is designed to sort records in a text file, but my main issue is getting the main function to ask the user if he would like to run the program again to read and write another file. My program right now, when the user enters 1 to run again, skips the first question to enter the program to read. Why is that? I appreciate the help! Here is my main function only: The program compiles only during the first run.
int main()
{
int fieldCount = 0;
int lineCount = 0;
char file[STR_LEN];
char filepath[STR_LEN];
char** fields = NULL;
char** lines = NULL;
int recCount = 0;
Person **sortedRecs = NULL;
int x;
do {
printf("Enter path of the file to read: ");
gets(filepath);
printf("Enter path to copy to: ");
gets(file);
fields = readFieldsDynamic(filepath, &fieldCount);
lines = readLinesDynamic(filepath, &lineCount);
recCount = getPersons(fields, fieldCount, &sortedRecs);
if (recCount && lines && sortedRecs && (recCount <= lineCount)) {
writeRecsToFile(file, sortedRecs, recCount, lines, lineCount);
printf("Sorted records are written in %s\n", file);
}
if (fields) {
freePointerToChars(fields, fieldCount);
}
if (lines) {
freePointerToChars(lines, lineCount);
}
if (sortedRecs) {
freeRecs(sortedRecs, recCount);
}
printf("Enter 1 to run program again: ");
scanf("%d%*c", &x);
} while (x == 1);
return 0;
}
What you can do is add a while loop to "eat up" all the newlines left in stdin stream to prevent the next getchar to not block for real user input.
while ((ch=getchar()) != EOF && ch != '\n')
;
Also please don't use gets in your code. Try fgets instead.
You need to keep a space before %c.
int main()
{
char ch;
do
{
something();
printf("\nDo you want to continue?");
scanf(" %c", &ch);
}while(ch=='y');
return 0;
}
This answer is based on your output looking something like:
Enter path of the file to read: /my/input/here/
Enter path to copy to: /blah/copy/here/
Enter 1 to run program again: 1
Enter path of the file to read:
Enter path to copy to: help/I/cant/type/in/the/above/field
My understanding is your program is probably carrying over the newlines between loops.
I've had similar problems in C++, and placing cin.get() after an input fixed it. I'm not certain of the C equivalent.

Not able to break from while loop using getchar in C

I am new newbie, facing a basic problem, Not able to break while loop using getchar.Following code compiles successfuly but keeps displaying "Please enter NAME" only and keeps taking typed characters from KeyBoard but doesn;t break on typing enter for new line character :-(
#include<stdio.h>
typedef struct employee
{
char name[20];
int age;
char country[20];
}emp;
void getinfo(char *str, const char *param)
{
int i;
char ch;
if (strcmp(param,"NAME" ) == 0)
{
printf("\nPlease enter NAME \n");
}
else if(strcmp(param, "COUNTRY") ==0)
{
printf("\nPlease enter COUNTRY \n");
}
while((ch==getchar())!= '\n')
{
str[i] = ch;
i++;
}
str[i]= '\0';
}
int getage(int *age)
{
printf("\n Please enter Age \n");
scanf("%d",age);
}
int main(void)
{
emp e1;
getinfo(e1.name, "NAME");
getinfo(e1.country, "COUNTRY");
getage(&e1.age);
}
Please provide help.
The
while((ch==getchar())!= '\n')
should read
while((ch=getchar())!= '\n')
^
Otherwise, you're comparing the (uninitialized) value of ch to getchar(), instead of assigning the result of getchar() to ch.
while((ch==getchar())!= '\n')
That line is currently an undefined operation since ch isn't assigned anything yet. Let's assume ch==getchar() is false, false != '\n' is true, since anything nonzero is true, and '\n' is greater than zero.
I assume you want to change the == in ch==getchar() to a single =.
You have typed while((ch==getchar())!='\n')
This statement calls getchar() and compares to the garbage value of ch, and compares against '\n'
it will be usually true, so the while loop keeps on executing even though ch is assigned nothing
So, change it to while((ch=getchar())!='\n')

Compare user input with text file in C

I need to have a user input a word then compare the word with a text file to see if it is correct. The user has 3 attempts to enter the word before the program terminates. My issue is reading the word from the file I know it's something simple that I have wrong. I should also clarify that the error I'm getting is in the compiler I haven't gotten to the point of being able to compare the strings yet!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(void)
{
FILE *fp;
fp = fopen("secret.txt","r");
char guess[10];
const char secret[10];
int i, c;
c = getc(fp);
fgets(secret, sizeof(secret), fp);
for (i=0; i < 3; i++)
{
printf("Please guess the word: \n");
scanf("%s", guess);
while (c !=EOF)
{
if (strcmp(secret,guess)==0)
{
printf("Your guess was correct");
return 0;
}
else
{
printf("Your guess was incorrect. Please try again\n");
}
}
fclose (fp);
}
return 0;
}
Here are some pointers:
c = getc(fp) consumes the first character of the file, so it never becomes part of the secret variable.
If secret.txt contains a newline, the newline is read into the secret variable.
The while (c != EOF) loop seems pointless, since c isn't modified inside the loop. Furthermore, the infinite nature of the loop prevents the outer for loop from functioning correctly.
If I were you, I'd fix the while loop and would make sure that secret is read correctly, for example by printing it out or examining it in a debugger.
What is
c = getc(fp);
needed for? My "guess" would be that you read the first character of the word into c and then secret misses the first character.
EDIT: Instead of using getc for EOF checking, which as said corrupts the read word (and this while loop is rubbish anyway), just check the return value of fgets:
if(fgets(secret, sizeof(secret), fp) == NULL)
//file is empty or other error occurred
and remove this infinite while(c != EOF) loop.
So it should rather look something like:
FILE *fp = fopen("secret.txt","r");
char guess[10];
const char secret[10];
int i;
if(fgets(secret, sizeof(secret), fp) == NULL)
{
printf("Error while reading file\n");
return -1;
}
fclose(fp);
for (i=0; i < 3; i++)
{
printf("Please guess the word: \n");
scanf("%s", guess);
if (strcmp(secret,guess) == 0)
{
printf("Your guess was correct");
return 0;
}
else
printf("Your guess was incorrect. Please try again\n");
}
return 0;
Your code is grossly off: you do not alter 'c' inside a loop, making it spin indefinitely. It's a good idea to sketch your algorithm on a piece of paper before you start coding. In your case, pseudocode should look like this:
Open file
Read the secret
Close file
Repeat three times:
--- Display the prompt
--- Read user input
--- If user input matches the secret, congratulate the user and exit.
Tell the user his guess was incorrect.
At this point, converting it to C should be more or less mechanical. Good luck!
while (c !=EOF)
{
if (strcmp(secret,guess)==0)
{
printf("Your guess was correct");
return 0;
}
else
{
printf("Your guess was incorrect. Please try again\n");
}
}
looks like an infinite loop to me

Resources