Using Scanf to read in an equation of random length - c

I am trying to read in an equation, then take each part separately, however then user can enter as big or small equation as they like (for example. 3+7, 2+-9+8 or even 2). I have this however it doesn't seem to be working.
printf("Please enter an equation\n");
scanf("%f", &num);
//printf("%f", num);
while ( num != '\n'){
scanf("%f", &num);
scanf("%c", &op);
//printf("%c %f \n", op, num);
}
when i output what i have got it is not the same as the input.

You may wish to read How to read a line from the console in C? for the full details, but basically you just do this:
char * getline(void) {
char * line = malloc(100), * linep = line;
size_t lenmax = 100, len = lenmax;
int c;
if(line == NULL)
return NULL;
for(;;) {
c = fgetc(stdin);
if(c == EOF)
break;
if(--len == 0) {
len = lenmax;
char * linen = realloc(linep, lenmax *= 2);
if(linen == NULL) {
free(linep);
return NULL;
}
line = linen + (line - linep);
linep = linen;
}
if((*line++ = c) == '\n')
break;
}
*line = '\0';
return linep;
}

You are trying to take the complete expression in a float variable (num, in your code). If you do a scanf("%f", &num); in while loop then you are just overwriting the values in num. You need to take the expression in a char array or char*. Then you need to have an algrithm to seperate the operators and numbers, convert the numbers to desired type and solve the euation.

If you want to read an expression and have your program understand that, you need severely heavier machinery. Either this is an XY problem, i.e., you need to rethink the problem and find another approach; or you should look into the whole parsing/compiling area.
Scripting languages (like Python or Perl) have some sort of eval builtin, so you can take a snippet of code as text and get it evaluated (run). Perhaps using one of those is a better match to your problem? But take care, blindly running anything the user inputs is a huge risk...

To read an arbitrary-length line with scanf, you can use
scanf("%[^\n]", equation);
This regular expression means "read everything until you find the '\n' character".
Keep in mind that this is not secure though, since the user can easily overflow the "equation" buffer. If you want to avoid that, I would suggest reading char by char in a loop, like so:
for(i=0; i < MAX_EQ_SIZE; i++)
{
char tmp;
scanf("%c", &tmp);
if(tmp == '\n')
break;
equation[i] = tmp;
}
Since you are asking just how to read, I'm not going into parsing the read equation.
Here is a code example:
#include <stdio.h>
#define MAX_EQ_SIZE 1024
void parse(char * eq)
{
// Do the processing
}
int main()
{
char equation[MAX_EQ_SIZE];
scanf("%[^\n]", equation); // Read a whole line
scanf("%*c"); // Read and ignore the \n
puts(equation);
parse(equation);
}

Use fgets() to read the line of input.
Use the return value from sscanf() to determine if number or operator.
int Eval(void) {
char buffer[100];
char *p = buffer;
printf("Please enter an equation\n");
if (fgets(buffer, sizeof buffer, stdin) == NULL)
return -1; // no more input
float num;
char op;
int n;
while (*p) {
if (sscanf(p, "%f %n", &num, &n) == 1) {
printf("num %f\n", num);
p += n;
}
if (sscanf(p, " %c %n", &op, &n) == 1) {
printf("op %c\n", op);
p += n;
} else if (*p) {
printf("Error '%s'\n", p);
return 0;
}
}
return 1;
}

Related

How to make "overloaded" scanf in C?

