Is this a good scanf alternative? - c

void fill_boxes(box *boxes, int length)
{
char *n;
for (int i = 0; i < length; i++) {
printf("Enter box %d id: ", i+1);
//scanf("%d",&boxes[i].id);
fgets(n,25,stdin);
boxes[i].id = strtol(n,NULL,10);
printf("Enter box %d name: ", i+1);
//scanf(" %49[^\n]",boxes[i].name);
fgets(boxes[i].name,50,stdin);
}
}
I tried this method to replace scanf and it looks like the input() function in python where it takes the input as a string and then you convert it to whatever type you want, which is what I'm doing with fgets() then strtol().
I'm asking if this is a good alternative to scanf or are there better solutions available ?

fgets() is good, when passed valid arguments.
fgets(n,25,stdin) is bad in this code as the value of pointer n is indeterminate.
Instead of using pointer char *n;, use an ample sized array like char buffer[100];. I recommend 2x the expected max size.
Use sizeof to determine array size, not a magic number.
Check fgets() return value.
Avoid using both fgets() and scanf() in the same program. I recommend using fgets() only until your understand why scanf() is problematic.
void fill_boxes(box *boxes, int length) {
// char *n;
char buffer[100];
for (int i = 0; i < length; i++) {
printf("Enter box %d id: ", i+1);
// fgets(n,25,stdin);
if (fgets(buffer, sizeof buffer ,stdin) == NULL) {
printf("No valid input\n");
break;
}
boxes[i].id = strtol(n,NULL,10);
printf("Enter box %d name: ", i+1);
// fgets(boxes[i].name,50,stdin);
if (fgets(buffer, sizeof buffer ,stdin) == NULL) {
printf("No valid input\n");
break;
}
snprintf(boxes[i].name, sizeof boxes[i].name, "%[^\n]", buffer);
// or
buffer[strcspn(buffer, "\n")] = 0; // Lop off potential \n
snprintf(boxes[i].name, sizeof boxes[i].name, "%s", buffer);
}
}
Additional code concerns:
What should happen if the line of input is excessively long?
What should happen with errant input like "123 zxfc\n" for boxes[i].id?
fgets(boxes[i].name,50,stdin); is a problem as rarely is the trailing '\n' desired in boxes[i].name. Zeroing the '\n' after the read, limits reduces by 1, what could be stored in boxes[i].name.

Related

fgets() doesn't work as expected in C

