Difference between "gets(s);" and "scanf("%s", s);" in C - c

I am writing a program that allows users to enter five names and sorts the names in Alphabet order, two adjacent names seperated by a newline character. Here is my code:
void sortWords(char s[][100], int n){
int i, j;
char *str;
for(i = 0; i < n-1; i++){
for(j = n- 1; j > i; j--){
if(strcmp(s[j], s[j-1]) == -1){
strcpy(str, s[j]);
strcpy(s[j], s[j-1]);
strcpy(s[j-1], str);
}
}
}
}
int main(){
char s[5][100];
int i;
for(i = 0; i < 5; i++){
fflush(stdin);
//gets(s[i]); // when I use this statement, my program doesn't work
scanf("%s", s[i]);
}
sortWords(s, 5);
for(i = 0; i < 5; i++){
printf("%s ", s[i]);
}
return 0;
}
When I changed the "scanf" in function main to "gets", after I have entered 5 names, the program just didn't print anything. Can anyone explain it for me, because normally, when I change one of them to the other function, I just have same results.

allows users to enter five names
Names usually have a space between the parts of the full name. scanf("%s", s) does not read a full name, but only part of a name.
Code has many other problems too.
Difference between "gets(s);" and "scanf("%s", s);" in C
One reads a line the other reads a word.
gets(), since C11 (2011) is no longer part of the C standard library.
Both are bad as they do not limit input and so can suffer buffer overflow.
The obsolete gets() would read and save a line - input unto and including the '\n'. The '\n' is read, but not saved. If the prior input operation left a '\n' in stdin, then gets() reads a short line of "\n" and saves as "".
scanf("%s", s) reads and discards any number of leading white-space characters (perhaps multiple '\n') and then reads and saves non-white-spaces. A following white-space stops the reading, but it is returned to stdin for the next input function.
With common input, scanf("%s", s) typically the leaves the final '\n' in stdin for the next input operation. gets() consumes it.
Both append a null character to s if any reading occurred.
gets() returns a pointer. scanf() returns a conversion count.
Recommendations
Do not use either gets(s) nor scanf("%s", s) in production code. To read a line, research fgets(). To read a word, research using a width like char s[100]; scanf("%99s", s);.
Best to test the return value of I/O functions.
Do not mix fgets()/gets() with scanf() functions until you understand why that is bad.
Other
if(strcmp(s[j], s[j-1]) == -1) is poor. strcmp() returns some negative, zero or some positive to indicate order. Better to use if(strcmp(s[j], s[j-1]) < 0).
strcpy(str, s[j]); is bad as pointer str has not been assigned a value. Better as char str[100]; strcpy(str, s[j]);.

gets() reads a line, scanf("%s") reads a word, and both should not be used.
for details, read #chuxReinstateMonica's answer.

Related

Program seems to be ignoring one instance of 'gets()' and show an error message when the user hasn't interacted with the program [duplicate]

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(){
int n=1,i,cont;
char string[50];
scanf("%d",&n);
while(n!=0){
gets(string);
cont=0;
for(i=0;i<strlen(string);i++){
if(string[i]=='.'){
cont++;
}
}
if(cont%2==0){
printf("S\n");
}else{
printf("N\n");
}
scanf("%d",&n);
}
return 0;
}
My problem is quite simple but troublesome, I want to read an integer value n, and then read a string, after that read n again, but whenever I run the program, it only reads the string value... but if I digit 0 the program ends... it's like my scanf is within the gets function.
Mixing scanf with gets or fgets is troublesome because they each handle newlines differently.
Get rid of the gets call (which is unsafe anyway) and replace it with the following scanf call:
scanf("%49s", string);
This will read at most 49 characters into string (i.e. one less that its size).
From OP's comments, it sounds like the goal is to be able to read strings containing spaces. While there are ways to accomplish this using scanf(), it would be better to use fgets(), which is at the least less error-prone.
The fgets() function can be used to read input for the number into a buffer, and this buffer can then be processed by sscanf() to extract the number. Since fgets() keeps the newline character, it is not left behind to interfere with the next I/O operation.
But, when fgets() is used to get the string, since the newline is retained, it may be desirable to remove it. This can be accomplished in a number of ways, but here strcspn() is used to provide the index of the first \r or \n character encountered; a \0 character is then written to this location, removing the terminating newline from the string.
The code below illustrates these suggestions. Note that both buffer[] and string[] are generously allocated to accommodate reasonably large inputs. If a user enters a large number of characters (more than 999 in this case), the extra characters are left behind in the input stream for the next I/O function call. Also note that the main loop has been streamlined a bit; now there is a for(;;) loop that never terminates, broken out of when the user enters 0 for the number. And, there is a nested loop within the main loop that prompts the user to enter a number until a valid number is entered. Since the #include <stdlib.h> was unnecessary, it was removed. Better code would check the values returned from the calls to fgets() for possible errors.
#include<stdio.h>
#include<string.h>
int main(void)
{
int n = 1, cont;
char buffer[1000];
char string[1000];
for (;;) {
/* Loop until user enters a number */
do {
printf("Please enter a number: ");
fgets(buffer, sizeof buffer, stdin);
} while (sscanf(buffer, "%d", &n) != 1);
/* Break on 0 */
if (n == 0) break;
/* Get a string, and remove trailing newline */
printf("Please enter a string\n");
fgets(string, sizeof string, stdin);
string[strcspn(string, "\r\n")] = '\0';
cont = 0;
for (size_t i = 0; i < strlen(string); i++) {
if (string[i] == '.') {
cont++;
}
}
if (cont % 2 == 0){
printf("S\n");
} else {
printf("N\n");
}
}
return 0;
}
When you enter 5 for an example, you hit a new line character afterwards.
So you are entering 2 characters: 5 and a new line character.
That new line character is causing your headache.
The new line character is also considered an input.
In order to ignore this new line char, simply add a new line that acts as a garbage collection:
char garbage[50];
scanf( "%d", &n);
fgets(garbage, sizeof(garbage), stdin);