Friends how can I make Scanf to take 1 or 2 or 3 numbers depending on input data I give?
sample data 1: "1 2 5"
sample data 2: "1 4"
sample data 3: "4"
if(scanf("%lf",&a)==1 )
{
printf("1 input num\n");
}
else if(scanf(" %lf %lf",&a, &b)==2 )
{
printf("2 input num\n");
}
else if(scanf("%lf %lf %lf",&a, &b, &c)==3 )
{
printf("3 input num\n");
}else
{
printf("Error message.\n");
return 1;
}
You might consider this an answer:
int InputNums=0;
InputNums = scanf("%lf %lf %lf",&a, &b, &c);
if(InputNums!=0)
printf("%d input num\n");
else
printf("Error message.\n");
It works by NOT eating one number and then trying whether instead more numbers could have been read, like your shown code does.
Instead try to read three numbers and then let scanf() tell you how many worked.
But actually I am with the commenters. If you do not have guaranteed syntax in your input (which scanf() is for) then use something else.
This is nicely describing which alternative in which situation AND how to get scanf to work in the same situation:
http://sekrit.de/webdocs/c/beginners-guide-away-from-scanf.html
Scanf already does this for you, and indeed you used the same scanf function with a variable number of arguments. You can look here: How do vararg work in C?
However you don't need to overload scanf, but rather pass to it a string telling what you need to scan. You can do this dynamically by changing the string at runtime.
The code to try is the following:
#include <stdio.h>
int main()
{
char one[] = "%d";
char two[] = "%d%d";
int o1;
int t1,t2;
scanf(one,&o1);
scanf(two,&t1,&t2);
printf("%d %d %d",o1,t1,t2);
return 0;
}
If you must use scanf() ....
"%lf" is a problem as it consumes leading white-space including '\n', so we lost where a line of input might have ended.
Instead first look for leading white-space and see if an '\n' occurs.
#define N 3
double a[N];
count = 0;
while (count < N) {
// Consume leading white-spaces except \n
unsigned char ch = 0;
while (scanf("%c", &ch) == 1 && isspace(ch) && ch != '\n') {
;
}
if (ch == '\n') {
break;
}
// put it back into stdin
ungetc(ch, stdin);
if (scanf("%lf", &a[count]) != 1)) {
break; // EOF or non-numeric text
}
count++;
}
printf("%d values read\n", count);
for (int i=0; i<count; i++) {
printf("%g\n", a[i]);
}
Alterantive to consume various multiple leading whitespaces that only uses scanf() with no ungetc():
// Consume the usual white spaces except \n
scanf("%*[ \t\r\f\v]");
char eol[2];
if (scanf("%1[\n]", eol) == 1) {
break;
}
If the line contains more than N numbers or non-numeric text, some more code needed to report and handle that.
The best solution to problems with scanf and fscanf is usually to use something other than scanf or fscanf. These are remarkably powerful functions, really, but also very difficult to use successfully to handle non-uniform data, including not only variable data but data that may be erronious. They also have numerous quirks and gotchas that, though well documented, regularly trip people up.
Although sscanf() shares many of the characteristics of the other two, it turns out often to be easier to work with in practice. In particular, combining fgets() to read one line at a time with sscanf() to scan the contents of the resulting line is often a convenient workaround for line-based inputs.
For example, if the question is about reading one, two, or three inputs appearing on the same line, then one might approach it this way:
char line[1024]; // 1024 may be overkill, but see below
if (fgets(line, sizeof line, stdin) != NULL) { // else I/O error or end-of-file
double a, b, c;
int n = sscanf(line, "%lf%lf%lf", &a, &b, &c);
if (n < 0) {
puts("Empty or invalid line");
} else {
printf("%d input num\n", n);
}
}
Beware, however, that the above may behave surprisingly if any input line is longer than 1023 characters. It is possible to deal with that, but more complicated code is required.
here is an example of using fgets, strtok and atof to achieve same:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main() {
char input[256];
double inputsf[256];
while(1) {
printf(">>> "); fflush(stdout);
char *s = fgets(input, 255, stdin);
if (!s)
break;
int count = 0;
char *t;
while (t = strtok(s, " \n")) {
s = NULL;
inputsf[count++] = atof(t);
}
printf("Found %d inputs\n", count);
for (int i = 0; i < count; i++)
printf(" %lf\n", inputsf[i]);
if (count == 0)
break;
}
return 0;
}
Bases on the #chux-ReinstateMonica comment, here is a piece of code which uses strtod. It skips leading spaces, but has an issue with the tailing spaces at the end of the string. So, some extra checking is needed there, which could be used for error checking as well. The following loop can replace the strtok loop from above.
while(*s) {
char *e;
double val = strtod(s, &e);
if (e == s)
break; // not possible to parse, break the loop
inputsf[count++] = val;
s = e;
}
You can solve your problem using those line of code
#include <stdio.h>
int main(){
int a[100];
int n;
printf("How many data you want to input: ");
scanf("%d", &n);
printf("Sample %d data Input: ", n);
for (int i=0; i <n; i++) {
scanf("%d", &a[i]);
}
printf("Sample data %d: ", n);
for (int i=0; i <n; i++) {
printf("%d ", a[i]);
}
if(n == 1){
printf("\n 1 input num\n");
}else if(n==2){
printf("2 input num\n");
}else if(n==3){
printf("3 input num\n");
}else{
printf("Error");
}
return 0;
}
if you want to take multiple input in single line use this line
int arr[100];
scanf ("%lf %lf %lf", &arr[0], &arr[1], &arr[2]);