Given the following code:
#include <stdio.h>
int main()
{
int testcase;
char arr[30];
int f,F,m;
scanf("%d",&testcase);
while(testcase--)
{
printf("Enter the string\n");
fgets(arr,20,stdin);
printf("Enter a character\n");
F=getchar();
while((f=getchar())!=EOF && f!='\n')
;
putchar(F);
printf("\n");
printf("Enter a number\n");
scanf("%d",&m);
}
return 0;
}
I want a user to enter a string, a character and a number until the testcase becomes zero.
My doubts / questions:
1.User is unable to enter a string. It seems fgets is not working. Why?
2.If i use scanf instead of fgets,then getchar is not working properly, i.e whatever character I input in it just putchar as a new line. Why?
Thanks for the help.
Mixing functions like fgets(), scanf(), and getchar() is error-prone. The scanf() function usually leaves a \n character behind in the input stream, while fgets() usually does not, meaning that the next call to an I/O function may or may not need to cope with what the previous call has left in the input stream.
A better solution is to use one style of I/O function for all user input. fgets() used in conjunction with sscanf() works well for this. Return values from functions should be checked, and fgets() returns a null pointer in the event of an error; sscanf() returns the number of successful assignments made, which can be used to validate that input is as expected.
Here is a modified version of the posted code. fgets() stores input in a generously allocated buffer; note that this function stores input up to and including the \n character if there is enough room. If the input string is not expected to contain spaces, sscanf() can be used to extract the string, leaving no need to worry about the newline character; similarly, using sscanf() to extract character or numeric input relieves code of the burden of further handling of the \n.
#include <stdio.h>
int main(void)
{
int testcase;
char arr[30];
char F;
int m;
char buffer[1000];
do {
puts("Enter number of test cases:");
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
/* handle error */
}
} while (sscanf(buffer, "%d", &testcase) != 1 || testcase < 0);
while(testcase--)
{
puts("Enter the string");
/* if string should not contain spaces... */
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
/* handle error */
}
sscanf(buffer, "%29s", arr);
printf("You entered: %s\n", arr);
putchar('\n');
puts("Enter a character");
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
/* handle error */
}
sscanf(buffer, "%c", &F);
printf("You entered: %c\n", F);
putchar('\n');
do {
puts("Enter a number");
if (fgets(buffer, sizeof buffer, stdin) == NULL) {
/* handle error */
}
} while (sscanf(buffer, "%d", &m) != 1);
printf("You entered: %d\n", m);
putchar('\n');
}
return 0;
}
On the other hand, if the input string may contain spaces, fgets() can read input directly into arr, but then the stored string will contain a \n character, which should probably be removed. One way of doing this is to use the strcspn() function to find the index of the \n:
#include <string.h> // for strcspn()
/* ... */
puts("Enter the string");
/* or, if string may contain spaces */
if (fgets(arr, sizeof arr, stdin) == NULL) {
/* handle error */
}
/* replace newline */
arr[strcspn(arr, "\r\n")] = '\0';
printf("You entered: %s\n", arr);
putchar('\n');
/* ... */
Note that a maximum width should always be specified when using %s with the scanf() functions to avoid buffer overflow. Here, it is %29s when reading into arr, since arr can hold 30 chars, and space must be reserved for the null terminator (\0). Return values from sscanf() are checked to see if user input is invalid, in which case the input is asked for again. If the number of test cases is less than 0, input must be entered again.
Finally got the solution how can we use scanf and fgets together safely.
#include <stdio.h>
int main()
{
int testcase,f,F,m;
char arr[30];
scanf("%d",&testcase);
while((f=getchar())!=EOF && f!='\n')
;
while(testcase--)
{
printf("Enter the string\n");
fgets(arr,30,stdin);
printf("Enter a character\n");
F=getchar();
while((f=getchar())!=EOF && f!='\n')
;
putchar(F);
printf("\n");
printf("Enter a number\n");
scanf("%d",&m);
while((f=getchar())!=EOF && f!='\n')
;
}
}
We need to make sure that before fgets read anything,flushout the buffer with simple while loop.
Thanks to all for the help.
A simple hack is to write a function to interpret the newline character. Call clear() after each scanf's
void clear (void){
int c = 0;
while ((c = getchar()) != '\n' && c != EOF);
}
Refer to this question for further explaination: C: Multiple scanf's, when I enter in a value for one scanf it skips the second scanf

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

Suggest an alternative for gets() function, using gcc compiler