Why is the first value of my char array 10?

I'm new to programming but I wanted to make a program that gets as input a number, (length) and then stores a series of a's and b's of said length. Finally it should output the numbers as the ascii numbers. (so 97 and 98)
I thought I should malloc a char array of the size length and then do a for-loop over it and print everything as an integer.
The problem is however that I get a value 10 as the value of the first letter.
Thanks a lot for any help!
int main()
{
int length;
scanf("%d", &length);
char *matrix = malloc((length + 1 ) * sizeof(char));
for (int i = 0; i < length; i++)
{
scanf("%c", &matrix[i]);
}
for (int i = 0; i < length; i++)
{
printf("\n%d", matrix[i]);
}
return 0;
}
When inputting 3 on the first line and aba on the next line, I get 10 97 98.
However I expected it to be 97 98 97. Why do I get a value of 10 in the first place of the array?
Use
scanf(" %c", &matrix[i]);
^^^^
instead of
scanf("%c", &matrix[i]);
^^
When the format starts with a blank all white spaces are skipped.
From the C Standard (7.21.6.2 The fscanf function)
5 A directive composed of white-space character(s) is executed by
reading input up to the first non-white-space character (which remains
unread), or until no more characters can be read.
10 is the ASCII code of the (white space) new line character '\n' that was present in the input buffer after you entered the length of the array.
The first scanf() with the format string %d leaves a newline in the input buffer.
What happens here, is that your terminal collects input one full line at a time, passing it to the program, and then the scanf() only reads the digits from the buffer, leaving the newline character there for the next scanf() to see. The same would happen if you entered 10 abc: the space, abc and the newline would be left there.
This mismatch is not something people usually expect, and it's one of the things that makes scanf() annoying. I would suggest using fgets() instead to first read a full line, matching what the terminal gives, and then parse the number from it with sscanf() or strtol() (or atoi()).
This cleans up the issue at the point where the first line is read, instead of passing it on to the next input function to handle. Otherwise all your input functions are tied together, if the next input would be for a whole line with possible white space, you'd need to know if you expect to clear a pre-existing newline or not. (You could also replace the later scanf("%c") with getchar(), not that that matters with buffering though.)
That said, the scanf("%c")/getchar() loop may still see newlines if you enter lines that don't have as many characters as the loop expects, so if you don't want to see them at all, filter them out.
So, something like this:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int length;
char linebuf[100];
fgets(linebuf, 100, stdin);
length = strtol(linebuf, NULL, 10);
char *matrix = malloc(length + 1);
for (int i = 0; i < length; i++)
{
matrix[i] = getchar();
}
for (int i = 0; i < length; i++)
{
printf("\n%d", matrix[i]);
}
printf("\n");
return 0;
}
(The obvious downside of fgets() is that you have to decide on a maximum length for the input line, allocate a buffer and call another function in addition to it.)

Counting the Whole String Including The Spaces in C