How to scanf multiple inputs separeted by space in C?

I am new in proggraming and i can't solve a problem. So i have to scanf and check if it is an integer (int n), and than read n floats (with checking if they are floats). Problem is that machine tests add multiple floats separeted by space in input and i don't know how to get these numbers.
I wrote something like this:
int n;
if(!scanf("%d", &n)){
printf("Invalid input");
return 1;
}
float *tab = malloc(n*sizeof(float));
printf("Enter variables: ");
for(int i=0; i<n; i++){
if(scanf("%f", (tab+i))!=1){
printf("Incorrect input data");
return 2;
}
}
I don't know if it is good and what to do if you input less or more numbers in input.
Guys, please explain me what is wrong here and how to solve it.
Thanks for your time.
How to scanf multiple inputs separated by space in C?
The "%d" and "%f" will happily handle numeric text separated by spaces, tabs, end-of-lines, etc., yet not distinguish between spaces and end-of-line. With insufficient input in one line, code will read the input of the next line. With excess input, the entire line is not read - rest of line reamins for next input function.
If OP is concerned about lines of inputs, best to read a line of input and then parse.
I don't know if it is good and what to do if you input less or more numbers in input.
Put yourself in charge: if you directed a team of coders, what would you want? Consume and ignore non-numeric input, consume input and report an error, simple end the code, etc.
Aside from the first scan, code looks reasonable as is.
For me, for robust code, I would drop all scanf() and use fgets() in a helper function to parse. Then sscanf() or strto*() to parse and complain if not as expected.
Sample
Of course this helper function is overkill for such a simple task, yet it is a helper function - one that I can use over and over for anytime I want to read a a group of float from one line. I can improve as desired (e.g. more error handle, handle overly long lines, ...)
// Read 1 line of input.
// return EOF on end-of-file or stream error,
// else return number of float read, even if more than N.
int get_floats(const char *prompt, float *dest, int N) {
if (prompt) {
fputs(prompt, stdout);
fflush(stdout);
}
char buf[BUFSIZ];
if (fgets(buf, sizeof buf, stdin) == NULL) {
return EOF;
}
char *endptr = buf;
int floats_read = 0;
// parse the line into floats
while (*endptr) {
const char *s = endptr;
float f = strtof(s, &endptr);
if (s == endptr) {
break; // no conversion
}
if (floats_read < N) {
dest[floats_read] = f;
}
floats_read++;
}
// Consume trailing white-space
while ((unsigned char) *endptr) {
endptr++;
}
if (*endptr) {
return -1; // Non-numeric junk at the end
}
return floats_read;
}
Usage:
int n;
if(get_floats("Enter variables: ", tab, n) != n) {
printf("Invalid input");
return 1;
}
The answer is really simple: put a space in front of your scanf format specifier. That tells scanf to eat all the whitespace before converting.
Like this:
#include <stdio.h>
#include <stdlib.h>
int main() {
int n;
if (1 != scanf(" %d", &n)) {
exit(1);
}
float *tab = calloc(n, sizeof *tab);
if (!tab) {
exit(3);
}
for (int i = 0; i < n; ++i) {
if (1 != scanf(" %f", &tab[i])) {
exit(2);
}
}
const char *sep = "";
for (int i = 0; i < n; i++) {
printf("%s%f", sep, tab[i]);
sep = ", ";
}
printf("\n");
free(tab);
return 0;
}

Scanf for reading string and int array in brackets

