I have a string such as "4 Tom Tim 6", and i am trying to scan those values with sscanf like this
sscanf(string, "%d %s %d", &NUMBER1, NAME, &number2 )
is there any way to do this and deposit in NUMBER1 the value 4, in NUMBER2 the value 6, and in NAME the value "Tom Tim"?
I tried but sscanf splits "Tom" and "Tim" because there is a whitespacew between them and thus it also returns a incorrect value for NUMBER2.
Update:
Let me be more specific. There will always be a number at the beginning and at the end of the my string, and a substring between those numbers, which could have any length and any quantity of whitespaces, and what im trying to get is that substring in a single variable, and the numbers in the beggining and the end.
You read it in as
sscanf(string, "%d %s %s %d", &NUMBER1, &NAME, &SECONDNAME, &NUMBER2);
then concatenate them
strcat(NAME," "); // Add space
strcat(NAME,SECONDNAME); // Add second name
Make sure that NAME has enough space to hold both the first and second name. You will also have to:
#include <string.h>
In order to come up with the solution (and tell whether it is even possible with sscanf), you need to provide more information about the format of your string. It is not possible to derive anything conclusive from a single example you provided so far.
In your particular case one needs to know where the name ends and the next number begins. How do you define that in your case? Are we supposed to assume that the first decimal digit character means the end of the name and the beginning of the number2? Or is it something more complicated? If the input string contains a "Tom16" sequence, is the entire "Tom16" supposed to be the name, or should we split it into "Tom" and leave 16 for number2?
Basically, your question, as stated, allows for no meaningful answer, only for random suggestions.
Update: Your description of the format of the string is still far from being complete, but I can suggest using the following format specifier in sscanf
sscanf(string, "%d %[^0123456789]%d", &number1, name, &number2)
This will work, assuming that the "numbers" you are referring to are composed of decimal digits only and assuming that name cannot contain any decimal digits. Also note that it will not include the leading space onto the name, but it will include the trailing space. If you don't want it you'll have to trim the trailing space from the name yourself.
In any case, parsing capabilities of sscanf are rather limited. They are normally inadequate for solving problem like yours. What I have above is probably the best you can get out of sscanf. If you need something even a little more elaborate, you'll have to parse your string manually, token by token, instead of trying to parse the whole thing in one shot with sscanf.
No, not with sscanf().
You can do it 'easily' with fgets(), and parsing the line character by character
/* basic incomplete version; no error checking; name ends with whitespace */
#include <ctype.h>
#include <stdio.h>
int num1, num2;
char name[250], line[8192], *p;
fgets(line, sizeof line, stdin);
num1 = num2 = 0;
p = line;
while (isdigit((unsigned char)*p) {num1 = num1*10 + *p - '0'; p++};
while (isspace((unsigned char)*p)) p++;
while (!isdigit((unsigned char)*p)) *name++ = *p++;
while (isdigit((unsigned char)*p) {num2 = num2*10 + *p - '0'; p++};
You can't do this work with the sscanf function and a "central" string with an arbitrary number of spaces in it, since the whitespace is also your delimiter for the next field; if that %s matched strings with whitespace, it would "eat" also the 6.
If it's only your "central" field that is "special" and you have only those three fields, you should read your string backwards to find the beginning of the third field, and transform it in number; then you replace the character before the 6 with a \0, thus truncating the string before the third field.
Then you can use strtoul to convert the first field and to determine where it ends (using its second parameter); considering the string that starts from there and goes to the end of the truncated string you get the second field.
#AndreyT is pretty much correct. I'm going to guess that the middle field should stop at any digit. If that's the case, then yes sscanf can do the job:
sscanf(string, "%d %[^0-9] %d", &NUMBER1, NAME, &number2);
You really want to limit the amount that's read to the length of your buffer though:
char name[32];
sscanf(string, "%d %31[^0-9] %d", &number1, name, &number2);
I should add that technically this isn't portable as-is. To be entirely portable, you should use [^0123456789] instead of [^0-9]. Old versions of Borland compilers actually treated "0-9" as meaning the three characters '0', '-' and '9'. The standard permits this, though I don't know of any current compiler that takes its permission to be stupid.
You could :
sscanf(string, "%d %s %s %d", &NUMBER1, NAME1 , NAME2, &number2 );
strcat(NAME , NAME1);
strcat(NAME , " ");
strcat(NAME , NAME2);
But this would result in undefined behaviour, if NAME is not big enough.
I can think of a couple of ways:
1) If you always know the size of the "Tom Tim" field, use the %c format with a length specifier:
int num1;
int num2;
char name[8];
sscanf(string, "%d %7c %d", &num1, name, &num2);
name[7] = '/0';
Note that NAME needs to be large enough to hold the characters read and that it won't be null terminated so that has to be done manually.
2) If you know there are always two fields, use two string specifiers and strncat() them together:
char name1[40];
char name2[20];
int num1;
int num2;
sscanf(string, "%d %s %s %d", &num1, name1, name2, &num2);
strncat(name1, name2, sizeof(name2)-1);
You could also parse the string using strtok_r(). I'll leave that as an exercise for the reader.
Related
I'm working on an old C program and ran into a problem with scanf. I have been experimenting for many hours but still cannot get scanf to read a particular line.
The line being read in is:
01/05\t\t840.81\tvisa payment (online)\tAFCU\n
The C code using scanf is:
sscanf(lineIn, "%d/%d %f %[A-Z\(\)a-z0-9]s\t%[A-Za-z0-9]s",
&month, &year, &amt, &item, &supplier);
This line scans the month, year, amt and item variables correctly and the values can be verified. But no matter what format I used at the end, I cannot get the value of supplier to be read in.
Can anyone suggest a format that will scan in all of the variables?
I cannot get the value if of supplier
Scanning stopped on the incorrect s. #William Pursell.
Lack of a ' ' in %[A-Z\(\)a-z0-9] contributes to not scanning all of the item.
Do not use "%s" nor "%[]" without a Width.
Use a width one less than the array count.
s is wrong after a %[]
Drop the s in "%[A-Z\(\)a-z0-9]s". "%[A-Z\(\)a-z0-9]" and "%s" are separate scan directives.
Do not use undefined escapes
\( and and \) are not defined.
float with money is marginally OK
Better as double. Money has a whole hosts of issues too.
Check conversion success
Do not use the address of any array for %[]
Pass the address of the first element of the array.
This implies compiling done without all warnings enabled.
Save time. Enable all warnings.
Allow spaces in item[], supplier[]
... the space in the %100[A-Za-z0-9 ] conversion has a different meaning from the space before %n: a space in a scan set matches the space character whereas a space elsewhere in the format string matches any white space characters, including TAB and newline. #chqrlie
I recommend using " %n" to detect complete conversion success.
Be generous, allow white-space between tokens. %d, %f, %s already allow leading white-space. %[] does not.
int main(void) {
const char *lineIn = "01/05\t\t840.81\tvisa payment (online)\tAFCU\n";
int month, year;
double amt;
char item[90 + 1];
char supplier[100 + 1];
// sscanf(lineIn,"%d/%d %f %[A-Z\(\)a-z0-9]s\t%[A-Za-z0-9]s",
// &month,&year,&amt,&item,&supplier);
int n = 0;
sscanf(lineIn, "%d /%d%lf %90[A-Z()a-z0-9 ] %100[A-Za-z0-9 ] %n", //
&month, &year, &amt, item, supplier, &n);
if (n == 0 || lineIn[n]) {
puts("Failed");
} else {
puts("Success");
}
}
I need to take input from the user that is a single line and has 5 values which I extract from that input ('c' needs to be 5). The input is this format: "word(number) number word sentence". But the problem is with the "word(number)" input area. I cannot get it so that the values are read from that part of the input and stored in command, and num1. Below is the code I tried but it does not seem to be working.
c = sscanf (line, "%s(%d) %d %s %128[^\n]", command, num1, num2, message, message2);
When I make it so that the user enters "word (number) number word sentence" instead, with a space before the brackets, with also changing the code from %s(%d) to %s (%d), that seems to work. But I need it without any space between the values command and num1.
You will need to exclude the parenthesis from the first string. Assuming the "command" is strictly alphanumeric and underscore, you can do this:
c = sscanf(line, "%[_a-zA-Z0-9](%d) %d %s %128[^\n]", command, &num1, &num2, message, message2);
Also, you need ampersands on num1 and num2; perhaps those were typos?
For safety, you should put length limits on command and message as you do for message2. Assuming 'char command[21], message[33];`, this would be:
"%20[_a-zA-Z0-9](%d) %d %32s %128[^\n]"
I cant figure out whats wrong. Am i using format specifiers in wrong way? Someone please help i am very new to coding.
#include <stdio.h>
#include <stdlib.h>
int main()
{
char name[20];int age;char grade;double gpa;char area[10];
printf("User Input\n");
printf("Enter your name: ");
fgets(name,20,stdin);
printf("Your name is %s\n",name);
printf("Enter your age: ");
scanf("%d\n",&age);
printf("Your age is %d\n",age);
printf("Enter you grade: ");
scanf("%c\n",&grade);
printf("Your grade is %c\n",grade);//Why is this giving an int output?
printf("Enter your gpa: ");
scanf("%f\n",&gpa);
printf("Your gpa is %f\n",gpa);
printf("Enter your area: ");
scanf("%s\n",&area);
printf("Your area is %s",area);//This shows grade input
return 0;
}
Output
You use fgets correctly when reading name. I'd recommend also using fgets for all your other inputs, and then parsing the intended values out of them. For example:
char age_str[20];
fgets(age_str, 20, stdin);
age = strtol(age_str, NULL, 10);
This is preferable to using scanf directly for non-string inputs since if input fails to match a format string, it will remain in stdin and screw up the other scanf calls.
If you would like to use scanf correctly:
Check its return value to see if it matches the number of format specifiers in the string. If not, some inputs were not successfully read. You may want to use a do/while loop for this.
Begin your format strings with a space, as in " %c", so that any whitespace remaining in stdin will be skipped over.
Don't end your format strings with a newline.
Some things to remember about scanf:
Most conversion specifiers like %s, %d, and %f will skip over leading whitespace - %c and %[ will not. If you want to read the next single non-whitespace character, use " %c" - the leading blank tells scanf skip over any leading whitespace before reading the next non-whitespace character;
For what you are trying to do, you should not use \n in your format strings - it will cause scanf to block until you enter a non-whitespace character;
You do not need to use the & operator on array expressions like area; under most circumstances, array expressions are converted to pointer expressions1. Honestly, you should read area the same way you read name, using fgets (and you should always check the result of fgets), or you should specify the maximum field width in the specifier: scanf( "%9s", area ); (a 10-element array can hold up to a 9-character string, since one element has to be reserved for the string terminator);
You should get in the habit of checking the result of scanf - it will return the number of successful conversions and assignments. For example, scanf( "%d %d", &x, &y ) will return 2 if both x and y are read successfully. It will return EOF if end-of-file is signaled or there's a read error.
scanf will read up to the next character that doesn't match the conversion specifier - IOW, if you're using %d, then scanf will skip over any leading whitespace, then read up to the next character that isn't a decimal digit. That character is left in the input stream. This means if you're using %d and type in 123e456, scanf will read up to that 'e' character and assign 123 to the target. If you try to read again with %d, scanf will immediately stop reading on that e and return 0 without assigning anything to the target (this is called a matching failure). This will continue until you remove that 'e' from the input stream (such as with getchar or fgetc or scanf with the %c specifier, etc.
You need to make sure the types of the arguments match the format specifier. %s expects an argument of type char *, %d expects int *, %f expects float *. %x expects unsigned int *, %lf expects double *, etc.
This is one of the "deeply unintuitive" aspects of C I was talking about in my comment.
So my text file looks similar to this
1. First 1.1
2. Second 2.2
Essentially an integer, string and then a float.
Using sscanf() and fgets() in theory, I should be able to scan this in (I have to do it in this format) but only get the integer can someone help point what I am doing wrong?
while(!feof(foo))
{
fgets(name, sizeof(name) - 1, foo);
sscanf(name,"%d%c%f", &intarray[i], &chararray[i], &floatarray[i]);
i++;
}
Where intarray, chararray, and floatarray are 1D arrays and i is an int initialized to 0.
The structure of the loop is wrong; you should not use feof() like that and you must always check the status of both fgets() and sscanf(). This code avoids overflowing the input arrays, too.
enum { MAX_ENTRIES = 10 };
int i;
int intarray[MAX_ENTRIES];
float floatarray[MAX_ENTRIES];
char chararray[MAX_ENTRIES][50];
for (i = 0; i < MAX_ENTRIES && fgets(name, sizeof(name), foo) != 0; i++)
{
if (sscanf(name,"%d. %49s %f", &intarray[i], chararray[i], &floatarray[i]) != 3)
...process format error...
}
Note the major changes:
The dot after the integer must be scanned by the format string.
The chararray has to be a 2D array to make any sense. If you read a single character with %c, it would contain the space after the first number, and the subsequent conversion specification (for the float value) would fail because the string name is not a floating point value.
The & in front of chararray[i] is not wanted when it is a 2D array. It would be needed if you were really reading a single character in a 1D array of characters instead of the whole string such as 'First' or 'Second' from the sample data.
The test checks that three values were converted successfully. Any smaller value indicates problems. With sscanf(), you'd only get EOF returned if there was nothing in the string for the first conversion specification to work on (empty string, all white space); you'd get 0 returned if the first non-blank was alphabetic or a punctuation character other than + or -, etc.
If you really want a single character instead of the name, then you'll have to arrange to read the extra characters in the word, maybe using:
if (sscanf(name,"%d %c%*s %f", &intarray[i], chararray[i], &floatarray[i]) != 3)
There's a space before the %c which is crucial; it will skip white space in the input, and then the %c will pick up the first non-blank character. The %*s will read more characters, skipping any white space (there won't be any) and then scanning a string of characters up to the next white space. The * suppresses an assignment; the scanned data won't be stored anywhere.
One of the major advantages of the fgets() plus sscanf() paradigm is that when you report the format error, you can report to the user the complete line of input that caused problems. If you use raw fscanf() or scanf(), you can only report on the first character that caused trouble, typically up to the end of the line, and then only if you write code to read that data. It is fiddlier (so the reporting is usually not very careful), and the available information is not as helpful to the user on those rare occasions when the reporting tries to be careful.
You need to change your format string to:
"%d %s %f"
The spaces are because you have spaces in your input data, the %s because you want to read a multi-character string at that point (%c only reads one character); don't worry though, as %s won't read past a space. You'll need to make sure you've got enough space in the target buffer to read the string, of course.
If you only want the first character of the second word, try:
"%d %c%s %f"
And add an extra (dummy) buffer to receive the string parsed by %s which you want to discard.
won't it be %s for string else it will only read a character with %c and then the float value might be affected.
try "%d %s %f"
%s won't help since it may read the float value itself. as far as I know, %c reads a single character. then it searches for a space that leads to problem. To scan the word, you can use a loop (terminated by a space ofcourse).
Hey,
I'm trying to get this function to get the following output with the listed input, the "..." is where I'm not sure what to write:
void Question8(void)
{
char sentence[100];
int grade;
scanf(….);
printf("%s %d", sentence, grade);
}
Input:
My CS Grade is 1000
Output:
My CS Grade is 100
However, the kicker is that I need the scanf to read a c-string and then an int with a single scanf command, is this even possible?
Edit:
I can only edit the code in the location with the three periods ( "..." ), I cannot use anything more. I can assume that the input listed is expected but I cannot change anything outside of the three periods.
The output does not contain typos, the purpose of this assignment is to use flags and escape sequences.
It is possible to read pre-formatted string using scanf, however the format must be strict.
This version will continue to read the input until a digit is encountered and then read an integer.
Here is your code again:
char sentence[100];
int grade;
scanf("%[^0-9] %d",sentence,&grade);
printf("%s %d\n", sentence, grade);
I'll get this over with quick:
<obligatory_rant>
stupid question, but I guess it's homework and you're
stuck with these absurd limitations
</obligatory_rant>
Then, if you need to read everything up to but excluding the first digit, then the number:
if (scanf("%100[^0-9] %3d", text, &number) == 2)
...
Notes:
100 in "%100[... should be whatever your actual buffer size is to protect against buffer overrun.
The %3d documents that at most 3 digits should partake the the numeric value, so 1000 is correctly read as 100.
[^...] means the string made up of characters not ("^") in the following set, which is then specified as 0-9 - the digits.
if (... == 2) tests whether both positional parameters were scanned / converted successfully.
If you can't add an if and error message, then simply:
scanf("%100[^0-9] %3d", text, &number)
Tested in Visual Studio 2008
#include <stdio.h>
int main()
{
char sentence[100];
int grade = 0;
scanf("%[^0-9] %d",sentence,&grade);
printf("%s %d", sentence, grade);
return 1;
}
Input :
My CS Grade is 100
Output :
My CS Grade is 100
This is a really horrible question. A correct set of scanf parameters would be "%14c%3d", sentence, &grade
Because a space is included in the printf statement the trailing space needs to not be stored in sentence. Because the input contains other spaces there is no other solution (that I can thing of) than a fixed length. The integer parsing also requires a fixed length to truncate 1000 to 100.
I can think of no reason to ever write code anything like this. The code fits the requirements but wouldn't be useful in any other circumstances. I think that this is a very poor training exercise.