Problem with sscanf not reading the 3 variables i want in C - c

I have an .txt with "323,John of Sea,11.2" (ignore the ")
and i want to read this and then divide it into 3 variables like: int number1 / char Name[100] / float number2
#include <stdio.h>
int main()
{
int number1;
float number2;
char Name[100],Phrase[100];
FILE *inf ;
if((inf = fopen("Information.txt","r")) == NULL){
printf("Erro!\n");
}
while (fgets(Phrase,100,inf) != NULL )
{
sscanf(Phrase , "%d,%s,%f", &number1 , Name, &number2 );
printf("%d %s %f \n", number1, Name, number2);
}
}
323,John of Sea,11.2
Well the problem is when i compile everthing it gives me 323 John 0.000000 an it should give me 323,John of Sea,11.2 i have tried many things but nothing seems to work. IMPOTANT = It needs to be separeted in 3 varibels 1/ int 1 / chat vector .
Sorry for the english and if you can i would realy apreciate the help.

The problem is that the %s scanf format specifier will only match a single word of input. Therefore, instead of matching John of Sea, it will only match John and leave of Sea on the input stream.
If you want to read all characters up to (but not including) the comma, then you should use %[^,] instead of %s.
Also, you should always check the return value of scanf to verify that it was able to match all 3 arguments, before attempting to use these arguments.
Additionally, I recommend to limit the number of characters written to Name, so that if the input is too large to fit into Name, no buffer overflow will occur (which may cause your program to crash). Since Name has a size of 100 characters, it has room for 99 normal characters plus the terminating null character. Therefore, I recommend to limit the number of matched characters to 99, by using %99[^,] instead of %[^,].
For the reasons stated above, I recommend that you change
sscanf(Phrase , "%d,%s,%f", &number1 , Name, &number2 );
to:
if ( sscanf( Phrase, "%d,%99[^,],%f", &number1, Name, &number2 ) != 3 )
{
fprintf( stderr, "Parsing error!\n" );
exit( EXIT_FAILURE );
}
Note that you will have to additionally #include <stdlib.h> in order to be able to use exit and EXIT_FAILURE.

Related

Read fixed size register with spaces from file

Im trying to read a fixed size register from a text file in c.
The structure of the register is the following:
00030REGIST X1Y005.0
5 characters for an integer
10 characters for an string(with spaces)
5 characters for a float
When I try to read the register, I get the following result:
00030REGIST X1Y005.00.00000
I get a 0.00000 at the end of the string
#include <stdio.h>
#include <stdlib.h>
int main () {
int id;
float price;
char desc[11];
FILE * data_file;
//Reading from file
if(!(data_file = fopen("./products.txt","r"))){
printf("\nError reading file\n");
exit(2);
}
// The value of the register is 00030REGIST X1Y005.0
// But i get 00030REGIST X1Y005.00.00000
while (fscanf(data_file,"%05d %[^\n]10s %05f", &id, desc, &price) != EOF) {
printf("%05d%s%05f\n",id , desc, price);
}
fclose(data_file);
return(0);
}
Edit: I changed the program to read 10 string characters which can include digits.
The format specifier %[^\n]10s is a strange hybrid of %s and %[]. I suggest the following, here just a single string for the sake of an example, and newlines added to the output for clarity.
#include <stdio.h>
int main(void)
{
int id;
float price;
char desc[11];
char input[] = "00030REGIST X1Y005.0";
int res = sscanf(input, "%d%10[^\n]%f", &id, desc, &price);
if(res == 3) {
printf("%05d\n%s\n%05f\n",id , desc, price);
}
}
Program output:
00030
REGIST X1Y
5.000000
You have:
while (fscanf(data_file,"%05d %[^\n]10s %05f", &id, desc, &price) != EOF)
You probably need:
while (fscanf(data_file,"%5d %40[^\n0-9] %5f", &id, desc, &price) == 3)
The 40 is based on the size of desc (you specify one less in the format string than in the length declared for the array). Note that a scan set %[…] is a conversion on its own. The 10s in your version is looking for the specific characters 1, 0, and s (and that will fail — the next character would be a newline or end-of-file because you didn't specify a size for the scan set). And the test should be for the expected number of conversions; anything else is an error of some sort.
If register names can contain digits, you're hosed because REGISTER XVY is 12 characters counting the space (which contradicts your claim that register names are up to 10 characters). Specifying any smaller value than 12 in %12[^\n0-9] won't convert that name and the following number because it will leave non-numeric characters in the input.
If you must have digits in the register name, you have to adopt a different strategy. You'd read the line, then strip the leading 5 digit number and convert that, strip the trailing 5 digit number and convert that, and take what's left as the register name, possibly stripping leading and trailing blanks.

Question about how to get different type of data in a line in C

is there anyway to use scanf to scan a line of things which contain different types of data for example , string and float and int IN C (c99)?
Heres my code
{
float numberwithdot;
int number;
printf("Enter name: ");
scanf("%d,%f",&number,&numberwithdot);
printf("Your name is %d %f.",number,numberwithdot);
return 0;
}
after i run the program and enter the value (23 2.3), it process finished with exit code 0 but out put with (2 2.000000) ,which is not good.Anyway , wanted to know is it possible or not using scanf to do "that"
*******New*******
Sorry i may have post a idiot sample for the question , take a look at my "failure" code
{
int recordnumber ,itemnumber ,quantity ;
float weight ;
char itemname[30];
char category[30];
char namelocationstat[30];
printf("Please enter 1> Record number, 2> Item name, 3> Item number, 4> Category, 5> Quantity\n""6> Weight 7> Recipient-, 8> Final Destination-, and 9> Delivery status :\n");
scanf("%d,%s,%d,%s,%d,%f,%s",&recordnumber, itemname,&itemnumber, category,&quantity,&weight, namelocationstat);
printf("%d,%s,%d,%s,%d,%.1f,%s", recordnumber, itemname, itemnumber, category, quantity, weight, namelocationstat);
}
i was think to use strtok to define between names and adress(havent finish the code yet) , but that off the topic , The problem i got this (1,��,0,#,0,0.0,) after inputting (1 asd 2 sad 1 1.1 asdasd asd da)
hopefully u guys can solve it , it helps alot!!
***NEW****
Problem Solved , its because of the comma i added , how folish i am !!!
Sorry for your time everyone , You guys are so awesome , sacrificing time to help ppl like me !! Wish u all have a good day ! Sir or Madam!
P.S. new "programmer" alert
Anyway , wanted to know is it possible or not using scanf to do "that"
Yes, it is.
scanf continues processing the input as long as the input matches (i.e. can be turned into something matching) the format stream.
Since you match for:
scanf("%d,%f",&number,&numberwithdot);
^
comma
the input must contain an integer (%d) followed by a comma followed by a float (%f).
Since your input is 23 2.3 without a comma scanf can only match the first integer.
Had your input been 23,2.3 with a comma, your code would have worked.
An advice: Always check the value returned by scanf like:
if (scanf("%d,%f",&number,&numberwithdot) != 2)
{
// Error! Did not scan exactly 2 values
}
Is it possible to use scanf() to scan different type of data and store it (string and float and int)?
Yes, of course:
#include <stdlib.h>
#include <stdio.h>
int main(void)
{
char str[21]; // Space for 20 characters + a terminating '\0'.
float f;
int i; // +------------------------------ % begins a conversion specification
// | +---------------------------- 20 specifies the with of the string to read
// | |+--------------------------- s conversion format specifier*)
// | || +------------------------- f conversion format specifier for float
// | || | +----------------------- i conversion format specifier for int
if (scanf("%20s%f%i", str, &f, &i) != 3) { // scanf() returns the number of
// arguments successfully assigned
fputs("Input error! Expected a string containing no whitespace, a float and an integer.\n\n", stderr);
return EXIT_FAILURE; // return an error code
}
printf("\"%s\"\n%f\n%i\n", str, f, i);
}
*) When using "%s" A L W A Y S specify the width of the string to read: "%[WIDTH]s", "%20s" in the example. The target (the array str in our example) must be at least of size width + 1.
It works just fine (just tested it). The problem is how you're imputing your numbers. Try this:
23,2.3
Furthermore, if you desire to input the way you tried, you need to change the string passed as argument to scanf:
scanf("(%d %f)",&number,&numberwithdot);

C: integers not printing correctly

(Hi guys. I tried searching for the problem I'm having and can't seem to find the solution so far. I'm totally new to programming and am learning C currently, but I am a complete noob so I apologize in advance if I'm making a dumb mistake.)
Here's the problem: Im tryna scan 4 integers and print their values using a while loop. The problem is, the numbers are being printed as crazy long numbers not as the ints that are input. I tried scanning and printing a single int and it printed fine but once I use multiple ints, it starts screwing aroud.
Here's my code:
#include <stdio.h>
int main()
{
int i, n1,n2,n3,n4;
printf("Enter 4 numbers.");
for(i=0;i<4;i++)
{
printf("\n\nEnter number %d: ", i+1);
scanf("%d,%d,%d,%d", &n1,&n2,&n3,&n4);
printf("%d,%d,%d,%d", n1,n2,n3,n4);
}
}
Two things:
the input format given in scanf() should match exactly to the input value for a successful scan. [You need to have ,s in your input]
Always check for the success of scanf() to ensure proper scanning of value. scanf() returns the number of items successfully matched and scanned.
So, you should change your code to something like,
if ( scanf("%d,%d,%d,%d", &n1,&n2,&n3,&n4) == 4)
{
// use n1, n2, n3, n4
}
else
//don't use them, return some error.
Note: Always initialize local variables. Many a time it will save you from the undefined behaviour of read-before-write scenario.
Also, [maybe?] the for loop is not required, as you're scanning all the four numbers at a time.
When you have scanf("%d,%d,%d,%d", &n1,&n2,&n3,&n4);
you must give your input as say
1,2,3,4( commas are needed)
And you said you want to read 4 numbers and you have a scanf that gets the 4 numbers. So there is no need for a loop here. If you want to loop get one number each time inside the loop.
You are looping 4 times, expecting to read 4 numbers in each loop...
Generally speaking, scanf() is a poor tool for parsing any kind of input that might not match the expected format -- and nothing is as fickle as user input. I usually advise reading in whole lines of input (via fgets()), and then parsing them in-memory as appropriate (which, in this case, would probably mean using strtol() and checking how much of the input string was parsed via its second parameter).
This, for example, is much more robust:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#define LINELEN_MAX 100
int main()
{
int i;
char input[ LINELEN_MAX ];
char * current;
char * end;
while ( 1 )
{
// whitespaces or commas do not matter,
// and neither does the amount of numbers.
puts( "Enter numbers, or 'q' to quit." );
if ( fgets( input, LINELEN_MAX, stdin ) == NULL )
{
puts( "Error on read." );
return EXIT_FAILURE;
}
if ( *input == 'q' )
{
puts( "Quitting." );
return EXIT_SUCCESS;
}
if ( input[ strlen( input ) - 1 ] != '\n' )
{
puts( "Line exceeded maximum width." );
return EXIT_FAILURE;
}
current = input;
end = input;
while ( *current )
{
if ( !isdigit( *current ) )
{
// skip non-digits
++current;
}
else
{
// parse 1..n digits and print
printf( "%ld\n", strtol( current, &end, 10 ) );
current = end;
}
}
}
}
One reason may be that all your values are being printed onto the same line without any space between them.
Basically you are printing 4 numbers continuously in one line which makes it look like one big number.
I advise you to add a new line format specifier.(If you are a newbie, then you might not understand this, so here are some links that may be useful)
http://www.codingunit.com/printf-format-specifiers-format-conversions-and-formatted-output
There is also the problem that you are reading 4 numbers 4 times , that is you are reading 16 variables in total. For this code , you actually don't need a for loop.

How can get int - string - float from file?

I have a file like this one:
1234 Homer 18.5
1223 Bart 25.5
9341 Lisa 30.0
3420 Marge 28.4
8730 Abram 26.7
1876 Barns 27.8
1342 Smiters 23.0
7654 Milhouse 29.7
How can i get the first part ( for example 1234 ) of each line?
And how can i get the name ( for example Homer ) of each line?
I wrote this code below:
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main()
{
char ch[25];
int i, num;
FILE *fp;
fp = fopen("studenti.txt","r"); // read mode
if( fp == NULL )
{
perror("Error while opening the file.\n");
exit(EXIT_FAILURE);
}
printf("The contents of numeri.txt file are :\n");
for(i = 0; i < 25; i++){
while( ( ch[i] = fgetc(fp) ) != EOF ){
if(!(ch[i] >= 'A' && ch[i] <= 'Z') && !( ch[i] >= 'a' && ch[i] <='z')){printf("%c",ch[i]);}
}}
fclose(fp);
return 0;
}
How can do that??
This is what fscanf function is for:
int n;
char name[25];
float x;
FILE* fp = ...
while (fscanf(fp, "%d%24s%f", &n, name, &x) == 3) {
// Do something with the data you just read:
printf("int=%d name='%s' float=%f\n", n, name, x);
}
Several things to note about the above:
fscanf returns the number of items it read from the file. Continue calling fscanf while it returns 3
%24s means "a string of up to 24 characters in length". name has 25 characters, because the last one is used for null termination
int and float parameters are passed to fscanf with an ampersand, because fscanf needs a pointer. String, on the other hand, takes no ampersand, because it's equivalent to a pointer.
If you are sure of the text format, the simplest may be to use fscanf.
int num;
char name[1024];
float grade;
fscanf(fp, "%d %s %f", &num, name, &grade);
Be aware, if the name is longuer than 1024 chars, you will have buffer overflows. If the format is not sure, you need to check the return code of fscanf (see the man page).
The only thing you ever read from files is bytes.
The first step is to check if the bytes are valid characters, and convert the bytes into characters if necessary. This is not necessarily simple. If the bytes are supposed to be ASCII, then you might only need to check if the bytes are valid ASCII (e.g. not less than or equal to zero and not above 0x80; and possibly not control characters like "delete" or "vertical tab").
However, where names are involved it's extremely unlikely that ASCII is adequate. This means you want something like UTF-8. In that case, at a minimum you need to check if the bytes are valid (variable length) UTF-8 sequences; in addition to checking for invalid characters (like "delete" or "vertical tab").
More complicated is if you simply don't know what the bytes are. There are ways to auto-detect the character encoding (but it's heuristics not 100% reliable).
The second step is parsing. Parsing typically has 2 equally important goals. The first goal is to convert the characters into a more easily processed form - e.g. like maybe a structure with 3 fields (an integer, string and float) representing each line of characters. The second goal of parsing is reporting any errors to the user in an easily understood manner.
For example, maybe the first number on each line must be a 4 digit code (like "0123"); and if there's only 3 digits (like "123") then you want to generate an error (e.g. "ERROR: CourseID too short on line 5 of file 'foo.txt'") so that it's easy for the user to know exactly what the problem is and easy for the user to fix it.
Note: I don't think I've ever seen code that uses fscanf() that is close to (what I consider) acceptable. There's almost never useful/descriptive error messages.

How to parse an input line with sscanf?

I have an input .txt file that looks like this:
Robert Hill 53000 5
Amanda Trapp 89000 3
Jonathan Nguyen 93000 3
Mary Lou Gilley 17000 1 // Note that came contains of 3 parts!
Warren Rexroad 72000 7
I need to read those lines and parse them into three different categories: name (which is an array of chars), mileage (int) and years(int).
sscanf(line, "%[^] %d %d ", name, &mileage, &years);
This doesn't work very well for me, any suggestions?
THE PROBLEM
The problem with the current specifier passed to sscanf is that it is both ill-formed, and even when fixed it won't do what you want. If you would have used [^ ] as the first conversion specifier, sscanf would try to read as many characters as it can before hitting a space.
If we assume that a name can't contain digits specifying [^0123456789] will read the correct data, but it will also include the trailing space after the name, but before the first mileage entry. This is however easily solved by replacing the last space with a null-byte in name.
To get the number of characters read into name we can use the %n specifier to denote that we'd sscanf to store the number of bytes read into our matching argument; we can later use this value to correctly "trim" our buffer.
We should also specify a maximum width of the characters read by %[^0123456789] so that it doesn't cause a buffer-overflow, this is done by specifying the size of our buffer directly after our %.
SAMPLE IMPLEMENTATION
#include <stdio.h>
#include <string.h>
int
main (int argc, char *argv[])
{
char const * line = "Mary Lou Gilley 17000 1";
char name[255];
int mileage, years, name_length;
sscanf(line, "%254[^0123456789]%n %d %d ", name, &name_length, &mileage, &years);
name[name_length-1] = '\0';
printf ("data: '%s', %d, %d", name, mileage, years);
return 0;
}
data: 'Mary Lou Gilley', 17000, 1
If you have a function that finds the positon of the first digit like so:
// This function returns the position of the
// space before the first digit (assuming that
// the names dont contain digits)...
char *digitPos(char *s){
if isdigit(*(s+1)) return s;
else return digitPos(s+1);
}
You can then just separate the two variables by inserting a '\0' at the right position like so:
pos = digitPos(line); // This is a pointer to the space
*pos = '\0';
strcpy(name, line);
sscanf(pos + 1, "%d %d", &mileage, &years);
This might help you get started. It lacks the intelligence of BLUEPIXY's solution which handles the trailing whitespace a little better than mine ( or you could chop it off yourself).
dan#rachel ~ $ echogcc -o t t.c
dan#rachel ~ $ echo "Dan P F 3 21" | ./t
Name: Dan P F ,
Mileage: 3,
Years: 21.
Here's the code.
#include <stdio.h>
#include <string.h>
int main(){
char *buf;
int mileage, years;
while(!feof(stdin) ){
if( fscanf( stdin, "%m[^0-9] %d %d", &buf, &mileage, &years) == 3 ){
fprintf(stderr, "Name:\t %s,\nMileage:\t %d,\nYears:\t %d.\n",
buf, mileage, years
);
}
}
}
You have discovered one of the three reasons *scanf should never be used: it's almost impossible to write a format specification that handles nontrivial input syntax, especially if you have to worry about recovering from malformed input. But there are two even more important reasons:
Many input specifications, including your %[...] construct, are just as happy to overflow buffers as the infamous gets.
Numeric overflow provokes undefined behavior -- the C library is licensed to crash just because someone typed too many digits.
The correct way to parse lines like these is to scan for the first digit with strcspn("0123456789", line), or while (*p && !isdigit(*p)) p++;, then use strtoul to convert the numbers that follow.
int pos;
sscanf(line, "%*[^0-9]%n", &pos);
line[--pos]=';';
sscanf(line, "%[^;]; %d %d ", name, &mileage, &years);

Resources