I'm trying to set up a code that counts the whole string and doesn't stop after the first space that it finds. How do I do that?
I tried this kind of code but it just counts the first word then goes to display the number of letters in that first word.
So far this is what I have tried.
int main(){
char get[100];
int i, space=0, len=0, tot;
scanf("%s", get);
for (i=0; get[i]!='\0'; i++)
{
if (get[i] == ' ')
space++;
else
len++;
}
tot = space + len;
printf("%i", tot);
}
And
int main(){
char get[100];
int len;
scanf("%s", &get);
len = strlen(get);
printf("%i", len);
}
But would still get the same answer as the first one.
I expected that if the
input: The fox is gorgeous.
output: 19
But all I get is
input: The fox is gorgeous.
output: 3
strlen already includes spaces, since it counts the length of the string up to the terminating NUL character (zero, '\0').
Your problem is that that the %s conversion of scanf stops reading when it encounters whitespace, so your string never included it in the first place (you can verify this easily by printing out the string). (You could fix it by using different scanf conversions, but in general it's easier to get things right by reading with fgets – it also forces you to specify the buffer size, fixing the potential buffer overflow in your current code.)
The Answer by Arkku is correct in its diagnose.
However, if you wish to use scanf, you could do this:
scanf("%99[^\n]", get);
The 99 tells scanf not to read more than 99 characters, so your get buffer won't overflow. The [^\n] tells scanf to read any character until it encounters the newline character (when you hit enter).
As Chux pointed out, the code still has 2 issues.
When using scanf, it is always a good idea to check its return value, which is the number of items it could read. Also, indeed the \n remains in the input buffer when using the above syntax. So, you could do this:
if(scanf("%99[^\n]", get) == 0){
get[0] = 0; //Put in a NUL terminator if scanf read nothing
}
getchar(); //Remove the newline character from the input buffer
I will take one example to explain the concept.
main()
{
char s[20], i;
scanf("%[^\n]", &s);
while(s[i] != '\0') {
i++;
}
printf("%d", i);
return 0;
}
i have used c language and u can loop through the ending the part of the string and you will get the length. here i have used "EDIT SET CONVESRION METHOD" to read string, you can also gets to read.

scanf not reading properly because of gets function

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
int main(){
int n=1,i,cont;
char string[50];
scanf("%d",&n);
while(n!=0){
gets(string);
cont=0;
for(i=0;i<strlen(string);i++){
if(string[i]=='.'){
cont++;
}
}
if(cont%2==0){
printf("S\n");
}else{
printf("N\n");
}
scanf("%d",&n);
}
return 0;
}
My problem is quite simple but troublesome, I want to read an integer value n, and then read a string, after that read n again, but whenever I run the program, it only reads the string value... but if I digit 0 the program ends... it's like my scanf is within the gets function.
Mixing scanf with gets or fgets is troublesome because they each handle newlines differently.
Get rid of the gets call (which is unsafe anyway) and replace it with the following scanf call:
scanf("%49s", string);
This will read at most 49 characters into string (i.e. one less that its size).
From OP's comments, it sounds like the goal is to be able to read strings containing spaces. While there are ways to accomplish this using scanf(), it would be better to use fgets(), which is at the least less error-prone.
The fgets() function can be used to read input for the number into a buffer, and this buffer can then be processed by sscanf() to extract the number. Since fgets() keeps the newline character, it is not left behind to interfere with the next I/O operation.
But, when fgets() is used to get the string, since the newline is retained, it may be desirable to remove it. This can be accomplished in a number of ways, but here strcspn() is used to provide the index of the first \r or \n character encountered; a \0 character is then written to this location, removing the terminating newline from the string.
The code below illustrates these suggestions. Note that both buffer[] and string[] are generously allocated to accommodate reasonably large inputs. If a user enters a large number of characters (more than 999 in this case), the extra characters are left behind in the input stream for the next I/O function call. Also note that the main loop has been streamlined a bit; now there is a for(;;) loop that never terminates, broken out of when the user enters 0 for the number. And, there is a nested loop within the main loop that prompts the user to enter a number until a valid number is entered. Since the #include <stdlib.h> was unnecessary, it was removed. Better code would check the values returned from the calls to fgets() for possible errors.
#include<stdio.h>
#include<string.h>
int main(void)
{
int n = 1, cont;
char buffer[1000];
char string[1000];
for (;;) {
/* Loop until user enters a number */
do {
printf("Please enter a number: ");
fgets(buffer, sizeof buffer, stdin);
} while (sscanf(buffer, "%d", &n) != 1);
/* Break on 0 */
if (n == 0) break;
/* Get a string, and remove trailing newline */
printf("Please enter a string\n");
fgets(string, sizeof string, stdin);
string[strcspn(string, "\r\n")] = '\0';
cont = 0;
for (size_t i = 0; i < strlen(string); i++) {
if (string[i] == '.') {
cont++;
}
}
if (cont % 2 == 0){
printf("S\n");
} else {
printf("N\n");
}
}
return 0;
}
When you enter 5 for an example, you hit a new line character afterwards.
So you are entering 2 characters: 5 and a new line character.
That new line character is causing your headache.
The new line character is also considered an input.
In order to ignore this new line char, simply add a new line that acts as a garbage collection:
char garbage[50];
scanf( "%d", &n);
fgets(garbage, sizeof(garbage), stdin);

fgets() isn't prompting user a second time

Here is how the code's written.
int main()
{
char enteredName[30];
char stringNum[4];
char continueLetter = 0;
int continueProgram = 0;
int enteredAge;
int i;
do
{
memset(enteredName,'\0', 30);
printf("Please enter a name: ");
fgets(enteredName, 29, stdin);
printf("\n\nNow please enter your age: ");
fgets(stringNum, 3, stdin );
for(i = 0; i < 30; i++)
{
if (enteredName[i] == '\n')
{
enteredName[i] = '\0';
break;
}
}
for(i = 0; i < 4; i++)
{
if (stringNum[i] == '\n')
{
stringNum[i] = '\0';
break;
}
}
enteredAge = atol(stringNum);
} while();
When I run through the loop a second time, I'm not able to enter a new name into the char array, it just goes to the next prompt (the age). Unless this issue involves linked lists, the problem seems to be with something else. Could you help me find the error? Thanks!
Your second fgets call leaves characters (specifically the newline) waiting to be read from stdin if you enter a two digit age.
Increase the length parameter to match the array size:
fgets(stringNum, 4, stdin);
Or better:
fgets(stringNum, sizeof stringNum, stdin);
You probably want to do the same for enteredName.
From the fgets(3) man page:
The fgets() function reads at most one less than the number of characters
specified by size from the given stream and stores them in the string
str.
You don't need to reserve the extra array entry for the null-terminator like you're doing - fgets will handle that correctly on its own.
The problem is,you are not flushing the input buffer that is why the fgets() takes you directly to the second prompt asking age.This is common problem encountered,just add fflush(stdin);//my compiler supports itafter fgets();.Here is the code which has worked for me hope it works for you too :
EDIT: There is one very useful post providing information regarding fflush().As it is described that fflush is basically meant to be called to an output stream.Although some compilers provide support for flushing stdin,this is considered an undefined behavior.While having another look at the program, I found out that using sizeof can work wonders and is valid, So, I have modified the program for better. The use of sizeof is also described in one of the answers here.
#include<stdio.h>
#include<stdlib.h>
int main()
{
char enteredName[30];
char stringNum[4];
int continueProgram=0;
int i;
while(continueProgram<3)
{
setbuf(stdout,NULL);
printf("Please enter a name: ");
fgets(enteredName, sizeof enteredName, stdin);
printf("\n\nNow please enter your age: ");
fgets(stringNum,sizeof stringNum, stdin );
for(i = 0; i < 30; i++)
{
if (enteredName[i] == '\n')
{
enteredName[i] = '\0';
break;
}
}
for(i = 0; i < 4; i++)
{
if (stringNum[i] == '\n')
{
stringNum[i] = '\0';
break;
}
}
//enteredAge = atol(stringNum);
continueProgram++;
}
return 0;
}
The problem is that you don't know whether the string that has been read contains a newline or not. If it doesn't contain a newline, then this is going to be read by the next call to fgets, leaving an empty string in it. To prevent it, check if the line contains a newline character at the end. If not just read it using getchar() and Voila!!(Note that this solution is valid only to your problem, not in general). This code is to be added after reading the stringNum string.
if(stringNum[strlen(stringNum)-1]!='\n')
{
getchar();
}
This was happening because, if the age is a double digit, then fgets is going to read until the last digit and not the newline character. So,you need to read it in case the last char is not \n. If the age is a single digit, the the original program works fine.
You try this following piece of code:
if(stringNum[strlen(arr)-1]=='\n')
stringNum[strlen(arr)-1]='\0';
else
while(getchar()!='\n');
Whenever you enter a two digit age, the newline character which you insert while pressing enter gets stored in the buffer.
What this above piece of code is doing is that, it will check whether the last character of your storage is filled with a newline character, if yes, then it will replace it with the null terminator.
Else, it will keep reading from the buffer until and unless the newline character is removed from the buffer.
PS: If you are using borland then you will have fflush(stdin) to flush out any extra character from the buffer as indicated by PHlFounder, but if you happen to use gcc then this method is very good.
Also you can create a function or macro for this piece of code and call it every time you need, for eg.
void function(char * arr)
{
if(arr[strlen(arr)-1]=='\n')
arr[strlen(arr)-1]='\0';
else
while(getchar()!='\n')
}

Resources