I've been trying to come up with solution for reading input which contains string and then brackets with array of numbers (I don't know how many numbers will be inputed.
Input could look like:
sacrifice (1, 2, 4, 2)
I am wondering if it is possible to achieve with scanf. I've been looking for different functions such as getline, sscanf, fgets and so on. But I couldn't come up with solution.
My code looks like this:
scanf("%[^(]", command);
while ( ( c = getchar() ) != ')' )
{
scanf("%d", weights[pos]);
pos++;
}
Which should read string until the bracket is found and then I tried to load the numbers in array as long as it doesn't reach the ')'. Yet it doesn't seem to work.
Is scanf viable to achieve this? Could anyone point me in better direction if not please?
I think it would be simpler to read the complete line from stdin and then parse it by hand using strtok or strcspn. Something like below could be done.
Disclaimer: This is just some sample code and doesn't handle all possible inputs and will crash with invalid input, it is just to give you an idea about how to do it. If you want to go this way, you would have to handle various error conditions, such as:
checking return value of malloc/getline/realloc
instead of atoi using a better function like strtol (which allows error checking),
handling white spaces in the input and
handling input which does not contain any parenthesis
Those are some of the many things which you would have to think about.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int *parse_numbers(char *numstr, size_t *size)
{
char *p;
char *s = numstr;
char *last;
size_t array_size = 10;
int *numbers = malloc(sizeof(int) * array_size);
size_t offset = 0;
for (p = strtok_r(s, ",", &last); p; p = strtok_r(NULL, ",", &last)) {
if (offset == array_size) {
array_size *= 2;
numbers = realloc(numbers, sizeof(int) * array_size);
//TODO do error check
}
numbers[offset++] = atoi(p); //strtol would be a better choice
}
*size = offset;
return numbers;
}
int main()
{
char *s = NULL;
char *p;
char *last;
int i = 0;
int *numbers;
size_t size;
size_t linesize = 0;
getline(&s, &linesize, stdin);
for (p = strtok_r(s, "(", &last); p; p = strtok_r(NULL, "(", &last)) {
if (i++ == 0) {
//This is the part of the string before '('
cmd = p;
} else {
// This is the part of the string after '('
numbers = parse_numbers(p, &size);
}
}
for (i = 0; i < size; i++) {
printf("%d\n", numbers[i]);
}
free(numbers);
free(s);
return 0;
}
Separate input from parsing. Far easier to handle the various issues of command processing. Concerning "don't know how many numbers will be inputed", IMO, a reasonable upper bound should be established. Else code is susceptible to overwhelming memory resources due to user input - a hacker exploit.
char command[1000];
while (fgets(command, sizeof command, stdin)) {
Now process the command using sscanf(), strtok() or your own code. The best method depends on maybe things not posted by OP, especially error handling.
int cmd_start;
int cmd_end;
int n = 0;
// sacrifice (1, 2, 4, 2, ...)
// +----------------- skip optional white space
// |+---------------- record scan position
// || +-------------- scan, but not save, lower case letters
// || | +------- record scan position
// || | | +----- skip optional white space
// || | | |+---- scan (
// || | | ||+--- skip optional white space
// || | | |||+-- record scan position
sscanf(command, " %n%*[a-z]%n ( %n", &cmd_start, &cmd_end, &n);
if (n == 0) {
printf("Invalid command '%s'\n", command);
continue;
}
int x[sizeof command / 2];
int x_count = 0;
char *p = &command[n]; // pick up where previous scan ended.
char sep[2] = {0};
while (sscanf(p, "%d %1[,)] %n", &x[x_count], sep, &n) == 2) {
x_count++;
p += n;
if (sep[0] == ')') break;
}
if (*p || sep[0] != ')') {
printf("Invalid separator '%s'\n", command);
continue;
}
// Use command
command[cmd_end] = '\0';
Process_Command(&command[cmd_start], x, x_count);
}
scanf("%d", weights[pos]); --> scanf("%d", &weights[pos]); – BLUEPIXY
That's indeed adequate to make the code work, provided a sufficiently dimensioned weights array.

Using fscanf to receive an array