Trying to input more than a single string in my program's strings array, for that used :
scanf("%80[^\r\n]", strings[i]);
fgets(string[i], MAXLEN, stdin);
a custom made function was also used:
int getString(char s[]) {
char ch;
int i=0;
while( (ch = getchar()) != '\n' && ch != EOF ) {
s[i] = ch;
++i;
}
s[i] = '\0';
fflush(stdin);
return i;
}
but unable to get input with more than one string each including white spaces
function gets() used to work earlier for me but since it is deprecated no alternative can be found
This is where it was used :
int getString(char s[]) {
char ch;
int i=0;
while( (ch = getchar()) != '\n' && ch != EOF ) {
s[i] = ch;
++i;
}
s[i] = '\0';
fflush(stdin);
return i;
}
struct vechileData
{
char vechileType[MAXLEN];
int begin_month;
int end_month;
double price;
} data[5];
int main(int argc, char const *argv[])
{
printf("Input Vechile data: \n");
int i=0;
while(i < 5) {
printf("Input vechile Type : \n");
fgets(data[i].vechileType, MAXLEN, stdin);
printf("Input begin month : \n");
scanf("%d", &data[i].begin_month);
printf("Input end monhth : \n");
scanf("%d", &data[i].end_month);
printf("Input price : \n");
scanf("%lf", &data[i].price);
++i;
}
printf("Input Vechile Type to display information about the vechile : \n");
char vech[MAXLEN];
fgets(vech, MAXLEN, stdin);
i=0;
while(i < 5) {
if (strcmp(vech,data[i].vechileType) == 0)
{
printf("vechileType: %s\n", data[i].vechileType);
printf("Begin month: %d\n", data[i].begin_month);
printf("End month: %d\n", data[i].end_month);
printf("Price : %lf\n", data[i].price);
}
++i;
}
return 0;
}
It skips the next input to string statement during run time, "seems to"
Your problem is really not a gets() issue.
None of the scanf("%d", ...) and scanf("%lf", ...) consume the '\n' after the number and thus contribute to your issue. It is the next read of stdin to take in the '\n'. So when the next car type is read, it gets the lingering '\n'. Your 2nd car type ends up being "\n".
Use of fgets(data[i].vechileType, MAXLEN, stdin); puts a '\n' in data[i].vechileType. You likely do not want this. Your former use of gets() consumed, but did not put the '\n' in its return.
I long ago gave up doing user input with scanf() due to these subtle issues.
Recommend to separate input from parsing, use fgets() and then sscanf(). Example:
char number[80];
if (fgets(number, sizeof(number), stdin)) {
sscanf(number, "%d", &x)
Your implementation of a gets() replacement differs as follows
1) It does not return s (or NULL or error/eof).
2) It does not set eof indicator on eof.
3) Should getchar() return a '\0', your while loop errantly continues.
Recommend that if you must replace gets(), do so via fgets().
#define My_gets_N (1024 /* Some BA number */)
char *My_gets(char * str) {
char buffer[My_gets_N];
char *retval = fgets(buffer, sizeof(My_gets_N), stdin);
if (retval) {
int l = strlen(buffer);
/* fgets() saves '\n', but gets() does not */
if ((l > 0) && (buffer[l-1] == '\n')) {
l--;
}
memcpy(str, buffer, l);
str[l] = '\0';
return str;
}
else {
return 0;
}
}
If you replacement solution needs to deal with string length > the fixed My_gets_N, other coding is needed.
You must be more specific about what went wrong with the fgets() approach, that's the one I would recommend and it does work.
Note that fgets() will input the entire line, including linefeed/carriage returns at the end, so you might need to clean those off if they're undesirable to keep.
I don't understand how gets() worked for you, despite the warning that practically every C book post K&R gives, as it's not only deprecated, but extremely dangerous to use. Like the others have said, fgets() would definitely work if you used it correctly.
Instead of replacing all the instances of uses of gets with fgets. Use following Macros:
#define TRUNCATE_NULL(strText) \
 { \
   int _strlen = strlen(strText); \
   if (_strlen > 0 && strText[_strlen - 1] == '\n') strText[_strlen - 1] = '\0'; \
   else while(fgetc(stdin)!='\n'); \
 }
