I'm writing a very small program in C that needs to check if a certain string is empty. For the sake of this question, I've simplified my code:
#include <stdio.h>
#include <string>
int main() {
char url[63] = {'\0'};
do {
printf("Enter a URL: ");
scanf("%s", url);
printf("%s", url);
} while (/*what should I put in here?*/);
return(0);
}
I want the program to stop looping if the user just presses enter without entering anything.
Since C-style strings are always terminated with the null character (\0), you can check whether the string is empty by writing
do {
...
} while (url[0] != '\0');
Alternatively, you could use the strcmp function, which is overkill but might be easier to read:
do {
...
} while (strcmp(url, ""));
Note that strcmp returns a nonzero value if the strings are different and 0 if they're the same, so this loop continues to loop until the string is nonempty.
Hope this helps!
If you want to check if a string is empty:
if (str[0] == '\0')
{
// your code here
}
If the first character happens to be '\0', then you have an empty string.
This is what you should do:
do {
/*
* Resetting first character before getting input.
*/
url[0] = '\0';
// code
} while (url[0] != '\0');
You can check the return value from scanf. This code will just sit there until it receives a string.
int a;
do {
// other code
a = scanf("%s", url);
} while (a <= 0);
Typically speaking, you're going to have a hard time getting an empty string here, considering %s ignores white space (spaces, tabs, newlines)... but regardless, scanf() actually returns the number of successful matches...
From the man page:
the number of input items successfully matched and assigned, which can be fewer than provided for, or even zero in the event of an early matching failure.
so if somehow they managed to get by with an empty string (ctrl+z for example) you can just check the return result.
int count = 0;
do {
...
count = scanf("%62s", url); // You should check return values and limit the
// input length
...
} while (count <= 0)
Note you have to check less than because in the example I gave, you'd get back -1, again detailed in the man page:
The value EOF is returned if the end of input is reached before either the first successful conversion or a matching failure occurs. EOF is also returned if a read error occurs, in which case the error indicator for the stream (see ferror(3)) is set, and errno is set indicate the error.
strlen(url)
Returns the length of the string. It counts all characters until a null-byte is found. In your case, check it against 0.
Or just check it manually with:
*url == '\0'
You can try like this:-
if (string[0] == '\0') {
}
In your case it can be like:-
do {
...
} while (url[0] != '\0')
;
First replace the scanf() with fgets() ...
do {
if (!fgets(url, sizeof url, stdin)) /* error */;
/* ... */
} while (*url != '\n');
The shortest way to do that would be:
do {
// Something
} while (*url);
Basically, *url will return the char at the first position in the array; since C strings are null-terminated, if the string is empty, its first position will be the character '\0', whose ASCII value is 0; since C logical statements treat every zero value as false, this loop will keep going while the first position of the string is non-null, that is, while the string is not empty.
Recommended readings if you want to understand this better:
C strings: https://www.tutorialspoint.com/cprogramming/c_strings.htm
C arrays: https://www.tutorialspoint.com/cprogramming/c_arrays.htm
Relation between arrays and pointers: https://www.programiz.com/c-programming/c-pointers-arrays
Logical operators: https://www.tutorialspoint.com/cprogramming/c_logical_operators.htm
I've written down this macro
#define IS_EMPTY_STR(X) ( (1 / (sizeof(X[0]) == 1))/*type check*/ && !(X[0])/*content check*/)
so it would be
while (! IS_EMPTY_STR(url));
The benefit in this macro it that it's type-safe. You'll get a compilation error if put in something other than a pointer to char.
It is very simple.
check for string empty condition in while condition.
You can use strlen function to check for the string length.
#include<stdio.h>
#include <string.h>
int main()
{
char url[63] = {'\0'};
do
{
printf("Enter a URL: ");
scanf("%s", url);
printf("%s", url);
} while (strlen(url)<=0);
return(0);
}
check first character is '\0'
#include <stdio.h>
#include <string.h>
int main()
{
char url[63] = {'\0'};
do
{
printf("Enter a URL: ");
scanf("%s", url);
printf("%s", url);
} while (url[0]=='\0');
return(0);
}
For your reference:
C arrays:
https://www.javatpoint.com/c-array
https://scholarsoul.com/arrays-in-c/
C strings:
https://www.programiz.com/c-programming/c-strings
https://scholarsoul.com/string-in-c/
https://en.wikipedia.org/wiki/C_string_handling
Verified & Summary:
check C string is Empty
url[0] == '\0'
strlen(url) == 0
strcmp(url, "") == 0
check C string Not Empty
url[0] != '\0'
strlen(url) > 0
strcmp(url, "") != 0
With strtok(), it can be done in just one line: "if (strtok(s," \t")==NULL)".
For example:
#include <stdio.h>
#include <string.h>
int is_whitespace(char *s) {
if (strtok(s," \t")==NULL) {
return 1;
} else {
return 0;
}
}
void demo(void) {
char s1[128];
char s2[128];
strcpy(s1," abc \t ");
strcpy(s2," \t ");
printf("s1 = \"%s\"\n", s1);
printf("s2 = \"%s\"\n", s2);
printf("is_whitespace(s1)=%d\n",is_whitespace(s1));
printf("is_whitespace(s2)=%d\n",is_whitespace(s2));
}
int main() {
char url[63] = {'\0'};
do {
printf("Enter a URL: ");
scanf("%s", url);
printf("url='%s'\n", url);
} while (is_whitespace(url));
return 0;
}
Related
I have this code and I need help converting the comments to c code
// if the input of scanf() is "q"
{
break;
}
else
{
// convert to int
}
Firstly, how do I check if an input is a certain character. Secondly, how do I turn a string into an integer. Example: "123" -> 123
Things I've tried, that didn't work: (it is possible that I implemented these solutions incorrectly)
how does scanf() check if the input is an integer or character?
Convert char to int in C and C++
I am not using any standard libraries except for stdio.h to print some logging information on the window
you have to know also that any string is terminated by null character which is '\0' to indicate the termination of the string , also you have to check is the user entered characters not numbers and so on (that's not implemented in this code).
I also handled if negative numbers are entered.
but you have to handle if the user entered decimals numbers , to sum up . there are so many cases to handle.
and here the edited code :
#include <stdio.h>
int main(){
char inputString[100];
printf("enter the input:\n");
scanf("%s", &inputString);
if(inputString[0] == 'q' && inputString[1] == '\0' )
{
printf("quiting\n");
//break;
}
else {
int i = 0;
int isNegative = 0;
int number = 0;
// check if the number is negative
if (inputString[0] == '-') {
isNegative = 1;
i = 1;
}
// convert to int
for ( ;inputString[i] != '\0' ; i++) {
number *= 10;
number += (inputString[i] - '0');
}
if(isNegative == 1)
number *= -1;
printf("you entered %d\n", number);
}
return 0;
}
The fundamental question here is, Do you want to use scanf?
scanf is everyone's favorite library function for easily reading in values. scanf has an input specifier, %d, for reading in integers.
And it has a different input specifier, %s, for reading in arbitrary strings.
But scanf does not have any single input specifier that means, "Read in an integer as an integer if the user types a valid integer, but if the user types something like "q", have a way so I can get my hands on that string instead."
Unless you want to move mountains and implement your own general-purpose input library from scratch, I think you have basically three options:
Use scanf with %d to read integers as integers, but check scanf's return value, and if scanf fails to read an integer, use that failure to terminate input.
Use scanf with %s to read the user's input as a string, so you can then explicitly test if it's a "q" or not. If not, convert it to an integer by hand. (More on this below.)
Don't use scanf at all. Use fgets to read the user's input as a whole line of text. Then see if it's a "q" or not. If not, convert it to an integer by hand.
Number 1 looks something like this:
while(some loop condition) {
printf("enter next integer, or 'q' to quit:\n");
if(scanf("%d", &i) != 1) {
/* end of input detected */
break;
}
do something with i value just read;
}
The only problem with this solution is that it won't just stop if the user types "q", as your original problem statement stipulated. It will also stop if the user types "x", or "hello", or control-D, or anything else that's not a valid integer. But that's also a good thing, in that your loop won't get confused if the user types something unexpected, that's neither "q" nor a valid integer.
My point is that explicitly checking scanf's return value like this is an excellent idea, in any program that uses scanf. You should always check to see that scanf succeeded, and do something different if it fails.
Number 2 would look something like this:
char tmpstr[20];
while(some loop condition) {
printf("enter next integer, or 'q' to quit:\n");
if(scanf("%19s", tmpstr) != 1) {
printf("input error\n");
exit(1);
}
if(strcmp(tmpstr, "q") == 0) {
/* end of input detected */
break;
}
i = atoi(tmpstr); /* convert string to integer */
do something with i value just read;
}
This will work well enough, although since it uses atoi it will have certain problems if the user types something other than "q" or a valid integer. (More on this below.)
Number 3 might look like this:
char tmpstr[20];
while(some loop condition) {
printf("enter next integer, or 'q' to quit:\n");
if(fgets(tmpstr, 20, stdin) == NULL) {
printf("input error\n");
exit(1);
}
if(strcmp(tmpstr, "q\n") == 0) {
/* end of input detected */
break;
}
i = atoi(tmpstr); /* convert string to integer */
do something with i value just read;
}
One thing to note here is that fgets includes the newline that the user typed in the string it returns, so if the user types "q" followed by the Enter key, you'll get a string back of "q\n", not just "q". You can take care of that either by explicitly looking for the string "q\n", which is kind of lame (although it's what I've done here), or by stripping the newline back off.
Finally, for both #2 and #3, there's the question of, what's the right way to convert the user's string to an integer, and what if it wasn't a valid integer? The easiest way to make the conversion is to call atoi, as my examples so far have shown, but it has the problem that its behavior on invalid input is undefined. In practice, it will usually (a) ignore trailing nonnumeric input and (b) if there's no numeric input at all, return 0. (That is, it will read "123x" as 123, and "xyz" as 0.) But this behavior is not guaranteed, so these days, most experts recommend not using atoi.
The recommended alternative is strtol, which looks like this:
char *endp;
i = strtol(tmpstr, &endp, 10); /* convert string to integer */
Unlike atoi, strtol has guaranteed behavior on invalid input. Among other things, after it returns, it leaves your auxiliary pointer endp pointing at the first character in the string it didn't use, which is one way you can determine whether the input was fully valid or not. Unfortunately, properly dealing with all of the ways the input might be invalid (including trailing garbage, leading garbage, and numbers too big to convert) is a surprisingly complicated challenge, which I am not going to belabor this answer with.
Here are some guidelines:
scanf("%s", &var) is incorrect: you should pass the maximum number of characters to store into the array var and pass the array without the & as it will automatically convert to a pointer to its first element when passed as an argument:
char var[100];
if (scanf("%99s", var) != 1) {
printf("premature end of file\n");
return 1;
}
to compare the string read to "q", you can use strcmp() declared in <string.h>:
if (strcmp(var, "q") == 0) {
printf("quitting\n");
return 0;
}
to convert the string to the number it represents, use strtol() declared in <stdlib.h>:
char *p;
long value = strtol(var, &p, 0);
testing for a proper conversion is tricky: strtol() updated p to point to the character after the number and set errno in case of range error:
errno = 0;
char *p;
long value = strtol(var, &p, 0);
if (p == var) {
printf("not a number: %s\n", p);
return 1;
}
if (*p != '\0') {
printf("extra characters: %s\n", p);
return 1;
}
if (errno) {
printf("conversion error: %s\n", strerror(errno));
return 1;
}
printf("the number entered is: %ld\n", value);
return 0;
Here is a complete program:
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
char var[100];
char *p;
long value;
printf("Enter a number: ");
if (scanf("%99s", var) != 1) {
printf("premature end of file\n");
return 1;
}
if (strcmp(var, "q") == 0) {
printf("quitting\n");
return 0;
}
errno = 0;
value = strtol(var, &p, 0);
if (p == var) {
printf("not a number: %s\n", p);
return 1;
}
if (*p != '\0') {
printf("extra characters: %s\n", p);
return 1;
}
if (errno) {
printf("conversion error: %s\n", strerror(errno));
return 1;
}
printf("the number entered is: %ld\n", value);
return 0;
}
You can try this: (Assuming only positive integers needs to convert)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
// Write C code here
char var[100];
int numb_flag=1, i=0,number=0;
scanf("%s",var);
while(var[i]!='\0') { // Checking if the input is number
if(var[i]>=48 && var[i]<=57)
i++;
else {
numb_flag = 0;
break;
}
}
if(numb_flag==1) {
number = atoi(var);
printf("\nNumber: %d",number);
} else {
printf("\nNot A Number");
}
return 0;
}
//Mind that in order to be more precise you could also use atof().
//The function works the same way as atoi() but is able to convert float
// (returned value is 'double') the string s
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXILEN 100 /*change the value based on your needs*/
int main(){
int n, lim = MAXILEN;
char s[MAXILEN], *p = s, c;
/*taking the string from the input -- I do not use scanf*/
while(--lim > 0 && (c = getchar()) != EOF && c != '\n')
*p++ = c;
//here you can also do the check (if you want the '\n' char):
//if(c == '\n')
// *s++ = c;
*p = '\0';
if(s[0] == 'q' && s[1] == '\0')
exit(EXIT_SUCCESS); /*change the argument based on your needs*/
else
n = atoi(s);
printf("[DEBUG]: %d\n", n);
}
I have this C program in which the last position in the *args array must be NULL. The NULL isn't being assigned or maybe printed? Is it because "%s" doesn't work with NULL?
In the program below I'm splitting a user inputted string and assigning each token to the *args array of pointers. The last element shall be NULL.
As noted above you don't count the NULL (unless it was the first one; bug) so this means args[counter -1 ] will be the last non-NULL entry when you print it. Here are some issues that I fixed:
Replaced run flag with a break, which eliminated the need including stdbool
args is subject to overflow
It doesn't make sense to do a bunch of work on exit so moved that to right after input
streamline strtok call, and fixed defect if first call returns NULL
Prettify output including changing message "last character" to "last string".
Replaced the two magic values of 81 with defines.
And a few issues not fixed:
You use both a terminating null and a counter to significant number of elements in args. Choose one or the other.
scanf is subject to buffer overflow, replace it with fgets() and post-processing of input to to get the result of the format string.
scanf("%[^\n]%*c", input); fails to read anything when input is "\n". It also lacks a width limit (#chux-ReinstateMonica).
#include <stdio.h>
#include <string.h>
#define MAX_INPUT 81
#define MAX_ARGS 81
int main() {
for(;;) {
char input[MAX_INPUT];
scanf("%[^\n]%*c", input);
if(!strcmp(input, "Exit")) break;
int counter = 0;
char *token;
char *args[MAX_ARGS];
do {
token = strtok(counter ? NULL : input, " ");
args[counter] = token;
if(token) counter++;
} while(token && counter < MAX_ARGS);
if(counter == MAX_ARGS) {
counter--;
args[counter] = NULL;
}
printf("\nlast string: %s\n", args[counter - 1]);
for(unsigned i=0; i < counter; i++) {
printf("%d %s\n", i, args[i]);
}
printf("\n");
}
return 0;
}
Hi folks thanks in advance for any help, I'm doing the CS50 course i'm at the very beginning of programming.
I'm trying to check if the string from the main function parameter string argv[] is indeed a number, I searched multiple ways.
I found in another topic How can I check if a string has special characters in C++ effectively?, on the solution posted by the user Jerry Coffin:
char junk;
if (sscanf(str, "%*[A-Za-z0-9_]%c", &junk))
/* it has at least one "special" character
else
/* no special characters */
if seems to me it may work for what I'm trying to do, I'm not familiar with the sscanf function, I'm having a hard time, to integrate and adapt to my code, I came this far I can't understand the logic of my mistake:
#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
int numCheck(string[]);
int main(int argc, string argv[]) {
//Function to check for user "cooperation"
int key = numCheck(argv);
}
int numCheck(string input[]) {
int i = 0;
char junk;
bool usrCooperation = true;
//check for user "cooperation" check that key isn't a letter or special sign
while (input[i] != NULL) {
if (sscanf(*input, "%*[A-Za-z_]%c", &junk)) {
printf("test fail");
usrCooperation = false;
} else {
printf("test pass");
}
i++;
}
return 0;
}
check if the string from the main function parameter string argv[] is indeed a number
A direct way to test if the string converts to an int is to use strtol(). This nicely handles "123", "-123", "+123", "1234567890123", "x", "123x", "".
int numCheck(const char *s) {
char *endptr;
errno = 0; // Clear error indicator
long num = strtol(s, &endptr, 0);
if (s == endptr) return 0; // no conversion
if (*endptr) return 0; // Junk after the number
if (errno) return 0; // Overflow
if (num > INT_MAX || num < INT_MIN) return 0; // int Overflow
return 1; // Success
}
int main(int argc, string argv[]) {
// Call each arg[] starting with `argv[1]`
for (int a = 1; a < argc; a++) {
int success = numCheck(argv[a]);
printf("test %s\n", success ? "pass" : "fail");
}
}
sscanf(*input, "%*[A-Za-z_]%c", &junk) is the wrong approach for testing numerical conversion.
You pass argv to numcheck and test all strings in it: this is incorrect as argv[0] is the name of the running executable, so you should skip this argument. Note also that you should pass input[i] to sscanf(), not *input.
Furthermore, lets analyze the return value of sscanf(input[i], "%*[A-Za-z_]%c", &junk):
it returns EOF if the input string is empty,
it returns 0 if %*[A-Za-z_] fails,
it also returns 0 if the conversion %c fails after the %*[A-Za-z_] succeeds,
it returns 1 is both conversions succeed.
This test is insufficient to check for non digits in the string, it does not actually give useful information: the return value will be 0 for the string "1" and also for the string "a"...
sscanf() is very tricky, full of quirks and traps. Definitely not the right tool for pattern matching.
If the goal is to check that the strings contain only digits (at least one), use this instead, using the often overlooked standard function strspn():
#include <stdio.h>
#include <string.h>
int numCheck(char *input[]) {
int i;
int usrCooperation = 1;
//check for user "cooperation" check that key isn't a letter or special sign
for (i = 1; input[i] != NULL; i++) {
// count the number of matching character at the beginning of the string
int ndigits = strspn(input[i], "0123456789");
// check for at least 1 digit and no characters after the digits
if (ndigits > 0 && input[i][ndigits] == '\0') {
printf("test passes: %d digits\n", ndigits);
} else {
printf("test fails\n");
usrCooperation = 0;
}
}
return usrCooperation;
}
Let's try this again:
This is still your problem:
if (sscanf(*input, "%*[A-Za-z_]%c", &junk))
but not for the reason I originally said - *input is equal to input[0]. What you want to have there is
if ( sscanf( input[i], "%*[A-Za-z_]%c", &junk ) )
what you're doing is cycling through all your command line arguments in the while loop:
while( input[i] != NULL )
but you're only actually testing input[0].
So, quick primer on sscanf:
The first argument (input) is the string you're scanning. The type of this argument needs to be char * (pointer to char). The string typedef name is an alias for char *. CS50 tries to paper over the grosser parts of C string handling and I/O and the string typedef is part of that, but it's unique to the CS50 course and not a part of the language. Beware.
The second argument is the format string. %[ and %c are format specifiers and tell sscanf what you're looking for in the string. %[ specifies a set of characters called a scanset - %[A-Za-z_] means "match any sequence of upper- and lowercase letters and underscores". The * in %*[A-Za-z_] means don't assign the result of the scan to an argument. %c matches any character.
Remaining arguments are the input items you want to store, and their type must match up with the format specifier. %[ expects its corresponding argument to have type char * and be the address of an array into which the input will be stored. %c expects its corresponding argument (in this case junk) to also have type char *, but it's expecting the address of a single char object.
sscanf returns the number of items successfully read and assigned - in this case, you're expecting the return value to be either 0 or 1 (because only junk gets assigned to).
Putting it all together,
sscanf( input, "%*[A-Za-z_]%c", &junk )
will read and discard characters from input up until it either sees the string terminator or a character that is not part of the scanset. If it sees a character that is not part of the scanset (such as a digit), that character gets written to junk and sscanf returns 1, which in this context is treated as "true". If it doesn't see any characters outside of the scanset, then nothing gets written to junk and sscanf returns 0, which is treated as "false".
EDIT
So, chqrlie pointed out a big error of mine - this test won't work as intended.
If there are no non-letter and non-underscore characters in input[i], then nothing gets assigned to junk and sscanf returns 0 (nothing assigned). If input[i] starts with a letter or underscore but contains a non-letter or non-underscore character later on, that bad character will be converted and assigned to junk and sscanf will return 1.
So far so good, that's what you want to happen. But...
If input[i] starts with a non-letter or non-underscore character, then you have a matching failure and sscanf bails out, returning 0. So it will erroneously match a bad input.
Frankly, this is not a very good way to test for the presence of "bad" characters.
A potentially better way would be to use something like this:
while ( input[i] )
{
bool good = true;
/**
* Cycle through each character in input[i] and
* check to see if it's a letter or an underscore;
* if it isn't, we set good to false and break out of
* the loop.
*/
for ( char *c = input[i]; *c; c++ )
{
if ( !isalpha( *c ) && *c != '_' )
{
good = false;
break;
}
}
if ( !good )
{
puts( "test fails" );
usrCooperation = 0;
}
else
{
puts( "test passes" );
}
}
I followed the solution by the user "chux - Reinstate Monica". thaks everybody for helping me solve this problem. Here is my final program, maybe it can help another learner in the future. I decided to avoid using the non standard library "cs50.h".
//#include <cs50.h>
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
void keyCheck(int);
int numCheck(char*);
int main(int argc, char* argv[])
{
//Error code == 1;
int key = 0;
keyCheck(argc); //check that two parameters where sent to main.
key = numCheck(argv[1]); //Check for user "cooperation".
return 0;
}
//check for that main received two parameters.
void keyCheck(int key)
{
if (key != 2) //check that main argc only has two parameter. if not terminate program.
{
exit(1);
}
}
//check that the key (main parameter (argv [])) is a valid number.
int numCheck(char* input)
{
char* endptr;
errno = 0;
long num = strtol(input, &endptr, 0);
if (input == endptr) //no conversion is possible.
{
printf("Error: No conversion possible");
return 1;
}
else if (errno == ERANGE) //Input out of range
{
printf("Error: Input out of range");
return 1;
}
else if (*endptr) //Junk after numeric text
{
printf("Error: data after main parameter");
return 1;
}
else //conversion succesfull
{
//verify that the long int is in the integer limits.
if (num >= INT_MIN && num <= INT_MAX)
{
return num;
}
//if the main parameter is bigger than an int, terminate program
else
{
printf("Error key out of integer limits");
exit(1);
}
}
/* else
{
printf("Success: %ld", num);
return num;
} */
}
How can I get a safe input of integer (especially, positive number) using scanf or gets? I've tried several solutions and each solution had some problems.
1. Using getchar() to remove string inputs
int safeInput() {
int input;
scanf("%d", &input);
while(getchar() != '\n');
return input;
}
This method effectively handles string inputs, however, if strings such as 3a are inputted, the value of input becomes 3, which is not a true exception handle.
2. Retrieving input as a string then converting to integer value.
int safeInput() {
char[200] input, safe_input;
gets(input);
// I know about the security issue about gets - but it's not the point.
int i = 0;
while (1) {
if (input[i] >= 48 && input[i] <= 57) safe_input[i] = input[i];
else break;
i++;
}
return atoi(safe_input);
}
This method has problem that it cannot handle if string that has longer length than allocated to input was inputted.
3. What if defining a string using pointer?
I concerned about defining input by pointer, like char *input;. However, once I executed gets(input)(or scanf("%s", input)), it raised runtime-error.
So what is a proper way to retrieve an integer value from console window using scanf or gets?
The answer depends on what exactly you mean by safe. If you want to catch any possible input error, your only option is to use a function of the strtol() family, which even allows for a range check. In my beginners' guide away from scanf(), I'm describing its use.
Here's the code adapted to what you're attempting here, with comments:
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <limits.h>
// return success as boolean (0, 1), on success write result through *number:
int safeInput(int *number)
{
long a;
char buf[1024]; // use 1KiB just to be sure
if (!fgets(buf, 1024, stdin))
{
// reading input failed:
return 0;
}
// have some input, convert it to integer:
char *endptr;
errno = 0; // reset error number
a = strtol(buf, &endptr, 10);
if (errno == ERANGE)
{
// out of range for a long
return 0;
}
if (endptr == buf)
{
// no character was read
return 0;
}
if (*endptr && *endptr != '\n')
{
// *endptr is neither end of string nor newline,
// so we didn't convert the *whole* input
return 0;
}
if (a > INT_MAX || a < INT_MIN)
{
// result will not fit in an int
return 0;
}
// write result through the pointer passed
*number = (int) a;
return 1;
}
First if you want a safe input, do not use gets. Saying that you know about the issues is not a true excuse when you could use fgets. Next, the trick is to try to read a non blank character after the int: if you find no one, then there is nothing after the int on the line.
int safeInput(int *input) { // the return value is the indicator of failed read
int c;
char dummy[2]; // never forget the terminating null!
if (scanf("%d%1s", input, dummy) == 1) return 1;
// in case of error, skip anything up to end of line or end of file
while (((c = fgetc(stdin)) != '\n') && (c != EOF));
return 0;
}
The nice point here, is that when scanf returns 1, the %1s has eaten anything up to the end of line, including the terminating 'n'. But this has a major drawback: the scanf will only end on end of stream or after reading one additional (non blank) character. For that reason, Felix Palmen's answer is easier and safer to use.
I have a delete function on array of structures books. I'm passing it an array of records, author of book and name of book and size of the list.
Now here given that list[0].author and list[5].author and author all are equal to "Dan Brown" (same string)
void delete(struct books *list,char author[],char name[],int n)
{
int i,a;
a=strcmp(list[0].author,list[5].author);
printf("%d\n",a); // prints 0
a=strcmp(list[0].author,author);
printf("%d\n",a); // prints other than 0
}
Why is it happening? What's wrong here?
From the documentation of fgets:
Reading stops when a newline character is found, at end-of-file or error. The newline, if any, is retained.
This means that fgets will not remove the final '\n' from the end of the read string. Thus, your strings are:
"Dan Brown"
"Dan Brown"
"Dan Brown\n"
They're not equal.
This is a very common issue when using fgets. That's why I usually prefer scanf, like this:
char buffer[BUF_LEN];
char format[16];
int scanf_result;
sprintf(format, "%%%u[^\n]", BUF_LEN);
//....
do
{
//TODO: Ask for input
scanf_result = scanf(format, buffer);
switch (scanf_result)
{
case -1: //TODO: Print error message and exit
case 0: //TODO: Print error mesage and break
}
//Discard remainings of buffered input line
while (getchar() != '\n') {;}
} while (1); //Ugly, but plain
Otherwise, you can use fgets with something like this:
int buf_len;
//TODO: Ask for input
while (fgets(buffer, BUF_LEN, stdin) == NULL)
{
//TODO: Check and handle error
}
buf_len = strlen(buffer);
//Remove trailing '\n', if present
if (buffer[buf_len - 1] == '\n')
{
buffer[--buf_len] = '\0';
}
Even though it's easier, I don't like this second method, because strlen scans the string another time to determine its length. In most cases, this is not a performance issue, I avoid it because I have my own mental issues.
You should verify your inputs. Sometimes by more than one method is necessary. Here, I am using strlen(), and strstr(), because if the length is ==, and a substring exists, then the strings ARE equal. So, try something like this to verify the input strings are what you thing they are before making a conclusion:
Note: the enum is of course not necessary, but included here to add clarity to example of output.
enum {
SAME, //0
NOT_SAME //1
}
void delete(struct books *list,char author[],char name[],int n)
{
int i,a, len1, len2;
A = NOT_SAME;
len1 = strlen(list[0].author);
len2 = (list[5].author);
if(strstr(list[0].author,list[5].author) && (len1==len2)) a = SAME;
printf("%d\n",a);
a = NOT_SAME;
len1 = strlen(list[0].author);
len2 = (author);
if(strstr(list[0].author,author) && (len1==len2)) a = SAME;
printf("%d\n",a);
}
check second strings by printing character by character.
Especially author string.
for(i=0; i < strlen(list[0].author);i++)
{
if(list[0].author[i]!=author[i])
{
printf("this is position is not matching\n",i+1);
//try to print characters and also print ascii characters.
break;
}
}
//or simply try to use strncpy()