I'm trying to take the input of floating numbers from a file and arrange it into an array.
The only trouble is that I don't know exactly how many floating numbers there will be each time though I do know that the max amount of floating numbers is 1000.
What I need to do is have fscanf take all the floating numbers but then stop at the next line, which is full of integers.
Here is what I have so far for this section:
for (repetition = 0; repetition <= 1000; repetition++)
{
fscanf(userFile, "%f", &itemPrice[itemNumber]);
itemNumber++;
}
But unfortunately, this continues on to assign array values to all of the other values in the next several lines.
I found another user input, auctionItems and used that to control the array length using while(itemNumber < auctionItems)
The return value of fscanf is the number of items successfully read. Use that to decide when to stop.
#include <stdio.h>
int main(int argc,char * argv[])
{
int i;
float ff[1000];
char next_text[17];
for (i=0; i < 1000; i++) {
int n_read;
n_read = fscanf(stdin, " %f", &( ff[i] ));
if (n_read < 1) {
fscanf(stdin, "%16s", next_text);
next_text[16] = (char) 0;
printf("Next text: '%s'\n", next_text);
break;
}
}
printf("Read %d items, %f .. %f\n",i,ff[0],ff[i-1]);
return 0;
}
Maybe you could try this method using fgetc and fputc:
for (repetition = 0; itemPrice <= 1000; repetition++)
{
int c = fgetc(userFile);
if (c == '\n' || c == EOF) break;
fputc(c);
fscanf(userFile, "%f", &itemPrice[itemNumber]);
itemNumber++;
}
char input[MAX];
while(fgets(input, MAX, userFile) != NULL){
sscanf(input, "%lf", &itemPrice[itemNumber++]);
}
OP is fairly close. Just stop when scanning fails.
fscanf() returns the number of fields scanned or EOF. In this case, checking for 1 is sufficient.
// for (repetition = 0; itemPrice <= 1000; repetition++) Use <, not <= #Arpit
for (i = 0; i < 1000; )
{
int retval = fscanf(userFile, "%f", &itemPrice[i]);
if (retval != 1) break;
i++;
}
A robust solution would detect unexpected data. This solution simple goes until the end-of-file or non-float text is encountered.
[Edit]
It appears OP has only 1 line of floats. In that case code could read the whole line and then parse the buffer.
#define CHAR_PER_FLOAT_MAX 20
char buf[1000*(CHAR_PER_FLOAT_MAX + 1)]; // Some large buffer
if (fgets(buf, sizeof buf, input)== NULL) return; // EOF or IO error
char *p = buf;
for (i = 0; i < 1000; ) {
int n = 0;
if (sscanf(p, "%f %n", &itemPrice[i], &n) != 1) break;
p += n;
i++;
}
if (*p) Handle_ExtraTextOnLine();
foo(itemPrice, i); // Use data
Another approach is to read direct from the file 1 float at a time and then look at the following white-space for an end-of-line. Less elegant.
for (i = 0; i < 1000; ) {
if (fscanf(input, "%f", &itemPrice[i]) != 1) {
// need to add code to consume the rest of the line if processing is to continue.
break;
}
i++;
// look for standard white-space
char buf[2];
while (fscanf(input, "%1[ \f\n\r\t\v]", buf) == 1) {
if (buf[0] == '\n') break;
}
}
foo(itemPrice, i); // Use data

A basic/ manual way to check that a value entered by the user is numeric