#define gets(strText) fgets(strText, sizeof(strText), stdin); TRUNCATE_NULL(strText);
Why use fgets?
Because it is more secure than gets.
Is gets really insecure?
Yes. It is greedy indeed, it will accept as much food as you give, even if it can not eat.
So technically, as #halfer rightly commented below,
with the use of gets, program is prone to buffer overflow.
How ?
char name[5];
gets(name);
Now provide input of more than 5 characters, it will accept it. This would overwrite data from memory, which should not be overwritten this way.
Ok with fgets, but why use TRUNCATE_NULL macro ?
fgets is not perfect either. it will accept \n (Enter) as character to be placed in input name.So to remove unnecessary \n, and to make sure expected functionality of gets is achieved we can use it.
Actually, there you can use while((getchar())!='\n'); to avoid such type of problem and one thing there is no need to use of fflush(stdin) function.
Here's code you can use
#include<stdio.h>
#include<string.h>
#define MAXLEN 50
int getString(char s[])
{
char ch;
int i=0;
while( (ch = getchar()) != '\n' && ch != EOF )
{
s[i] = ch;
++i;
}
s[i] = '\0';
return i;
}
struct vechileData
{
char vechileType[MAXLEN];
int begin_month;
int end_month;
double price;
}data[5];
int main(int argc, char const *argv[])
{
printf("Input Vechile data: \n");
int i=0;
while(i < 2)
{
printf("Input vechile Type : \n");
fgets(data[i].vechileType, MAXLEN, stdin);
printf("Input begin month : \n");
scanf("%d", &data[i].begin_month);
printf("Input end monhth : \n");
scanf("%d", &data[i].end_month);
printf("Input price : \n");
scanf("%lf", &data[i].price);
while((getchar())!='\n');
++i;
}
printf("Input Vechile Type to display information about the vechile : \n");
char vech[MAXLEN];
fgets(vech, MAXLEN, stdin);
i=0;
while(i < 2)
{
if (strcmp(vech,data[i].vechileType) == 0)
{
printf("vechileType: %s\n", data[i].vechileType);
printf("Begin month: %d\n", data[i].begin_month);
printf("End month: %d\n", data[i].end_month);
printf("Price : %lf\n", data[i].price);
}
++i;
}
return 0;
}
I hope this will help you.....

C application skips my scanf calls

