I want to write a program, at first I input a number N, then I want to get the name (can consist of multiple words) and price of N items one by one. Example:
3
item a // the name can consist of multiple words
25.00
item b
12.50
item c
8.12
Next I want to process this data, however i got stuck on the scanning part. my code looks like this:
#include <stdio.h>
int main(){
int n;
char name[50];
int price;
scanf("%d\n", &n);
for(int i = 0; i < n; i++){
scanf("%s\n%d",name,&price);
printf("%s , %d", name, price);
}
printf("end");
}
This works for a single word item, but if the item has a space in it will not continue scanning. I tried using the gets() function, however I still don't have the right result. the code:
for(int i = 0; i < n; i++){
gets(name);
scanf("%d\n",&price);
printf("%s , %d\n", name, price);
}
printf("end");
returns:
3 // Input 3 items
item a // name of first item
1 // price of item 1
item b // name of item 2
item a , 1 // the print of the first item
2 // price of item 2
item c // name of item 3
item b , 2 // print of item 2
3 // price of item 3
word // no clue where this new input came from
end // end of scanning
My question is, how would I go about correctly scanning an input such as this? I also tried changing the scan function into while((c = getchar()) != '\n');, but got the same result...
Mixing gets(), scanf() is bad as scanf() tends to leave the trailing '\n' in stdin.
Using gets() is bad.
scanf("%s", ...) is not useful for reading a line of info with spaces meant to be saved.
how would I go about correctly scanning an input such as this?
A simple alternative is to read each line into a string and then parse the string.
#include <stdio.h>
int main() {
char line[100];
int n;
fgets(line, sizeof line, stdin);
sscanf(line, "%d", &n);
for(int i = 0; i < n; i++){
char name[50];
// int price;
double price;
fgets(line, sizeof line, stdin);
sscanf(line, " %49[^\n]", name);
fgets(line, sizeof line, stdin);
// sscanf(line, "%d", &price);
sscanf(line, "%lf", &price);
printf("%s , %.2f\n", name, price);
}
printf("end");
}
Advanced: Better code would check the return values of fgets(), sscanf(). Maybe replace sscanf(line, "%lf",... with strtod().
I think you could probably figure out what's happening here pretty easily if you added some more output. Let's try shall we?
Also, first of all you're really looking for a double input - not an integer. With an integer, your scan function won't match properly based on what you're looking for. But let's add some more output!
#include <stdio.h>
int main()
{
int n;
char name[50];
double price;
printf("Enter count: ");
scanf("%d", &n);
for (int i = 0; i < n; i++) {
printf("%s\n", "Please type a name followed by a newline followed by a number and then press enter:");
scanf("%s\n%lf", name, &price);
printf("%s , %lf", name, price);
}
return 0;
}
So I think what was happening in your initial attempt was a combination of things. Mostly that you were possibly pressing enter after the output from the first iteration? That will break the scanf call - since it isn't expecting to start with a \n but it is immediately expecting you to enter a name.
Second, I don't know the number you entered in the first iteration, because you didn't supply the output of it while still using scanf - so I have nothing other than speculation. You possibly used an integer on the first go, and subsequently on the remaining iterations you chose to use decimals? Again, this is only a guess.
Sometimes it is easier to just write your own functions with error checking. Below is a suggestion. You may also want to check that the numbers are non-negative.
#include <assert.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
void ReadLine(char result[], int resultLen)
{
int ch, i;
assert(resultLen > 0);
i = 0;
ch = getchar();
while ((ch != '\n') && (ch != EOF)) {
if (i < resultLen - 1) {
result[i] = ch;
i++;
}
ch = getchar();
}
result[i] = '\0';
}
void ReadInteger(int *i)
{
int ch, count;
count = scanf("%d", i);
if (count == 1) {
do {
ch = getchar();
} while (isspace(ch) && (ch != '\n'));
} else {
fprintf(stderr, "invalid input, integer expected\n");
exit(EXIT_FAILURE);
}
}
void ReadReal(double *x)
{
int ch, count;
count = scanf("%lf", x);
if (count == 1) {
do {
ch = getchar();
} while (isspace(ch) && (ch != '\n'));
} else {
fprintf(stderr, "invalid input, real number expected\n");
exit(EXIT_FAILURE);
}
}
int main(void)
{
char name[50];
int n;
double price;
ReadInteger(&n);
for (int i = 0; i < n; i++) {
ReadLine(name, sizeof name);
ReadReal(&price);
printf("%s, %.2f\n", name, price);
}
return 0;
}
As chux said in the comments of this answer: "All scan specifiers, except %c, %[, %n consumes leading white-spaces." So you don't need to account for them.
scanf("%s%d",name,&price);
And looking at your input, you should use float or double for the price.
scanf("%s%lf",name,&price);
Note that this works only if items are made of one word. If they can be of two or more words, you'd better use fgets
EDIT: for items made of more words you should use fgets
fgets(name, 50, stdin);
scanf("%lf",&price);
Replace your for loop with this:
for (int i = 0; i < n; i++) {
scanf(" %[^\n]\n%d", name, &price);
printf("%s , %d\n", name, price);
}
The first space skips leading spaces, and the [^\n] allows you to get more than one word as string input.
Related
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]);
I am trying to read the number of characters including, the spaces.
I use the scanf function to check for chars using %c. Also on a side note, how would I go about storing the input into an array?
#include <stdio.h>
int main(void) {
char n, count= 0;
while (scanf("%c", &n) != EOF) {
count = count+1;
}
printf("%d characters in your input \n", count);
return 0;
}
When I test input (with spaces) such as
abcdefg
it doesn't print anything.
Defining a MAX_CHAR and checking that in loop would protect you against invalid memory write.
Remember that last byte of an array should be left for '\0', if you want to print or use the char array.
#include <stdio.h>
#define MAX_CHAR 100
int main(void) {
char n[MAX_CHAR]={0}, count= 0;
while((count!=MAX_CHAR-1)&&(scanf("%c",&n[count])==1))
{
if((n[count]=='\n')){
n[count]=0;
break;
}
count++;
}
printf("%d characters in your input [%s]\n", count, n);
return 0;
}
scanf does return EOF when it reaches the end of the file. But in order for you to see that happening, you should give your program a file input when you call it like this:
./a.out < input.txt
Inside input.txt you could put any text you want. But if you want to work in the command line, you should read until you find a \n
#include <stdio.h>
int main(void) {
char n, count = 0;
scanf("%c", &n);
while (n != '\n') {
count = count+1;
scanf("%c", &n);
}
printf("%d characters in your input \n", count);
return 0;
}
If you want to store the input in an array, you must know the size of the input (or at least the maximum size possible)
#include <stdio.h>
int main(void) {
char n, count = 0;
char input[100]; //the max input size, in this case, is 100
scanf("%c", &n);
while (n != '\n') {
scanf("%c", &n);
input[count] = n; //using count as the index before incrementing
count = count+1;
}
printf("%d characters in your input \n", count);
return 0;
}
Furthermore, if don't know the size or max size of the input, you'd have to dynamically change the size of the input array. But I think that would be a little advanced for you right now.
Your printf doesn't print anything because runtime doesn't reach to it. Your code looping for ever in while loop
while (scanf("%c", &n) != EOF) {
count = count+1;
}
because scanf won't return EOF in this case
I am unable to put the value in the first element of the array. It's always asking to put the value in array second element.
#include<stdio.h>
#include<string.h>
int main(void)
{
int a, i;
char names[50][50];
printf("\nEnter the number of names you want :");
scanf("%d", &a);
for(i = 0; i < a; i++)
{
printf("\n%d name :", i);
gets(names[i]);
}
printf("\nThe required name lists :");
for(int i = 0; i < a; i++)
{
printf("\n%d name :", i+1);
puts(names[i]);
}
return 0;
}
As scanf leaves behind a dangling newline character \n it causes the gets(Use fgets) to not wait for the input from the user. Try flushing the input buffer by using getchar.
Update: Added mechanism to remove the trailing \n registered by the fgets
#include<stdio.h>
#include<string.h>
int main()
{
int a,i;
printf("Enter the number of names you want: ");
scanf("%d",&a);
//Flush the input buffer
int ch;
while ((ch = getchar()) != '\n' && ch != EOF);
char names[50][50];
for(i=0;i<a;i++)
{
printf("%d name: ",i);
fgets(names[i],50,stdin); //Use fgets instead of gets
// To remove th \n registed by the fgets
char *p;
if ((p = strchr(names[i], '\n')) != NULL)
*p = '\0';
}
printf("The required name lists: \n");
for(int i=0;i<a;i++)
{
printf("%d name: ",i+1);
puts(names[i]);
}
return 0;
}
Reference:
Remove newline skipped by scanf
Remove newline registered by fgets
Put this after line scanf("%d",&a), as a workaround,
char c;
scanf("%c",&c);
Also use fgets(names[i],50,stdin) instead of gets(names[i])
Note: You get warning when you use gets in your code, as it is always assumes a consistent input from user. More explanation over here
Why is the gets function so dangerous that it should not be used?
This is my code. I didnt want to use scanf() to read the name and I tried using fgets() but after I put the first name and age, the second and third time my program runs the for loop it doesnt take age .
#include <stdio.h>
#include <string.h>
struct student
{
char name[15];
int age;
};
int main()
{
int i;
struct student s[A];
for (i = 0 ; i < A ; i++)
{
printf("enter the names and age\n");
fgets(s[i].name, 10, stdin);
scanf("%d", &s[i].age);
}
printf("\n\n");
for (i = 0 ; i < A ; i++)
{
printf("%s\t%d\n", s[i].name, s[i].age);
}
return 0;
}
It doesnt work, why?
But when I replace fgets with
scanf("%s%d",s[i].name,&s[i].age);
It works fine
The difference between fgets() and scanf()
fgets(...) typically reads until receiving a '\n'
scanf("%d", ...) typically:
1. Reads and discards leading white-space.
2. Reads numeric input (sign,digits) until scanning a non-digit.
3. Non-digit is put back into stdin for the next input function.
Example:
JohnEnter
"John\n" is read by fgets() into s[0].name.
21Enter
21 is read by scanf("%d",...) into s[0].age. '\n' put back into stdin
"\n" is read by fgets() into s[1].name.
M
"M" is read by scanf("%d",...), nothing is put in s[1].age. 'M' put back into stdin.
aryEnter
"Mary\n" is read by fgets() into s[2].name.
19Enter
19 is read by scanf("%d",...) into s[2].age. '\n' put back into stdin
"\n" is read by fgets() into s[3].name.
Alternative: To read 2 lines, call fgets() twice, then parse:
int Scan_student(struct student *dest) {
char buffer[2][80];
dest->name[0] = '\0';
dest->age[0] = -1;
printf("enter the names and age\n");
for (int i=0; i<2; i++) {
if (fgets(buffer[i], sizeof buffer[i], stdin) == NULL) {
return EOF; // stdin was closed, no more input (or input error)
}
buffer[i][strcspn(buffer[i], "\r\n")] = '\0'; // lop off potential trailing \n
}
// parse the 2 buffers: MANY options here - something simple for now.
if (sscanf(buffer[0], " %14[-'A-Za-z ]", dest->name) != 1) {
return 0;
}
if (sscanf(buffer[1], "%d", &dest->age) != 1) {
return 0;
}
return 1;
}
int i;
struct student st[3];
for (i = 0 ; i < sizeof(st) / sizeof(st[0]) ; i++) {
if (Scan_student(&st[i]) != 1) break;
}
If you input both the name and the age in a single line, then that's the normal behavior because fgets() will read the whole line until 9 bytes (in your case) are read or a '\n' is found.
You need one of the two, for instance you could only use fgets() like this
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct student
{
char name[100];
int age;
};
int main()
{
int i;
struct student s[1];
for (i = 0 ; i < sizeof(s) / sizeof(*s) ; i++)
{
char number[100];
char *unconverted;
printf("Name > ");
fgets(s[i].name, sizeof(s[i].name), stdin);
if ((unconverted = strchr(s[i].name, '\n')) != NULL)
*unconverted = '\0'; // Remove the trailing '\n'
printf("Age > ");
fgets(number, sizeof(number), stdin);
s[i].age = strtol(number, &unconverted, 10);
if ((*unconverted != '\0') && (*unconverted != '\n'))
s[i].age = -1; // Invalid value indicating input error
}
for (i = 0 ; i < sizeof(s) / sizeof(*s) ; i++)
printf("Name: %s\nAge : %d\n", s[i].name, s[i].age);
return 0;
}
Or scanf() only
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
struct student
{
char name[100];
int age;
};
int main()
{
int i;
struct student s[1];
for (i = 0 ; i < sizeof(s) / sizeof(*s) ; i++)
{
printf("Name Age > ");
if (scanf("%99s%d", s[i].name, &s[i].age) != 2)
{
fprintf(stderr, "input error\n");
s[i].name[0] = '\0';
s[i].age = -1;
}
}
for (i = 0 ; i < sizeof(s) / sizeof(*s) ; i++)
printf("Name: %s\nAge : %d\n", s[i].name, s[i].age);
return 0;
}
and you can then input both name and age in a single line.
The fgets() method is better because you don't need to deal with the '\n' that scanf() doesn't pick up from stdin. A combination would work if you are careful to force the user to input the values in separate lines.
To be honest fgets() isn't required here as you've specified to read from stdin you should simply use gets() this reads from stdin by default. If your moving between a scanf() statement and a gets() statement you should use fflush(stdin) to clear out the input stream, example below:
scanf("%99s", s[i].name);
fflush(stdin);
gets(s[i].age);
In general your better sticking with either scanf() or gets() and not combining them.
I want to use infinite type specifiers (%d) in scanf() function.
For example-
printf("Enter numbers: \t");
scanf("--%d SPECIFIERS--");
So its not definite how many nos. the user will enter. I don't want my program to ask the user the 'numbers of characters'.. but I want to allow any the numbers of characters. But its not possible to enter infinite %d in scanf().
So can anyone please tell what is the C program of finding average of numbers given by the user (if you dont know how much nos. the user will give and you don't want the program to ask 'how many numbers.')?
This is tricky. 2 approaches
1 - fgets() Read 1 line, then parse
char buffer[1000];
int count = 0;
double sum = 0;
int num;
fgets(buffer, sizeof buffer, stdin);
const char *p = buffer;
int n;
while (sscanf(p, "%d%n", &num, &n) == 1) {
p += n;
; // do something with `num`
sum += num;
count++;
}
printf("Average %f\n", sum/count);
2 - Lets say you infinite input ends with the end-of-line. Now the problem is that %d will consume all leading whitespace, including \n. Thus we need to consume and test all whitespace beforehand
int count = 0;
double sum = 0;
int num;
for (;;) {
int ws = 0;
while (isspace(ws = fgetc(stdin)) && (ws != '\n'));
if (ws == '\n') break;
ungetc(ws, stdin);
if (scanf("%d", &num) != 1) break;
; // do something with num
sum += num;
count++;
}
printf("Average %f\n", sum/count);
If you really interested in infinite number of inputs the just try this
while(1)
{
printf("Enter numbers: \t");
scanf("%d", number);
}
It will take input until you forcibly close your program!
But does it make any sense of doing this ?
You should have some way of knowing where the input ends. There are many ways for it and each has a possibly different solution. The two most common ones would be:
Input finishes at end-of-line
The solution is to read one line and then parse the line to get your numbers until the line ends.
This has the benefit that the program could ask for other input afterwards for other parts of the program. The disadvantage is that the user has to input all the numbers in the same line.
Input finishes at end-of-file
Simply loop, reading one number until end of file:
while (scanf("%d", &num) == 1)
/* do something with num */
Note: the user can enter end-of-file in a Linux console with Ctrl+D
If the user input is always numbers separeted by spaces and then at the end is an enter (newline). Then you can use the following code
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[])
{
int input;
char c;
while (scanf(" %d%c", &input, &c) == 2 ) {
printf("number is %d\n", input);
if ( c == '\n') break;
}
}
If the use want to communicate the number of input as argument
int main(int argc, char *argv[])
{
int number_of_input = atoi(argv[1]);
int input, i;
for (i=0; i<number_of_input; i++) {
scanf(" %d", &input);
}
}
and when you call you program. you call it in this way:
$ myprogram 5
and 5 here is the number of the integer that you can input
myprogram will be saved in argv[0]
5 will be saved in argv[1]
myprogram and 5 are saved as sting in the argv[] array. atoi(argv[1]) will convert the "5" as string to 5 as integer
you can make the user enter an infinite integer input in this way too:
int main(int argc, char *argv[])
{
int input, i;
while (1) {
scanf(" %d", &input);
}
}
And you can give the user a way to stop this infinite loop:
int main(int argc, char *argv[])
{
int input;
while (scanf(" %d", &input) != EOF) {
//....
}
}
here you can stop the infinite loop with
EOF = CTRL + D (for Linux)
EOF = CTRL + Z (for Windows)
At first reading, the solution to a problem like this is to loop until the user inputs a "done" character. This could be a letter Q for example. By reading in the input as a string you can process both numbers and letters. The code below processes one input at a time (followed by ) - with the possibility to either Quit (terminate program), or Clear (restart calculation, keep program running):
printf("Enter numbers to average. Type Q to quit, or C to clear calculation.\n");
char buf[256];
double sum=0, temp;
int ii = 0;
while(1)
{
printf("Input: \t");
fgets(buf, 255, stdin);
if (tolower(buf[0])=='q') break;
// allow user to "clear" input and start again:
if (tolower(buf[0])=='c') {
sum = 0;
ii = 0;
printf("Calculation cleared; ready for new input\n");
continue;
}
ii++;
sscanf(buf, "%lf", &temp);
sum += temp;
printf("At this point the average is %lf\n", sum / (double)ii);
}
printf("Done. The final average of the %d numbers is %lf\n", ii, sum / ii);
EDIT Following some back-and-forth in the comments to this and other answers, here is a solution that addresses your problem. Code has been tested - it compiles, runs, gives expected results:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void){
double sum=0;
int ii=0;
char buf[256], *temp;
char *token;
printf("Enter the numbers to average on a single line, separated by space, then press <ENTER>\n");
fgets(buf, 255, stdin);
temp = buf;
while((token=strtok(temp, " ")) != NULL) {
temp = NULL; // after the first call to strtok, want to call it with first argument = NULL
sum += atof(token);
ii++;
printf("Next token read is number %d: '%s'\n", ii, token); // so you see what is going on
// remove in final code!!
}
printf("AVERAGE: ***** %lf *****\n", sum / (double)ii);
return 0;
}
One more edit If you want to use getline instead (which you asked about in the comments - and it's even safer than fgets since it will increase the buffer size as needed), you would change to change the code a little bit. I am just giving some of the pertinent lines - you can figure out the rest, I'm sure:
double sum=0;
char *buf, *temp; // declaring buf as a pointer, not an array
int nBytes = 256; // need size in a variable so we can pass pointer to getline()
buf = malloc(nBytes); // "suggested" size of buffer
printf("Enter numbers to average on a single line, separated with spaces\n")
if (getline(&buf, &nBytes, stdin) > 0) {
temp = buf;
// rest of code as before
}
else {
// error reading from input: warn user
}
I am sure you can figure it out from here...