I've searched in and out of these forums but am still having trouble. My understanding of C is still very basic. I'm creating a small program that takes 3 numerical values entered by the user and then calculates the highest. I nailed that.
I now want to ensure that the user enters only integer values. I managed to get the prog to keep prompting the user to re-enter the value until it is within the specified numerical range (for example, any number between 1 and 1000 inclusive, piece of cake) but that's not good enough. I used 3 while loops to test each value, but this only works as long as the input is of type integer.
The trick is I cant use built in functions. It needs to be manual (sorry, poor choice of words) I tried to use char variables and x = getchar(); to get the ASCII value and test it in a condition but I can't get it working in a loop. (while/ do-while)
I also tried using a "for loop" and array variables but once again am struggling to get it to keep prompting the user.
I've also tried to test the value returned by scanf to see if its integer but my knowledge level of correct C syntax is level: noob. Either my loops don't loop or they loop infinitely.
Here is some sample code:
int x, y, z =0;
printf("Enter the first number:\n");
scanf("d", &x);
while (condition) /* Here is where I need to determine that the entered val is false */
{
printf("Wrong input. Re-enter a valid value.\n");
x =0;
scanf("%d", &x); /*user re-prompted */
}
I'm getting the idea that I'll have to use ASCII and a loop, but I just can't get to it. Also, the values entered get sent to a function for comparing and are then returned.
Could someone give me some advice and a few tips please?
Much thanks
You would have to use something like fgets, and strtol:
long someValue;
char *bufEnd = NULL;
char buf[128]; // max line size
do {
printf("enter a value: ");
fgets(buf, 128, stdin);
someValue = strtol(buf, &bufEnd, 10); // base 10
} while (bufEnd == buf || *bufEnd != '\n');
printf("got value: %li", someValue);
What we are doing here is we are tapping into strtol's capability to tell us where it stopped parsing, by passing in bufEnd.
Then, we are making sure that bufEnd doesn't point to the beginning of buf (in which case, it didn't start with a number), and also checking to make sure that bufEnd points to \n, or the end of the line (making sure that the user didn't enter something like 123abc, which strtol would interpret as 123). You may wish to trim buf of whitespace characters first, however.
You're absolutely on the right track with "scanf()". Just check the return value. If you don't get the expected #/values, then you got invalid input:
char found = FALSE;
int ival;
double x;
while (!found)
{
printf("Please enter a valid integer: ");
if (scanf("%d", &ival) !=1) {
printf ("Invalid! Please re-enter!\n");
continue;
}
printf("Please enter a valid floating point number: ");
if (scanf("%lf", &x) !=1) {
printf ("Invalid! Please re-enter!\n");
continue;
}
found = TRUE;
}
Here's my solution. It safe against buffer overflow and straightforward .
#include <stdio.h>
#define LEN 10
int main() {
int a;
char str[LEN];
fgets( str, LEN, stdin );
while ( !sscanf( str, "%d", &a ) )
fgets( str, 10, stdin );
printf("Num is : %d\n", a);
return 0;
}
#include <stdio.h>
#include <limits.h>
#include <ctype.h>
int getInteger(int* err){
int ch;
int n;//int32
int takeNum, sign;
long long int wk;//long long int as int64
wk=0LL;
*err = 0;
takeNum = 0;//flag
sign = 1;//minus:-1, other:1
/* //skip space character
while(EOF!=(ch=getchar()) && (ch == ' ' || ch == '\t' || ch == '\n'));
ungetc(ch, stdin);
*/
while(EOF!=(ch=getchar())){
if(ch == '-'){
if(takeNum != 0){//in input number
*err = 1;
break;
}
if(sign == -1){//already sign
*err = 2;
break;
}
sign = -1;
continue;
}
if(ch >= '0' && ch <= '9'){//isdigit(ch) in ctype.h
if(takeNum == 0)
takeNum = 1;
wk = wk * 10 + (ch - '0')*sign;
if(INT_MAX < wk || INT_MIN > wk){//overflow
*err = 3;
break;
}
continue;
}
if(ch != '\n'){//input other [-0-9]
*err = 4;
}
break;
}
if(takeNum == 0){//not input number
*err = 5;
} else {
n=wk;
}
while(ch != '\n' && EOF!=(ch=getchar()));//skip to newline
return n;
}
int getValue(const char* redoprompt, int low, int high){
int num, err=0;
while(1){
num = getInteger(&err);
if(err || low > num || high < num)
printf("%s", redoprompt);
else
break;
}
return num;
}
#define max(x,y) ((x)>(y))? (x) : (y)
int main(){
const char *error_message = "Wrong input. Re-enter a valid value.\n";
int x, y, z, max;
x = getValue(error_message, 1, 1000);
y = getValue(error_message, 1, 1000);
z = getValue(error_message, 1, 1000);
max = max(max(x,y), z);
printf("max:%d\n", max);
return 0;
}

Resources