I'm trying to write code to count how many times a string repeats inside another one. (If there is some easier approach, please let me know.)
Here is the code that I have now:
int getStringLenght (char str[]) {
int lenghtOfTheString;
int i;
for (i = 0; i < 100; i++) {
if(str[i] == '\0') {
lenghtOfTheString = i;
break;
}
}
return lenghtOfTheString;
}
int main()
{
printf("Type a string: ");
char T[1024];
scanf("%s",&T);
char P[100];
printf("Type a substring: ");
scanf("%s",&P);
printf("%s",P);
int stringSize = getStringLenght (P);
int occurences = 0;
int i;
for (i = 0; i < 10; i++) {
int j;
if(T[i] == P[0]) {
for (j = 0;j<10;j++) {
char c1 = T[i+j];
char c2 = P[j];
if(c1 != c2) {
break;
}
if(j == stringSize-1) {
occurences++;
//printf("string iguais em i = %d",i);
}
}
}
}
printf("\nThe substring %s was found %d times", P, occurences);
return 0;
}
The app compiles. When I type "banana", for example, on the first scanf, and then "na" on the second, the app comes out with the right answer. But, if I type "banana and milk" on the first scanf, it automatically interprets the second scanf as "and", even when I don't type anything but "banana and milk ENTER"
What's happening?
scanf's "%s" conversion only reads characters until it encounters white-space (e.g., space, new-line, or tab). When you enter more than one word, it reads the first. The second call reads the second, and so on.
If you want to read an entire line, you usually want to use fgets instead (scanf can do the job as well, but it's a little trickier, and uses a feature of which many are unaware, so they often find it difficult to understand).
You don't understand how scanf works. http://www.cplusplus.com/reference/clibrary/cstdio/scanf/ %s will only read one string, terminated by white space. If you want to keep reading strings, or read a line, you have to keep using scanf until one of your strings ends in a new line or EOF, or use another function, like fgets.
You have to remember that many functions are already implemented. This is why your getStringLength (you have typo in it's name) is needless. You can simply check the string's length using strlen function from string.h. What is more when you import this file you also have access to strstr function which finds the first occurrence of a given substring in a string. Try to use them instead of reinventing the wheel ;)
That is a standart problem with scanf. There are 3 ways to fix this:
1: Call fflush after each scanf:
scanf("%s", some_string); // you don't need to write &some_string because giving a array to a function automatically converts it to a pointer
fflush(stdin);
fflush() isn't available on every system.
2: Putting scanf in a loop:
do
scanf("%s", somestring);
while (getchar() != '\n');
3: Don't use scanf! Use fgets and sscanf!
char buffer[100]; // buffer for fgets()
fgets(buffer, 100, stdin); // read a line from stdin (standart input) into buffer
sscanf(buffer, "%s", some_string); // convert buffer in any format you want

Reading in a variable length string user input in C

I am trying to read in a variable length user input and perform some operation (like searching for a sub string within a string).
The issue is that I am not aware how large my strings (it is quite possible that the text can be 3000-4000 characters) can be.
I am attaching the sample code which I have tried and the output:
char t[],p[];
int main(int argc, char** argv) {
fflush(stdin);
printf(" enter a string\n");
scanf("%s",t);
printf(" enter a pattern\n");
scanf("%s",p);
int m=strlen(t);
int n =strlen(p);
printf(" text is %s %d pattrn is %s %d \n",t,m,p,n);
return (EXIT_SUCCESS);
}
and the output is :
enter a string
bhavya
enter a pattern
av
text is bav 3 pattrn is av 2
Please don't ever use unsafe things like scanf("%s") or my personal non-favourite, gets() - there's no way to prevent buffer overflows for things like that.
You can use a safer input method such as:
#include <stdio.h>
#include <string.h>
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
static int getLine (char *prmpt, char *buff, size_t sz) {
int ch, extra;
// Get line with buffer overrun protection.
if (prmpt != NULL) {
printf ("%s", prmpt);
fflush (stdout);
}
if (fgets (buff, sz, stdin) == NULL)
return NO_INPUT;
// If it was too long, there'll be no newline. In that case, we flush
// to end of line so that excess doesn't affect the next call.
if (buff[strlen(buff)-1] != '\n') {
extra = 0;
while (((ch = getchar()) != '\n') && (ch != EOF))
extra = 1;
return (extra == 1) ? TOO_LONG : OK;
}
// Otherwise remove newline and give string back to caller.
buff[strlen(buff)-1] = '\0';
return OK;
}
You can then set the maximum size and it will detect if too much data has been entered on the line, flushing the rest of the line as well so it doesn't affect your next input operation.
You can test it with something like:
// Test program for getLine().
int main (void) {
int rc;
char buff[10];
rc = getLine ("Enter string> ", buff, sizeof(buff));
if (rc == NO_INPUT) {
// Extra NL since my system doesn't output that on EOF.
printf ("\nNo input\n");
return 1;
}
if (rc == TOO_LONG) {
printf ("Input too long [%s]\n", buff);
return 1;
}
printf ("OK [%s]\n", buff);
return 0;
}
In practice you shouldn't bother too much to be precise. Give yourself some slack to have some memory on the stack and operate on this. Once you want to pass the data further, you can use strdup(buffer) and have it on the heap. Know your limits. :-)
int main(int argc, char** argv) {
char text[4096];
char pattern[4096];
fflush(stdin);
printf(" enter a string\n");
fgets(text, sizeof(text), stdin);
printf(" enter a pattern\n");
fgets(pattern, sizeof(pattern), stdin);
int m=strlen(text);
int n =strlen(pattern);
printf(" text is %s %d pattrn is %s %d \n",text,m,pattern,n);
return (EXIT_SUCCESS);
}
Don't use scanf or gets for that matter because as you say, there is not real way of knowing just how long the input is going to be. Rather use fgets using stdin as the last parameter. fgets allows you to specify the maximum number of characters that should be read. You can always go back and read more if you need to.
scanf(%s) and gets read until they find a terminating character and may well exceed the length of your buffer causing some hard to fix problems.
The main problem in your case is having char arrays of unknown size. Just specify the array size on declaration.
int main(int argc, char** argv) {
int s1[4096], s2[4096];
fflush(stdin);
printf(" enter a string\n");
scanf("%s", s1);
printf(" enter a pattern\n");
scanf("%s", s2);
int m = strlen(s1);
int n = strlen(s2);
printf(" text is %s of length %d, pattern is %s of length %d \n", s1, m, s2, n);
return (EXIT_SUCCESS);
}

Resources