I am currently working on an practice. My program is working and I just want to make it more robust and foolproof. The code is of the following:
printf("Enter Name : ");
memset(userinput, '\0', 50);
fgets(userinput, 50, stdin);
I accidentally hit the enter key(newline) and for my program, the system just dangle there and couldn't accept anymore inputs. I am only allowed to use fgets. So is there any way of rejecting \n from being entered as a field?
One way you could do it is to check if the first character is a newline:
do {
printf("Enter Name : ");
memset(userinput, '\0', 50);
fgets(userinput, 50, stdin);
}
while ( userinput[0] == '\n');
printf("Hello %s", userinput);
This would still leave you up to space + newline, but its a first start.
So is there any way of rejecting \n from being entered as a field?
... to make it more robust and foolproof.
Code should not prevent '\n' from being read via fgets(). Instead, code should assess that input for validity. A leading '\n' is likely only 1 concern. Make code easy to update as certainly the criteria for a valid name will evolve.
I recommend a separate name test.
bool ValidNameInputTest(const char *userinput) {
if (*userinput == '\n') return false; // fail
// add other tests as needed
return true;
}
In a loop, test and repeat as needed. Code should exit the loop on success. When an end-of-file or input error occurs, code should detect/handle that too.
...
char userinput[50];
do {
printf("Enter Name : ");
fflush(stdout); // add to insure above output is seen before reading input
//memset(userinput, '\0', 50);// Commented out as not needed - although useful for debug
// Check return value and used a derived size
//fgets(userinput, 50, stdin);
if (fgets(userinput, sizeof userinput, stdin) == NULL) {
Handle_EndOfFile_or_Error();
}
} while (!ValidNameInputTest(userinput));
Related
I am trying to make a program which will store the data entered by the user in a text file whose name is provided by the user. Program will terminate when the user enters exit. strcmp function of string.h is used for string comparison and fgets() is used to read data from stdin.
Here is my code.
#include<stdio.h>
#include<string.h>
void main()
{
char file[60]; // will store file name
printf("Enter file name: ");
fgets(file, 59, stdin);
FILE *fp = fopen(file, "a+"); // open file in append mode
if(fp == NULL){
printf("File not found !");
return;
}
char data[100];
printf("Enter some data to add to file(exit to terminate): ");
fgets(data, 99, stdin);
int flag = strcmp(data, "exit");
while(flag != 0){
fputs(data, fp);
fgets(data, 59, stdin);
flag = strcmp(data, "exit");
printf("%d\n", flag); // for checking whether string are correctly comapred or not
}
printf("Bye");
}
Program does not terminate even if i enter exit. I have also tried concatenating "\n" at the end of string input by user but that also doesn't help. Although, gets() function works fine, but i know it is not preferred to use to I shifted to fgets() but it doesn't work for me.
Check the man page for fgets(), it reads and stores the newline (caused by pressing ENTER) after the entered input. Thus, the strcmp() fails.
You have to manually strip the input buffer off the newline, before you can compare the input. A simple yet elegant way of doing that would be
data[strcspn(data, "\n")] = 0;
fgets reads in a complete "line", i.e. a sequence of characters until (and including!) a new line character. Hence, when a user presses "Enter", the new line will be part of the string read in and a strcmp(data,"exit") will evaluate to "not equal".
So either strip off the new line before comparison, or compare with a string literal including a new line. Since you write the data as is(i.e. including the new lines) to a file, it seems cumbersome to first strip the new line off and add it then in the output manually. So I'd actually suggest the second approach:
fgets(data, 100, stdin);
flag = strcmp(data, "exit\n");
An alternative would be to use strstr if excess characters do not matter (i.e. your program would exit if the user types "exit" or "asdfexitasdf". - both of which contain "exit".)
So
int flag = strstr(data, "exit");
if(flag != NULL)
//exit the program
else
//stay in the program
I'm currently practicing File I/O in C. I created a program where I get a file and extract data from it and I would like an option to change the file that is being read. The problem that I'm encountering is that for example I have two files: sample1.txt and sample2.txt. If I chose sample1.txt as the first file to be read and then I wanted to change the file to sample2.txt what ends up happening is that the filename does not change to sample2.txt but instead always stays what ever filename the first file has.
Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <assert.h>
#include <time.h>
#include <ctype.h>
int main()
{
char file_location[100]={0};
char new_location[100]={0};
FILE* fPointer=NULL;
int choice;
printf("Enter the filename that you wish to open.\n");
scanf("%[^\n]s",file_location); // I enter sample1.txt//
printf("%s\n",file_location);
fPointer=fopen(file_location,"r"); // success,sample1.txt is currently being read.//
if (fPointer==NULL)
{
printf("File error!,invalid file name! program will now exit.\n");
exit(0);
}
else
{
printf("Success!\n");
}
printf("Do you want to change the file being read\n); //Now I want to change the file,from sample1.txt to sample2.txt//
prinft("Enter 1 to change, 0 to exit the program\n);
do{
scanf("%d",&choice);
printf("You entered %d\n",choice);
if(choice<0||choice>1)
{
printf("Error,please choose between 1 and 0\n");
}
}while(choice!=1||choice!=0);
switch(choice) // I enter 1,go to case1//
{
case 0:
printf("Exiting program now\n");
exit(0);
break;
case 1:
fclose(fPointer);
printf("Enter the filename that you wish to open.\n");
scanf("%[^\n]s",new_location); //scanf does not even prompt me to enter a string.//
printf("%s\n",new_location); //nothing prints//
fPointer=fopen(new_location,"r"); // fpointer still points to sample1.txt//
break;
}
return 0;
}
Could anyone explain to me why my code keeps failing?
Any constructive criticism ,notes about File I/O is appreciated.
The fundamental problem is that your code mishandles newlines. This method of obtaining input probably does not do what you think it does:
scanf("%[^\n]s",new_location);
First, there is no reason for the trailing s here. Second, this will leave a trailing newline behind in the input stream. This newline is ignored when you call scanf("%d",&choice);, since the %d conversion specifier skips over leading whitespace characters. But, this second call to scanf() also leaves a newline character behind, and this is not ignored by the final call to scanf(), since %[^\n] tells scanf() to read characters until a newline is encountered.
One fix is to simply remove the scanset from your scanf() statements:
scanf("%s", new_location);
This will ignore leading whitespace characters, so the newline left behind from the previous call to scanf() will be ignored.
Also, when you open the new file, you are using file_location instead of new_location.
You will need to change your do loop condition to:
while(choice != 1 && choice != 0);
since you want to repeat the loop only if the user enters a number that is both not 0 and not 1.
If you want to read lines that include spaces, which is what [^\n] is sometimes used for, a better alternative is to use fgets(). This function keeps the newline, so you will need to remove it from the filenames. Also, if you are using fgets() to get the strings, it is best to use fgets() to get the numeric input, by reading the user input into a buffer and using sscanf() to parse it. Then your input code would look like:
/* Get first filename, and remove the newline */
printf("Enter the filename that you wish to open.\n");
fgets(file_location, sizeof file_location, stdin);
file_location[strcspn(file_location, "\r\n")] = '\0'; // remove newline
...
/* Get numeric input; this keeps the newline, so it does not interfere
* with the next call to `fgets()` */
char buffer[100];
fgets(buffer, sizeof buffer, stdin);
sscanf(buffer, "%d", &choice);
...
/* Get the new filename, and remove the newline */
printf("Enter the filename that you wish to open.\n");
fgets(new_location, sizeof new_location, stdin);
new_location[strcspn(new_location, "\r\n")] = '\0'; // remove newline
I'm programming this code (personal software) for a spa which is still in a beta version but I encounter a issue well is more like an idea than an issues let me explain you:
Source code:
fflush(stdin);
gets(NC1.Customer_Nameandlastname);
fflush(stdin);
printf("provide the customer's age\n\t");
fflush(stdin);
scanf("%d",&NC1.Customer_Age);
fflush(stdin);
this is just part of the source code but what I want to do when the program is running is this:
If the the person that is typing the information makes a mistake or wants to retype the same information but the next command line is already waiting for the input data the question is how would I do to go back to the previous line and then after I finish continue to the next line?
So it is like how would I return to the previous scanf() if I already type that information and then the system is waiting for the next line.
Please help me because I dont really know what yo do I am seeking how to do it but I still not able to find it.
You cannot portably flush input from stdin with fflush(stdin);. It invokes undefined behavior.
You can read the rest of an offending line from stdin with this function:
void flush_line(FILE *fp) {
int c;
while ((c = getc(fp)) != EOF && c != '\n')
continue;
}
Furthermore, do not use gets(). This unsafe function (you cannot provide the size of the destination array, so any properly crafted input may cause undefined behavior, a flaw that can be used by an attacker to compromise your program). The function was finally removed from the C Standard in 2011. Use fgets() and remove the trailing linefeed if any this way:
if (fgets(line, sizeof line, stdin)) {
line[strcspn(line, "\n")] = '\0';
...
}
You cannot restart a failed scanf(). The return value gives you some information as to where it failed, but not enough to restart the parse reliably. A better way to parse standard input is to read line by line and use sscanf() to parse the lines. Example:
/* read the customer's age. Keep prompting in case of invalid input */
for (;;) {
char line[80];
printf("provide the customer's age\n\t");
/* force flush of stdout for systems that do not do it
automatically upon reading from stdin */
fflush(stdout);
if (!fgets(line, sizeof line, stdin)) {
/* end of file reached */
return -1;
}
if (sscanf(line, "%d", &NC1.Customer_Age) == 1) {
/* input parsed correctly, break out to the next question */
break;
}
/* parse failed, output an appropriate message and restart */
printf("bad input, please type a number\n");
}
This is really a basic problem, what you want is a loop like this
while (fgets(buffer, SIZE_OF_BUFFER, stdin) != NULL) {
if (is_input_valid(buffer) == 1)
break;
/* Or else, go again */
}
After asking the user for all the details, ask something like
Do you want to store this record in the database?
Instruct the user that if he makes a mistake, he should enter garbage for the following fields, and answer "no, I want to discard this record".
Maybe the user will understand by himself that this is what he should do, because asking for such confirmation is a pretty standard practice.
I'm iterating through a loop. On the first iteration, i can write on the stdin and get the data I want. on the second operation: name is skipped, and it asks me for name2. Why is it skipping name?
for (i = 0; TRUE; i++) {
printf("> nom :");
fgets(items[i].name, 15, stdin);
printf("nom: %s\n", items[i].name);
if (items[i].name[0] == '.') break;
printf("> prenom : ");
fgets(items[i].name2, 15, stdin);
printf("name2: %s\n", items[i].name2);
}
The code you presented does not prompt for the name, even on the first iteration of the loop. If you get any prompting for it at all then that is happening before entering the loop. In contrast, the code presented does prompt for name2.
Each iteration of the loop begins by reading the next name (without prompting) and printing it. If you see different behavior then you are running different code.
Sorry for the bad title, but i didn't know a better one!
Target: I'm trying to make an command handler. So I'm printing out via printf("cmd: ") and listing on stdin via fgets(). If theres an Input I'm check on commands via if .. else if. So my Problem now: If there is no input on stdin it should repeat the function and print cmd!
int cmd_handler()
{
printf("cmd: ");
char command[LINE_MAX];
fgets(command, LINE_MAX, stdin);
if(command != NULL)
{
if(strcmp(command, "xyz"))
{
xyz();
}
}
return 0;
}
I really don't know how i can arrange that. simple call cmd_handler() on else isn't working. Maybe someone can give me a tip how to solve it.
EDIT:
It should look like this, if there is no input(2x for example) on stdin:
cmd:
cmd:
cmd:
THIS CODE ISN'T THE REAL ONE!
regards
You need a loop. I would suggest a while loop with an exit condition, perhaps set by an "exit" command.
int run = 1;
while (run) {
printf("cmd: ");
...
else if (strcmp(command, "exit") == 0) {
run = 0;
}
}
You need to check whether fgets() returns NULL, not whether command is NULL:
if (fgets(command, LINE_MAX, stdin) != NULL)
Then you can add an 'else' clause to handle the error condition, as you were trying to do.
The way you check for empty string is not going to work.
if(command != NULL)
This condition will always evaluate to true as command, in the expression, gets converted to a pointer and is always non-null.
To check if there are any alpha-numeric characters, use isalnum() from <ctype.h> and ensure there's no whitespace characters in command. You seem to want to use recursion whereas a loop is probably more suited.
char command[LINE_MAX];
int alnum=0;
while(1) {
alnum=0;
printf("cmd: ");
fgets(command, LINE_MAX, stdin);
for(i=0;i<strlen(command);i++)
if(isalnum(command[i])) {
alnum=1;
break;
}
if(!alnum) continue;
if(strcmp(command, "xyz"))
{
xyz();
}
....
break;
}
This way, you can ensure it handles any whitespace you may input. But rest of your strcmp(command"xyy") will fail if user inputs " xyz". So it may suffice to check whether user simply hits ENTER:
if(command[0] == '\n') continue;
instead of the above check using alnum().