C - How to scan an int only entered after symbol - c

I am having difficulty scanning from user input an integer (and storing it) only if printed directly after a !:
char cmd[MAX_LINE/2 + 1];
if (strcmp(cmd, "history") == 0)
history(hist, current);
else if (strcmp(cmd, "!!") == 0)
execMostRecHist(hist, current-1);
else if (strcmp(cmd, "!%d") == 0)
num = %d;
else
{//do stuff}
I understand this is completely wrong syntax for strcmp(), but just as an example of how I am gathering user input.

strcmp doesn't know about format specifiers, it just compares two strings. sscanf does what you want: It tests whether a string has a certain format and converts parts of the string to other types.
For example:
int n = 0;
if (sscanf(cmd, " !%d", &num) == 1) {
// Do stuff; num has already been assigned
}
The format specifier %d tells sscanf to look for a valid decimal integer. The exclamation mark has no special meaning and matches only if there is an exclamation mark. The space at the front means that the command may have leading white space. Nothe that there may be white space after the exclam and before the number and that the number may well be negative.
The format specifier is special to the scanf family and related to, but different from the ´%dformat ofprintf`. Is usually has no meaning in other strings and certainly not when it is found unquoted in the code.

Don't you like writing a checker by yourself?
#include <ctype.h>
#include <stdio.h>
int check(const char *code) {
if (code == NULL || code[0] != '!') return 0;
while(*(++code) != '\0') {
if (!isdigit(*code)) return 0;
}
return 1;
}
/* ... */
if (check(cmd))
sscanf(cmd + 1, "%d", &num);

Use sscanf() and check its results.
char cmd[MAX_LINE/2 + 1];
num = 0; // Insure `num` has a known value
if (strcmp(cmd, "history") == 0)
history(hist, current);
else if (strcmp(cmd, "!!") == 0)
execMostRecHist(hist, current-1);
else if (sscanf(cmd, "!%d", &num) == 1)
;
else
{//do stuff}

Related

How to check if scanf("%s", &var) is a number, and thus turn it into an integer

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

How do I read a whole line to a string?

int run_add_line_after(Document *doc, char *command) {
int paragraph_num, line_num;
char com[MAX_STR_SIZE + 1], extra[MAX_STR_SIZE + 2], line[MAX_STR_SIZE + 1];
if (sscanf(command, " %s %d %d %s", com, &paragraph_num, &line_num, extra)
== 4 && paragraph_num > 0 && line_num >= 0 && extra[0] == '*') {
strcpy(line, &(extra[1]));
if (add_line_after(doc, paragraph_num, line_num, line) == FAILURE) {
printf("add_line_after failed\n");
}
return SUCCESS;
}
return FAILURE;
}
I want sscanf to read everything left in command to extra but it's only taking the first word. For example, if I have:
command: "add_line_after 1 0 *first line of the document"
I want to see:
com: "add_line_after"
paragraph_num: 1
line_num: 0
extra: "first line of the document"
but instead I get:
com: "add_line_after"
paragraph_num: 1
line_num: 0
extra: "first"
because %s stops when it hits the space. How do I read the rest of the line while still ignoring any whitespace between '0' and '*'?
For reference, MAX_STR_SIZE is 80 and command is a 1025 character array (though I don't think that matters). Just assume extra is large enough to hold the rest of the line.
sscanf is really the wrong tool to use here. It can be done, but probably should not be used the way you are trying. Fortunately, you are not passing com to add_line_after, which means it is not necessary to ensure that com is a properly null-terminated string, and this allows you to avoid all of that unnecessary string copying. (If you were passing com, you would either have to copy it, or write a null terminator into command.) You don't want or need to use sscanf to move data at all. You can just use it to parse the numeric values. It's not clear to me if you want to discard any whitespace that follows the *, and this code does not. If you want to do so, removing that whitespace is trivial and left as an exercise for the reader:
int
run_add_line_after(struct document *doc, const char *command)
{
int paragraph_num, line_num, n;
const char *line;
if( 2 == sscanf(command, "%*s %d %d %n", &paragraph_num, &line_num, &n)
&& paragraph_num > 0 && line_num >= 0 && command[n] == '*' )
{
line = command + n + 1;
if( add_line_after(doc, paragraph_num, line_num, line) == FAILURE) {
fprintf(stderr, "add_line_after failed\n");
} else {
return SUCCESS;
}
}
return FAILURE;
}
The idea here is to simply use sscanf to figure out where the extra data is in the string and to parse the integer values. This is absolutely the wrong tool to use (the scanf family is (almost) always the wrong tool), and is used here only for demonstration.
-- edit --
But, of course, this doesn't do exactly what you want. It would be much cleaner to move some functionality into add_line_after to handle the newline, but since you also need to remove the newline, it becomes necessary to do something like:
int
run_add_line_after(struct document *doc, char *command)
{
int paragraph_num, line_num, n;
char *line;
if( 2 == sscanf(command, "%*s %d %d %n", &paragraph_num, &line_num, &n)
&& paragraph_num > 0 && line_num >= 0 && command[n] == '*' )
{
line = command + n + 1;
line[strcspn(line, "\n")] = '\0';
if( add_line_after(doc, paragraph_num, line_num, line) == FAILURE) {
fprintf(stderr, "add_line_after failed\n");
} else {
return SUCCESS;
}
}
return FAILURE;
}
This is not ideal. It would be better if you modify the API so that you can avoid both copying the data and modifying the string that you are given.
Got it. Format string should be " %s %d %d %[^\n]s". It will keep reading into the last string variable until it hits the enter key.

if statement with string compare in C

I'm supposed to write a short C code where I generate a random number between 1 and 6 if I type "random". If I type in "exit" or "quit", the program must end. "quit" and "exit" work, but nothing happens when I enter "random".
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main() {
printf("enter your command");
char input[100];
fgets(input, 100, stdin);
if (strcmp(input, "quit") == 0){
exit(0);
} else if (strcmp(input, "exit") == 0) {
exit(0);
} else if (strcmp(input, "random") == 0) {
srand(time(NULL));
int random_number = rand() %7;
printf("%d\n",random_number);
}
return 0;
}
You need to remove the new line character '\n' that can be appended to the string read by fgets.
For example
char input[100];
input[0] = '\0';
if ( fgets (input, 100, stdin) )
{
input[strcspn( input, "\n" )] = '\0';
}
Take into account that the initializer in this declaration
int random_number = rand() %7;
generates numbers in the range [0, 6]. If you need the range [1, 6] then the initializer should look like
int random_number = rand() %6 + 1;
And according to the C Standard the function main without parameters shall be declared like
int main( void )
Your fgets call is reading the inserted command plus the newline at the end. So you should compare with the newline as well, or choose a different input reading method (such as using scanf, useful for dealing with any whitespace, or removing the newline yourself).
strcmp(input, "quit\n") == 0
strcmp(input, "exit\n") == 0
strcmp(input, "random\n") == 0
You didn't notice with the first two commands, but they never passed the test as well.
You could also add a final else to grab anything not matched. Only altering that (without dealing with newlines) would prove that the others are not matching as well:
/* ... */
} else {
printf("unknown command\n");
}
An example using scanf:
char input[101];
scanf(" %100s", input); /* discards any leading whitespace and
* places the next non-whitespace sequence
* in `input` */

Trouble reading in Tokens in C

I can't seem to figure out, how to correctly read in a .txt file that has the following appereance: (example)
+ 1
+ 2
- 2
+ 5
p -1
? 5
and so on...
what I need now is to store the operator / token which can be '+' '-' 'p' or something like that, and the int that follows in two different variables because I need to check them later on.
char oprtr[1];
int value;
FILE *fp = fopen(args[1], "r");
while(!feof(fp) && !ferror(fp)){
if(fscanf(fp, "%s %d\n", oprtr, &value) < 1){
printf("fscanf error\n");
}
if(strcmp(oprtr, "+") == 0){
function1(bst, value);
} else if(strcmp(oprtr, "-") == 0){
function2(bst, value);
} else if((strcmp(oprtr, "p") == 0) && value == -1){
function3(root);
//some other functions and so on...
}
printing out oprtr and value in the loop shows that they are not being red in correctly, but it does compile. Does someone have a solution?
You have single characters, you can use == to compare them instead of strcmp. Just read the input in pairs and use a switch for example.
char c;
int x;
while(fscanf(fp, "%c %d", &c, &x) == 2)
{ switch(c)
{ case '+': /* ... */
}
}
Your string oprtr is too small to hold anything but an empty string (remember that C strings need a terminating 0 character!). So:
char oprtr[1];
needs to be at least:
char oprtr[2]; // string of maximum size 1
or more defensively:
char oprtr[256]; // string of maximum size 255
You can use the fscanf function, you can get the input from the file.
int fscanf(FILE *stream, const char *format, ...);
fscanf(fp," %c %d",&c,&d);

Why does this accept invalid input?

I have a triangle program in c
#include <stdio.h>
// A function which decides the type of the triangle and prints it
void checkTriangle(int s1, int s2,int s3)
{
// Check the values whether it is triangle or not.
if ((s1 + s2 > s3 && s1 + s3 > s2 && s2 + s3 > s1) && (s1 > 0 && s2 > 0 && s3 > 0))
{
// Deciding type of triangle according to given input.
if (s1 == s2 && s2 == s3)
printf("EQUILATERAL TRIANGLE");
else if (s1 == s2 || s2 == s3 || s1 == s3)
printf("ISOSCELES TRIANGLE\n");
else
printf("SCALENE TRIANGLE \n");
}
else
printf("\nTriangle could not be formed.");
}
int main(void)
{
// Initializing variables
int a,b,c;
// Getting input from user
printf("Please enter the sides of triangle");
printf("\nPlease enter side 1:");
scanf("%d",&a);
printf("Please enter side 2:");
scanf("%d",&b);
printf("Please enter side 3:");
scanf("%d",&c);
// Calling function in order to print type of the triangle.
checkTriangle(a,b,c);
}
When the input is:
7b
it gives an error, which is what I want, but when I entered the data like this:
7
7
7b
it ignores 'b' and take 7 as an integer — but why? How can I prevent this?
What I want to do is give an error also for
7
7
7b
If you want to be able to detect an error with the user's input, such as a line not being a valid decimal integer, then you could do the following:
Read the input into a buffer using fgets(buffer, size, stdin)
Use strtoul(buffer, &endptr, 10) to parse the buffer as a decimal integer (base 10), where endptr is a char*
endptr will point to the first invalid character in buffer, ie. the character after the last one which was successfully parsed
Now if *endptr == '\0', ie. endptr points to the end of buffer, the whole string was parsed as a valid decimal integer
If you really want each number on a separate line of input, and for the whole of the line to be valid number or space, then you probably need to forget scanf() and family and use fgets() and strtol() instead.
#include <stdlib.h>
#include <errno.h>
#include <stdio.h>
#include <ctype.h>
#include <limits.h>
static int read_side(void)
{
char buffer[4096];
if (fgets(buffer, sizeof(buffer), stdin) == 0) // EOF
return -1;
char *end;
errno = 0;
long result = strtol(buffer, &end, 10);
if (result < 0 || errno != 0) // Neither errors nor negative numbers are allowed
return -1;
if (end == buffer) // Nothing was convertible
return -1;
while (isspace(*end))
end++;
if (*end != '\0') // Non-spaces after the last digit
return -1;
if (result > INT_MAX) // Result too big for `int`
return -1;
return result;
}
(If you needed to accept any valid int value but distinguish errors, then you'd pass in a pointer to the function and return -1 on error or 0 on success, and assign the safe result to the pointer.)
Yes, it really is that fiddly to do the job properly. And yes, analyzing the result of strtol() is as tricky as that; you do have to be very careful. (And there's an outside chance I've forgotten to check for a detectable error condition.) And no, I don't think you can do the equivalent job with scanf() et al; in particular, the behaviour on overflow with scanf() is undefined.
you shouldn't use scanf or do scanf("%[^\n]s", word); Or use someting like get()
also put d or x at the end of my example not s for string :P
Read the input into a string buffer. Parse the string to extract numeric values be of any kind one by one.
%d accepts only integer. try with %x in scanf() for hex-decimal input.
Better you can get input as string then check using isnumeric() else use scanf("%[^\n]s", word) like #mou suggested.

Resources