I need to make a small calculator for school, using the following template: "operation(operator1, operator2)". E.G.: "Add(1, 2)" will return 3, "Multiply(2, 5)" will return 10. I know I can use a strtok to retrieve the operation, but I'm not sure how to retrieve the operators. Currently I have the following:
#include <stdio.h>
int main()
{
char Math_Input[32]; //This is where I will store the input in the following template: operation("operator1, operator2")
float Operator_1, Operator_2;
printf("What calculation do you wish to do?: ");
fgets(Math_Input,20,stdin); //Here I retrieve the entire line and I'll store it in Math_Operation, next step is to retrieve the operation, operator1 and operator2
char * Math_Operation = strtok(Math_Input, "(");
printf("%s\n", Math_Operation);
}
Update:
After a bit of code given which is using sscanf() I revised my code to the following:
#include <stdio.h>
int main()
{
char Math_Input[32]; //This is where I will store the input in the following template: operation("operator1, operator2")
char Math_operation[16];
float Operator_1, Operator_2;
printf("What calculation do you wish to do?: ");
fgets(Math_Input,20,stdin); //Here I retrieve the entire line and I'll store it in Math_Input, next step is to retrieve the operation, operator1 and operator2
if (3 != sscanf(Math_Input, "%15s (%f ,%f )", Math_operation, &Operator_1, &Operator_2)) {
fprintf(stderr, "Incorrect line >%s<\n", Math_Input);
printf("Operation: %s \n", Math_operation);
printf("Operator1: %d \n", Operator_1);
printf("Operator2: %d \n", Operator_2);
}
else {
// Ok process the operation...
printf("Else");
printf("Operation: %s \n", Math_operation);
printf("Operator1: %d \n", Operator_1);
printf("Operator2: %d \n", Operator_2);
}
}
I used a few printf's to debug. This should work in theory right? But when I test it out like this: (so this is my console) it doesnt seem to work...
What calculation do you wish to do?: Add(1, 2)
Incorrect line >Add(1, 2)
<
Operation: Add(1,
Operator1: 0
Operator2: 0
Process returned 0 (0x0) execution time : 3.698 s
Press any key to continue.
If the input is expect to be in the format operation(value_1, value2), it looks like a correct use case for sscanf. The scanf family is said to be a poor man's parser, because it has no support for error recovery. But here you have already got your input line from fgets and only need a trivial parsing. So I would just use:
char Math_Input[32]; //This is where I will store the input in the following template: operation("operator1, operator2")
char Math_operation[16];
float Operator_1, Operator_2;
printf("What calculation do you wish to do?: ");
fgets(Math_Input,20,stdin); //Here I retrieve the entire line and I'll store it in Math_Operation, next step is to retrieve the operation, operator1 and operator2
if (3 != sscanf(Math_Input, "%15[^ (] (%f ,%f )", Math_operation, &Operator_1, &Operator_2)) {
fprintf(stderr, "Incorrect line >%s<\n", Math_input);
}
else {
// Ok process the operation...
}
...
The good news with sscanf is that it will ignore any non significative blank character.
Additional improvements: make sure that fgets could get a complete line (not more than 19 characters) else read until the end of line in you later need a loop
In my opinion, the best (and most efficient) way would be to write a simple parser, something like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int
main(void)
{
char input[32], function_name[32];
float function_args[2];
char *iterator, *bufptr, buffer[32];
/* we will use buffer to temporarily store float,
and bufptr to store tokens beginning */
printf("What calculation do you wish to do?: ");
fgets(input, 20, stdin);
/* On this step we have input, probably in format `name(val1, val2)`.
Now we are going to parse this: */
iterator = input; /* iterator will point to first character of an input */
bufptr = iterator; /* bufptr will now point to a beginning of a token */
while (*iterator != '(' && *iterator != '\0')
++iterator;
/* Now iterator points to '(', or string ended because we got
NULL-terminator, so we will check for a terminator: */
if (*iterator == '\0') { /* String ended */
fputs("Syntax error :(", stderr);
exit(1);
}
/* Copying function name to function_name variable.
We will copy amount of characters equal iterator - input,
because we added 1 to iterator, so iterator points to '(' now: */
strncpy(function_name, bufptr, iterator - bufptr);
/* Now, we are doing exacly the same thing, but now for ','.
You can surround this loops with a function: */
++iterator; /* iterator points to '(', so we will just add 1 */
bufptr = iterator;
while (*iterator != ',' && *iterator != '\0') ++iterator;
if (*iterator == '\0') { /* String ended */
fputs("Syntax error :(", stderr);
exit(1);
}
strncpy(buffer, bufptr, iterator - bufptr);
function_args[0] = strtof(buffer, NULL);
/* Now we can continue parsing, iterator now should point to a comma.
Doing same thing as upper, but now for ')' */
++iterator;
bufptr = iterator;
while (*iterator != ')' && *iterator != '\0') ++iterator;
if (*iterator == '\0') { /* String ended */
fputs("Syntax error :(", stderr);
exit(1);
}
strncpy(buffer, bufptr, iterator - bufptr);
function_args[1] = strtof(buffer, NULL);
while (*iterator != ')' && *iterator != '\0') ++iterator;
if (*iterator == '\0') { /* String ended */
fputs("Syntax error :(", stderr);
exit(1);
}
/* Printing */
printf("Function that you called: %s(%f, %f)\n",
function_name, function_args[0], function_args[1]);
/* And on this point you have all expected data, so you can call functions */
if (!strcmp(function_name, "Add")) {
Add(function_args[0], function_args[1]);
} else if (!strcmp(function_name, "Substract")) {
Substract(function_args[0], function_args[1]);
} else {
/* ... */
}
}
As you said in the comments:
the user will be typing in "Add(1,2)" or "Multiply(2,5)"
you can just do what you are doing i.e. tokenizing the string:
#include <stdio.h>
#include <string.h>
int main()
{
int data_field = 3;
char Math_Input[32];
char* Math_Operation = NULL;
// operation("operand1,operand2")
float operand_1, operand_2;
printf("What calculation do you wish to do?: ");
scanf("%31[^\n]%*c", Math_Input);
Math_Operation = strtok(Math_Input, "(");
while(data_field-- != 0)
{
printf("%s\n", Math_Operation);
if (data_field == 2)
{
Math_Operation = strtok(NULL, ",");
sscanf(Math_Operation, "%f", &operand_1);
}
if (data_field == 1)
{
Math_Operation = strtok(NULL, ")");
sscanf(Math_Operation, "%f", &operand_2);
}
}
return 0;
}
Output:
What calculation do you wish to do?: Add(2,1)
Add
2
1
P.S.: You can store operands using sscanf().
Related
I want read data from console and output to Text file with reserve of N character per variable of structure type.
The Text file is similar to:
1 111 1 Peter
22 22 2 John Lays
3 3 3 Anne Belgs
I do not know if I'm using the most correct functions.
Also I can not read ("carro.name") more than 1 word (example: John Lays)
struct estruturaCarro {
int id, potencia, avariado;
char name[11];
} carro;
...
//Read data to Text File:
...
printf("\n ID......:"); scanf("%d", &carro.id);
printf("\n Potencia:"); scanf("%d", &carro.potencia);
printf("\n Avariado:"); scanf("%d", &carro.avariado);
printf("\n NAME:"); scanf("%10[0-9a-zA-Z ]", carro.name); // or scanf("%[^\n]s",...)
fprintf(fp, "%-2d %-3d %-1d %-10s \n\n", carro.id, carro.potencia, carro.avariado, carro.name);
...
//Show File Text data:
...
int registos=0;
while(1)
{
fscanf(fp, "%d %d %d %-10s", &carro.id, &carro.potencia, &carro.avariado, carro.name);
if(feof(fp)){ break; }
printf("%-2d %-3d %-1d %-10s\n", carro.id, carro.potencia, carro.avariado, carro.name);
registos++;
}
printf("\nCarros=%d", registos);
As you say in your question you cannot use scanf to read a complex name including spaces.
But before to search how to do it is needed to decide what to do.
Probably you do not want to memorize the extra spaces at the beginning and at the end (including the newline), and probably a name must not be empty.
But what about inside a complex name ? If the user enter John Lays do you save the name with the two spaces or you want to simplify to have only one ? Do you have to manage other special character like '-' (are John - Lays / John- Lays / John -Lays read as John-Lays ?).
What to do if the input string is longer than 10 characters ? Just to stop to read letting the rest for the next read or to bypass up to a newline ? Because you print a message before each input you clearly want an input per line and the rest of the line must be bypassed.
If you do not want to read the string as it is enter the best way is probably to write your own read string function.
You also have to decide what to do if the user do not enter a number for ID or Potencia or Avariado, currently you do not even detect the error, this is not a good way. So in that case do you abort all (exit program), or you redo the read ? Probably you prefer to read again, for that you need to bypass the invalid input, but what that means, to bypass all up to a newline ?
For instance :
#include <stdio.h>
#include <string.h>
#include <ctype.h>
/* read an int memorizing its value in v,
return 0 in case of EOF else a non null value */
int readInt(const char * msg, int * v)
{
for (;;) {
fputs(msg, stdout);
if (scanf("%d", v) == 1)
return 1;
/* not a number or EOF, bypass all up to \n */
int c;
while ((c = fgetc(stdin)) != '\n')
if (c == EOF)
return 0;
puts("invalid value"); /* message may be also get in argument */
}
}
/* read a string up to a \n
remove extra spaces at the beginning and end
simplify internal multiple spaces
accept any character and do not manage in a special way characters like like '-'
a non empty string must be read
read at most sz-1 characters in s then place the null character (as fgets), sz must be > 1
if the line too long bypass the rest of the input up to \n
return 0 in case of EOF else a non null value */
int readStr(const char * msg, char * s, size_t sz)
{
fputs(msg, stdout);
/* read the first char bypassing spaces including \n */
if (scanf(" %c", s) == 0)
// EOF
return 0;
size_t index = 1;
int c;
sz -= 1;
while (index != sz) {
c = fgetc(stdin);
if ((c == EOF) || (c == '\n'))
break;
if (!isspace(c))
s[index++] = c;
else if (s[index - 1] != ' ')
s[index++] = ' ';
}
s[(s[index - 1] != ' ') ? index : index-1] = 0;
// bypass possible rest of the line
while ((c != EOF) && (c != '\n'))
c = fgetc(stdin);
return 1;
}
/* ******************* */
struct estruturaCarro {
int id, potencia, avariado;
char name[11];
} carro;
int main()
{
do {
if (!readInt("\n ID......:", &carro.id) ||
!readInt("\n Potencia:", &carro.potencia) ||
!readInt("\n Avariado:", &carro.avariado) ||
!readStr("\n NAME:", carro.name, sizeof(carro.name))) {
puts("EOF");
return -1;
}
else
printf("%-2d %-3d %-1d '%-10s' \n\n", carro.id, carro.potencia, carro.avariado, carro.name);
} while (strcmp(carro.name, "end"));
return 0;
}
Compilation and execution:
pi#raspberrypi:/tmp $ gcc -pedantic -Wextra -Wall r.c
pi#raspberrypi:/tmp $ ./a.out
ID......:aze
invalid value
ID......:qsd
invalid value
ID......:1
Potencia:2
Avariado:3
NAME:aze u iiiiiiiiiiiiiiiiii
1 2 3 'aze u iiii'
ID......:11
Potencia:22
Avariado:0
NAME: end
11 22 0 'end '
pi#raspberrypi:/tmp $
When you read in your file and supposing it was produced doing fprintf(fp, "%-2d %-3d %-1d %-10s", ...) :
char line[21]; /* each line has 20 characters newline included */
while (fgets(line, sizeof(line), fp) != NULL) {
if (sscanf(line, "%d %d %d", &carro.id, &carro.potencia, &carro.avariado) != 3)
/* abnormal case, invalid file */
break; /* anything else you want to do */
/* the string starts at the index 9 and has 10 characters out of the newline */
memcpy(carro.name, line + 9, 10);
carro.name[10] = 0;
/* ... */
}
note the name have spaces at the end if its length is less than 10 characters
Or you can read in a way similar to the previous on stdin.
Could anyone help me figure out how to extract 10/3 from a string, perform the math of 10/3 as a float/double, and store the result? I've tried modifying an older code that I have but I couldn't get it to work. This is that code:
#include<stdio.h>
#include<string.h>
#define SLASH "\\."
int main(void){
char path[100];
printf("Enter file path: \n");
fgets(path, sizeof(path), stdin);
path[strlen(path) - 1] = '\0';
char *token = strtok(path, SLASH);
while(token != NULL){
printf("%s\n", token);
/*Rather than printf the result
I've tried saving the result to
a variable but I could only get
10 to save*/
token = strtok(NULL, SLASH);
}
return 0;
}
I will then be putting the solution into this portion of a larger program:
} else if(j == 6){
arr3[j][(strlen(arr3[j])) - 1] = NULL;
sprintf(temp_arr, "%.18lf", atof(arr3[j]));
strcat(arr3[j], temp_arr);
strcat(arr3[j], new_line);
} else if(j == 7){
I read the following in from a .csv file:
input from file:
The output that I need to obtain is:
Correct output:
Could anyone give me some ideas on how to do this? I've tried to do a for loop instead of a while loop in hopes to save 10 to an array[0] and 3 to an array[1 ], but I would only get array[0] = 10 and array[1 ] = 0. Other times I would just get random junk instead of zero. Using this token approach seemed so straight-forward before but I can't convert for this task. It doesn't seem like this should be so hard for me but I'm stuck. Thanks in advance for any help that anyone can provide.
Edit:
I tweaked the first code to save each separately:
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define SLASH "\\."
int main(void){
char path[100];
printf("Enter file path: \n");
fgets(path, sizeof(path), stdin);
path[strlen(path) - 1] = '\0';
char *token = strtok(path, SLASH);
while(token != NULL){
int i = 0;
char temp[100];
strcpy(temp, token);
printf("%s\n", token);
printf("temp = %s\n", temp);
token = strtok(NULL, SLASH);
i++;
}
return 0;
}
From there, I'm trying to implement that into the program that I'm actually working on.
char temp_arr[17];
} else if(j == 6){
arr3[j][(strlen(arr3[j])) - 1] = '\0';
strcpy(temp_arr, arr3[j]);
char *token = strtok(arr3[j], SLASH);
int m = 0;
while(token != NULL){
strcpy(temp_arr2, token);
printf("temp = %s\n", temp_arr2);
token = strtok(NULL, SLASH);
m++;
}
sprintf(temp_arr, "%.18lf", atof(arr3[j]));
strcat(arr3[j], temp_arr);
strcat(arr3[j], new_line);
But I'm getting 10/3 instead of 10 and 3 separately.
There are a number of ways to approach this problem. One of the simplest is to parse the string using the capabilities of strtol which will parse a string for a numeric value, convert the value to a long, and set its endptr parameter to point to one character after the last digit used in the conversion.
(which will be '/' in your case, but you should still check for any intervening spaces between the end of the number and your operator)
You can then start scanning forward from endptr until you locate '/' (or any non-numeric character to use for the operator). Save the character, and advance your pointer by one and call strtol again to convert your final number to long.
Putting the pieces together, you can do something like the following:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define BASE 10
#define EXPR 64
int main (void) {
long a, b;
char op,
expr[EXPR] = "",
*p = expr,
*ep;
printf ("enter expression: ");
if (!fgets (expr, EXPR, stdin)) {
fprintf (stderr, "error: user canceled input.\n");
return 1;
}
a = strtol (p, &ep, BASE); /* convert a to long */
if (p == ep) { /* no digits converted */
fprintf (stderr, "error: strtol - a, no digits found.\n");
return 1;
}
p = ep; /* set p to end-pointer */
while (*p && isspace (*p)) p++; /* skip any intervening spaces */
op = *p++; /* set operator char */
b = strtol (p, &ep, BASE); /* convert b to long */
if (p == ep) { /* no digits converted */
fprintf (stderr, "error: strtol - b, no digits found.\n");
return 1;
}
printf ("%ld %c %ld = ", a, op, b); /* output expression */
switch (op) { /* handle +, -, *, / */
case '+': printf ("%ld\n", a + b); break;
case '-': printf ("%ld\n", a - b); break;
case '*': printf ("%ld\n", a * b); break;
case '/': printf ("%f\n", (double)(a) / b); break;
default : printf ("(error - invalid operator)\n"); break;
}
return 0;
}
(note: you should also check errno following each call to strtol to insure there were no errors, overflow/underflow, etc.. in the conversion. That is left to you)
Example Use/Output
$ ./bin/aexprb
enter expression: 10/3
10 / 3 = 3.333333
$ ./bin/aexprb
enter expression: 10 / 3
10 / 3 = 3.333333
$ ./bin/aexprb
enter expression: 10 + 3
10 + 3 = 13
Look things over and let me know if you have further questions.
I made this code:
/*here is the main function*/
int x , y=0, returned_value;
int *p = &x;
while (y<5){
printf("Please Insert X value\n");
returned_value = scanf ("%d" , p);
validate_input(returned_value, p);
y++;
}
the function:
void validate_input(int returned_value, int *p){
getchar();
while (returned_value!=1){
printf("invalid input, Insert Integers Only\n");
getchar();
returned_value = scanf("%d", p);
}
}
Although it is generally working very well but when I insert for example "1f1" , it accepts the "1" and does not report any error and when insert "f1f1f" it reads it twice and ruins the second read/scan and so on (i.e. first read print out "invalid input, Insert Integers Only" and instead for waiting again to re-read first read from the user, it continues to the second read and prints out again "invalid input, Insert Integers Only" again...
It needs a final touch and I read many answers but could not find it.
If you don't want to accept 1f1 as valid input then scanf is the wrong function to use as scanf returns as soon as it finds a match.
Instead read the whole line and then check if it only contains digits. After that you can call scanf
Something like:
#include <stdio.h>
int validateLine(char* line)
{
int ret=0;
// Allow negative numbers
if (*line && *line == '-') line++;
// Check that remaining chars are digits
while (*line && *line != '\n')
{
if (!isdigit(*line)) return 0; // Illegal char found
ret = 1; // Remember that at least one legal digit was found
++line;
}
return ret;
}
int main(void) {
char line[256];
int i;
int x , y=0;
while (y<5)
{
printf("Please Insert X value\n");
if (fgets(line, sizeof(line), stdin)) // Read the whole line
{
if (validateLine(line)) // Check that the line is a valid number
{
// Now it should be safe to call scanf - it shouldn't fail
// but check the return value in any case
if (1 != sscanf(line, "%d", &x))
{
printf("should never happen");
exit(1);
}
// Legal number found - break out of the "while (y<5)" loop
break;
}
else
{
printf("Illegal input %s", line);
}
}
y++;
}
if (y<5)
printf("x=%d\n", x);
else
printf("no more retries\n");
return 0;
}
Input
1f1
f1f1
-3
Output
Please Insert X value
Illegal input 1f1
Please Insert X value
Illegal input f1f1
Please Insert X value
Illegal input
Please Insert X value
x=-3
Another approach - avoid scanf
You could let your function calculate the number and thereby bypass scanf completely. It could look like:
#include <stdio.h>
int line2Int(char* line, int* x)
{
int negative = 0;
int ret=0;
int temp = 0;
if (*line && *line == '-')
{
line++;
negative = 1;
}
else if (*line && *line == '+') // If a + is to be accepted
line++; // If a + is to be accepted
while (*line && *line != '\n')
{
if (!isdigit(*line)) return 0; // Illegal char found
ret = 1;
// Update the number
temp = 10 * temp;
temp = temp + (*line - '0');
++line;
}
if (ret)
{
if (negative) temp = -temp;
*x = temp;
}
return ret;
}
int main(void) {
char line[256];
int i;
int x , y=0;
while (y<5)
{
printf("Please Insert X value\n");
if (fgets(line, sizeof(line), stdin))
{
if (line2Int(line, &x)) break; // Legal number - break out
printf("Illegal input %s", line);
}
y++;
}
if (y<5)
printf("x=%d\n", x);
else
printf("no more retries\n");
return 0;
}
Generally speaking, it is my opinion that you are better to read everything from the input (within the range of your buffer size, of course), and then validate the input is indeed the correct format.
In your case, you are seeing errors using a string like f1f1f because you are not reading in the entire STDIN buffer. As such, when you go to call scanf(...) again, there is still data inside of STDIN, so that is read in first instead of prompting the user to enter some more input. To read all of STDIN, you should do something the following (part of code borrowed from Paxdiablo's answer here: https://stackoverflow.com/a/4023921/2694511):
#include <stdio.h>
#include <string.h>
#include <stdlib.h> // Used for strtol
#define OK 0
#define NO_INPUT 1
#define TOO_LONG 2
#define NaN 3 // Not a Number (NaN)
int strIsInt(const char *ptrStr){
// Check if the string starts with a positive or negative sign
if(*ptrStr == '+' || *ptrStr == '-'){
// First character is a sign. Advance pointer position
ptrStr++;
}
// Now make sure the string (or the character after a positive/negative sign) is not null
if(*ptrStr == NULL){
return NaN;
}
while(*ptrStr != NULL){
// Check if the current character is a digit
// isdigit() returns zero for non-digit characters
if(isdigit( *ptrStr ) == 0){
// Not a digit
return NaN;
} // else, we'll increment the pointer and check the next character
ptrStr++;
}
// If we have made it this far, then we know that every character inside of the string is indeed a digit
// As such, we can go ahead and return a success response here
// (A success response, in this case, is any value other than NaN)
return 0;
}
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.
// (Per Chux suggestions in the comments, the "buff[0]" condition
// has been added here.)
if (buff[0] && 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;
}
void validate_input(int responseCode, char *prompt, char *buffer, size_t bufferSize){
while( responseCode != OK ||
strIsInt( buffer ) == NaN )
{
printf("Invalid input.\nPlease enter integers only!\n");
fflush(stdout); /* It might be unnecessary to flush here because we'll flush STDOUT in the
getLine function anyway, but it is good practice to flush STDOUT when printing
important information. */
responseCode = getLine(prompt, buffer, bufferSize); // Read entire STDIN
}
// Finally, we know that the input is an integer
}
int main(int argc, char **argv){
char *prompt = "Please Insert X value\n";
int iResponseCode;
char cInputBuffer[100];
int x, y=0;
int *p = &x;
while(y < 5){
iResponseCode = getLine(prompt, cInputBuffer, sizeof(cInputBuffer)); // Read entire STDIN buffer
validate_input(iResponseCode, prompt, cInputBuffer, sizeof(cInputBuffer));
// Once validate_input finishes running, we should have a proper integer in our input buffer!
// Now we'll just convert it from a string to an integer, and store it in the P variable, as you
// were doing in your question.
sscanf(cInputBuffer, "%d", p);
y++;
}
}
Just as a disclaimer/note: I have not written in C for a very long time now, so I do apologize in advance if there are any error in this example. I also did not have an opportunity to compile and test this code before posting because I am in a rush right now.
If you're reading an input stream that you know is a text stream, but that you are not sure only consists of integers, then read strings.
Also, once you've read a string and want to see if it is an integer, use the standard library conversion routine strtol(). By doing this, you both get a confirmation that it was an integer and you get it converted for you into a long.
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
bool convert_to_long(long *number, const char *string)
{
char *endptr;
*number = strtol(string, &endptr, 10);
/* endptr will point to the first position in the string that could
* not be converted. If this position holds the string terminator
* '\0' the conversion went well. An empty input string will also
* result in *endptr == '\0', so we have to check this too, and fail
* if this happens.
*/
if (string[0] != '\0' && *endptr == '\0')
return false; /* conversion succesful */
return true; /* problem in conversion */
}
int main(void)
{
char buffer[256];
const int max_tries = 5;
int tries = 0;
long number;
while (tries++ < max_tries) {
puts("Enter input:");
scanf("%s", buffer);
if (!convert_to_long(&number, buffer))
break; /* returns false on success */
printf("Invalid input. '%s' is not integer, %d tries left\n", buffer,
max_tries - tries);
}
if (tries > max_tries)
puts("No valid input found");
else
printf("Valid input: %ld\n", number);
return EXIT_SUCCESS;
}
ADDED NOTE: If you change the base (the last parameter to strtol()) from 10 to zero, you'll get the additional feature that your code converts hexadecimal numbers and octal numbers (strings starting with 0x and 00 respectively) into integers.
I took #4386427 idea and just added codes to cover what it missed (leading spaces and + sign), I tested it many times and it is working perfectly in all possible cases.
#include<stdio.h>
#include <ctype.h>
#include <stdlib.h>
int validate_line (char *line);
int main(){
char line[256];
int y=0;
long x;
while (y<5){
printf("Please Insert X Value\n");
if (fgets(line, sizeof(line), stdin)){//return 0 if not execute
if (validate_line(line)>0){ // check if the string contains only numbers
x =strtol(line, NULL, 10); // change the authentic string to long and assign it
printf("This is x %d" , x);
break;
}
else if (validate_line(line)==-1){printf("You Have Not Inserted Any Number!.... ");}
else {printf("Invalid Input, Insert Integers Only.... ");}
}
y++;
if (y==5){printf("NO MORE RETRIES\n\n");}
else{printf("%d Retries Left\n\n", (5-y));}
}
return 0;}
int validate_line (char *line){
int returned_value =-1;
/*first remove spaces from the entire string*/
char *p_new = line;
char *p_old = line;
while (*p_old != '\0'){// loop as long as has not reached the end of string
*p_new = *p_old; // assign the current value the *line is pointing at to p
if (*p_new != ' '){p_new++;} // check if it is not a space , if so , increment p
p_old++;// increment p_old in every loop
}
*p_new = '\0'; // add terminator
if (*line== '+' || *line== '-'){line++;} // check if the first char is (-) or (+) sign to point to next place
while (*line != '\n'){
if (!(isdigit(*line))) {return 0;} // Illegal char found , will return 0 and stop because isdigit() returns 0 if the it finds non-digit
else if (isdigit(*line)){line++; returned_value=2;}//check next place and increment returned_value for the final result and judgment next.
}
return returned_value; // it will return -1 if there is no input at all because while loop has not executed, will return >0 if successful, 0 if invalid input
}
I want to write a code to ensure that the users input only 1 digit. If a user enters something like "0 1 3" I want my program to read an error message which I have no idea how to do. Anyone has an idea how to approach this? My current code just takes in the first number if a user enters bunch of numbers with a space in between.
Please see my code below. Thanks :D
//Prompt the user to enter the low radius with data validation
printf("Enter the low radius [0.0..40.0]: ");
do
{
ret = scanf("%lf", &lowRadius);
//type validation
if (ret != 1)
{
int ch = 0;
while (((ch = getchar()) != EOF) && (ch != '\n'));
printf("Wrong input. Please enter one numerical value: ");
}
//range validation
else if((lowRadius < 0 || lowRadius > 40))
{
printf("Incorrect value. Please enter in range 0-40: ");
}
else break;
} while ((ret != 1) || (lowRadius < 0 || lowRadius > 40));//end while lowRadius
If you read the line into a string, then analyse it, you avoid the problem of hanging on unsupplied input. You have done most of the work already, but this shows how to trap too much input. It works by scanning a string after the double to pick up any more input. The return value from sscanf tells you if there was, because it returns the number of items successfully scanned.
#include <stdio.h>
#include <stdlib.h>
void err(char *message)
{
puts(message);
exit(1);
}
int main(void)
{
double lowRadius = 0.0;
char inp[100];
char more[2];
int conv;
if(fgets(inp, sizeof inp, stdin) == NULL) {
err("Input unsuccesful");
}
conv = sscanf(inp, "%lf %1s", &lowRadius, more); // conv is number of items scanned
if(conv != 1) {
err("One input value is required");
}
if(lowRadius < 0.0 || lowRadius > 40.0) {
err("Number out of range");
}
printf("%f\n", lowRadius);
return 0;
}
I'm unsure about your stipulation of a single digit, since that won't allow your maximum value to be entered.
Read a whole line and convert it with strtod.
Alexander has the right approach, but doesn't give much detail. Here is how I would do it, using getline() to read the input, and then strspn() plus strtod() to parse the input that was read. If you are not familiar with working with pointers, this will be difficult to understand - but if you are learning C, you'll get there eventually:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
double lowRadius;
char *lineptr = NULL;
size_t n;
char *startptr;
char *endptr;
char *ws = " \t\n"; /* possible whitespace characters */
printf("Enter the low radius [0.0..40.0]: ");
while(1) {
/* free lineptr if set - neeeded if we iterate on error input */
if( lineptr ) {
free(lineptr);
lineptr = NULL;
}
/* now read a line of input */
while( getline(&lineptr, &n, stdin) == -1 ) {
/* error returned, just retry */
continue;
}
/* skip over any leading whitespace */
startptr = lineptr + strspn(lineptr,ws);
/* Now try to convert double */
lowRadius = strtod(startptr, &endptr);
if( endptr==startptr || endptr[strspn(endptr,ws)] != 0 ) {
/* either no characters were processed - e.g., the
line was empty, or there was some non-whitespace
character found after the number. */
printf( "Wrong input. Please enter one numerical value: ");
} else if( (lowRadius < 0.0) || (lowRadius > 40.0) ) {
printf( "Incorrect value. Please enter in range 0-40: " );
} else {
if( lineptr ) free(lineptr);
break;
}
}
printf( "value entered was %lf\n", lowRadius );
}
I have a function to read a text file with the following format
string int int
string int int
string int int
I want to write a function that will assign the values from the text file into variables, but there will also be some cases where the format of the text file will be
string int
string int
string int
In that case, I'd like to set the value of the last int variable to 1. My code I have so far works with the first example but I'm a bit stuck on getting the second scenario to work:
void readFile(LinkedList *inList, char* file)
{
char tempName[30];
int tempLoc, tempNum;
FILE* f;
f = fopen(file, "r");
if(f==NULL)
{
printf("Error: could not open file");
}
else
{
while (fscanf(f, "%s %d %d\n", tempName, &tempLoc, &tempNum) != EOF)
{
insertFirst (inList, tempName, tempLoc, tempNum);
}
}
}
In the second case, fscanf will return 2 instead of 3. So you can rewrite the code like this:
while (1) {
int ret = fscanf(f, "%s %d %d\n", tempName, &tempLoc, &tempNum);
if (ret == EOF) {
break;
}
if (ret == 2) {
tempNum = 1;
} else if (ret != 3) {
// line appear invalid, deal with the error
}
insertFirst (inList, tempName, tempLoc, tempNum);
}
A more hacky way would be to set tempNum to 1 before calling fscanf and just check for EOF as you did above. But I think the code above is clearer.
Edit: to avoid overflows, this would be better. The code would perform better but this is harder to write. Just like above, I did not write any code for the error conditions but you definitely want to handle them
char lineBuf[255];
while (fgets(lineBuf, sizeof(lineBuf), f) != NULL) {
int spaceIdx, ret;
const int len = strlen(lineBuf);
if (len == (sizeof(lineBuf) - 1) {
// line is too long - either your buf is too small and you should tell the user
// that its input is bad
// I recommend to treat this as an error
}
lineBuf[len - 1] = '\0'; // remove \n
--len; // update len, we've removed one character
if (isspace(*lineBuf)) {
// error, line should not start with a space
}
spaceIdx = strcspn(lineBuf, "\t ");
if (spaceIdx == len) {
// error, no space in this line
}
// Ok, we've found the space. Deal with the rest.
// Note that for this purpose, sscanf is a bit heavy handed (but makes the code
// simpler). You could do it with strtol.
// Also, the first space in the format string is important, so sscanf skips
// all the space at the beginning of the string. If your format requires only
// one space between fields, you can do sscanf(lineBuf + spaceIdx + 1, "%d %d"...
ret = sscanf(lineBuf + spaceIdx, " %d %d", &tempLoc, &tempNum);
if (0 == ret) {
// error, no ints
}
else if (1 == ret) {
tempNum = 1;
}
// at that point, you could copy the first part of lineBuf to tempName, but then
// you have to deal with a potential overflow (and spend time on an useless copy),
// so use lineBuf instead
lineBuf[spaceIdx] = '\0';
insertFirst (inList, lineBuf, tempLoc, tempNum);
}