Using fgets and sscanf for taking a string from a file - c

i have a file.txt structured this way:
author, "title", genre, price, copies_inStock
R. Tolkien, "The lords of the rings", Fantasy, 65.50, 31
i tried using fgets and sscanf
FILE *fp = NULL;
char string[100];
char title[30], author[30], genre[30];
float price;
int copies=0;
fp = fopen("text.txt", "r");
while(!feof(fp)) {
fgets(string, 100, fp);
sscanf(string, "%[^,],%[^,],%[^,],%f[^,],%d[^ ]", autore, titolo, genere, &prezzo, &copie);
}
fclose(fp);
printf("%s %s %s %.2f %d\n", author, title, genre, price, copies);
OUTPUT
R. Tolkien "The lord of the rings" fantasy 65,50 0
Why it don't access to the variable copies?
There are better ways? Thanks

The format specifiers on this line are incorrect
sscanf(string, "%[^,],%[^,],%[^,],%f[^,],%d[^ ]", autore, titolo, genere, &prezzo, &copie);
It should be
sscanf(string, "%[^,], %[^,], %[^,],%f,%d", autore, titolo, genere, &prezzo, &copie);
The additional spaces are to filter leading whitespace - it is not automatic with %[] (or with %c).
The %f and %d were a sort of mangled hybrid of what they should be. The conversion of those stops at the first character that cannot be used, without your intervention.
Side note: you really must check the result of scanf() function family: the number of successful conversions made.

"%f[^,]" is legal, yet certainly not what OP wants.
"%f" scans for a float, then "[^,]" scans for that 4 character sequence.
There are better ways?
Use " %n" to check scanning success. It records the offset of the scan.
Use width limits like 29 in %29[^,] to not overfill the char array.
Use a space before %29[^,] to consume optional leading whitespace.
Money is always tricky.
Do not use while(!feof(fp)). Check return from fgets().
char string[100];
char title[30], author[30], genre[30];
double price;
int copies;
FILE *fp = fopen("text.txt", "r");
if (fp) {
while(fgets(string, sizeof string, fp)) {
int n = 0;
sscanf(string, " %29[^,], %29[^,], %29[^,],%lf ,%d %n",
author, title, genre, &price, &copies, &n);
// If n==0, scan was incomplete
// If string[n], string has extra garbage
if (n == 0 || string[n]) {
fprintf(stderr, "Bad <%s>\n", string);
} else {
printf("%s %s %s %.2f %d\n", author, title, genre, price, copies);
// Robust code here would do additional work:
// Trim trailing string whitespace. Range checks on numeric values, etc.
}
}
fclose(fp);
}

Related

Values lossing in fscanf

The IDE I used is Clion.
I wanna read the Line-separated data stored in .txt file.
Each line contains firstname, surname, gender, ID and age, which are str, str, str, int and int.
StudentList.txt
Olivia SWANSON F 29001 20
Emma ONEILL F 7900 19
I try to use fscanf to read the data.
FILE *fp;
char fname[20];
char sname[20];
char gender[1];
int ID;
int age;
fp = fopen("C:\\Users\\Catlover\\Desktop\\DSA\\Program2\\StudentList.txt", "r");
while(fscanf(fp, "%s %s %s %d %d", fname, sname, gender, &ID, &age)!= EOF)
{
printf("%s,%s,%s,%d,%d\n", fname, sname, gender, ID, age);
}
fclose(fp);
return 0;
But the result it return looks like a little bit weird becasue it doesn't output the second value.
Result is
Olivia,,F,29001,20
Emma,,F,7900,19
Something shocks me is that the same code runned in PellesC lead to the correct result.
I used to learn C++ so there may exists some important rules in C but I didn't notice. Can anyone show that for me?
"%s" without width
Never use "%s" in a *scanf() without a width to indicate the max number of non-white-space characters to read and save. Recall that after reading, a null character is appended. Example: if the buffer size is 100, code can only read up to 99.
char gender[1]; is too small for "F".
Wrong check
fscanf(fp, "%s %s %s %d %d", ...) can return other values than 5 or EOF. As only 5 is acceptable, test against that.
Test open success
If fopen() fails, fscanf(), fclose() are bad
Other issues exist too*
But lets use start with fixing the above.
char fname[20 + 1];
char sname[20 + 1];
char gender[1 + 1];
int ID;
int age;
FILE *fp = fopen("C:\\Users\\Catlover\\Desktop\\DSA\\Program2\\StudentList.txt", "r");
if (fp) {
while(fscanf(fp, "%20s %20s %1s %d %d", fname, sname, gender, &ID, &age) == 5) {
printf("%s,%s,%s,%d,%d\n", fname, sname, gender, ID, age);
}
fclose(fp);
}
return 0;
You need to have space to accommodate null byte also.
char gender[1];
to
char gender[2];

printing whole line with file io

This is a part of my 'Phonebook' program.
void viewall(){
int n, checking = 0;
char name[50];
fp = fopen("Phonebook.txt","r");
printf ("\n\n");
fscanf (fp, "%s %d", name, &n);
while (!feof(fp)){
printf (" %s +880%d\n", name, n);
fscanf (fp, "%s %d", name, &n);
++checking;
}
if (checking == 0){
printf (" Contact List is Empty. No Contacts to Show...");
}
printf ("\n\n");
fclose(fp);
menu();
}
This part displays all the contacts in list. But if any contact name has two parts they get separated. For example: I enter Anik Shahriar as name and then enter my number. I looked at my file and this data was there how it should be.
Anik Shahriar 01*******93
But when I wanted to display all the contacts. It got printed like this:
Anik 0
Shahriar 01*******93
How can i make the program print the whole line ?
This behaviour is caused by the lines
fscanf (fp, "%s %d", name, &n);
The format identifier %s scans for a single string. Any whitespace character ends the scan.
http://www.cplusplus.com/reference/cstdio/scanf/
%s
String of characters
Any number of non-whitespace characters,
stopping at the first whitespace character found. A terminating null
character is automatically added at the end of the stored sequence.
I suggest to use ca CSV file and use a format string like.
scanf("%[^,] %d", name, number)
Dont forget to test the scanf result value.

Scanning strings and ints from file into array in C

I am scanning from a file into parallel arrays.
I successfully scan the strings, but the ints and floats do not scan correctly!
What is going wrong here???
Num, human, and cool are arrays declared in the main function.
An example of a record in hello.txt:
Angela, Merkel, 50, 10, 9.1
void read(int *lines, char first[ENTRY][FIRST], char last[ENTRY][LAST], int *num, int *human, float *cool)
{
FILE *ifile;
int i;
ifile = fopen("hello.txt", "r");
fscanf(ifile, "%[^,] %*c %[^,] %*c %d %*c %d %*c %f", first[0], last[0], &num[0], &human[0], &cool[0]);
printf("%s", first[0]);
printf("%s\n", last[0]);
printf("%d\n", num[0]);
printf("%d\n", human[0]);
printf("%f", cool[0]);
fclose(ifile);
}
Try
fscanf(ifile, "%[^,] %*c %[^,] %*c %d %*c %d %*c %f", first[0], last[0], num, human, cool);
First, with scanf family of functions, a space matches any amount of white space, including none, in the input, also don't use %*c, it is not necessary. Second, you need to specify the comma in format string to successfully scan the fields. Third, when you scanf always check its return value to make sure the expected number fields were input. Try this fix:
void read(int *lines, char first[ENTRY][FIRST], char last[ENTRY][LAST], int *num, int *human, float *cool)
{
FILE *ifile;
ifile = fopen("hello.txt", "r");
if (ifile == NULL) return;
int ret = fscanf(ifile, "%[^,], %[^,], %d, %d, %f", first[0], last[0], &num[0], &human[0], &cool[0]);
if (ret != 5) { // input file does not match the format
return;
}
printf("%s ", first[0]);
printf("%s\n", last[0]);
printf("%d\n", num[0]);
printf("%d\n", human[0]);
printf("%f\n", cool[0]);
fclose(ifile);
}

How do I read using sscanf a character string with spaces and then a number?

I want to read from the keyboard data to be inserted in a list.
For example if I type ins "name_to_insert" 19930412, the character string name should be name_to_insert, without the quotation marks and date should be 19930412.
I don't know how to properly specify the second sscanf.
char op[4], name[30], s[50];
int date;
while (scanf("%[^\n]%*c", s)==1)
{
sscanf(s, "%s", op);
if (strcmp(op, "ins")==0)
{
sscanf(s, "%*s %[^0-9]%d", name, &date);
printf("Name is %s and date is %d\n", name, date);
}
}
I solved the problem with the quotes by simply removing the first and the last character from the name:
char op[4], nameTemp[30], *name, s[50];
int date;
while (scanf("%[^\n]%*c", s)==1)
{
sscanf(s, "%s", op);
if (strcmp(op, "ins")==0)
{
sscanf(s, "%*s %[^0-9]%d", nameTemp, &date);
name=nameTemp+1;
name[strlen(name)-2]='\0';
printf("Name is %s and date is %d\n", name, date);
}
}
You're almost right. Assuming the quotes are mandatory, you simply need to wrap the scan-set conversion specifier in quotes:
if (sscanf(s, "%*s \"%29[^\"]\" %d", name, &date) != 2)
...oops...
Note the %29[^0-9]; this limits the string to 29 bytes plus the terminal null '\0' that will fit in your 30-byte array. You should probably have a similar check on your outer scanf() too, or just use fgets() instead of scanf() there.
If the quotes are optional, you have to work a little harder, scanning for the name as non-digits and removing the quotes after the scan:
if (sscanf(s, "%*s %29[^0-9] %d", name, &date) != 2)
...oops...
Here's some test code:
#include <stdio.h>
static void scanner(const char *fmt, const char *s)
{
char name[30];
int date;
if (sscanf(s, fmt, name, &date) != 2)
printf("Scan failed {%s} and {%s}\n", fmt, s);
else
printf("{%s} and {%s} => {%s} %d\n", fmt, s, name, date);
}
int main(void)
{
char source[][30] =
{
"ins \"name in quotes\" 12345",
"ins name without quotes 12345",
};
enum { NUM_SOURCE = sizeof(source) / sizeof(source[0]) };
char format[][20] =
{
"%*s \"%29[^\"]\" %d",
"%*s %29[^0-9] %d",
};
enum { NUM_FORMAT = sizeof(format) / sizeof(format[0]) };
for (int i = 0; i < NUM_FORMAT; i++)
{
for (int j = 0; j < NUM_SOURCE; j++)
scanner(format[i], source[j]);
}
return 0;
}
Sample output:
{%*s "%29[^"]" %d} and {ins "name in quotes" 12345} => {name in quotes} 12345
Scan failed {%*s "%29[^"]" %d} and {ins name without quotes 12345}
{%*s %29[^0-9] %d} and {ins "name in quotes" 12345} => {"name in quotes" } 12345
{%*s %29[^0-9] %d} and {ins name without quotes 12345} => {name without quotes } 12345
The failed conversion is to be expected; the format looks for quotes and there aren't any.
I also played with a slightly different harness and the format string:
"%*s %1[\"]%29[^\"]%1[\"] %d"
(passing two 2-character strings, q1 and q2, to hold the quotes), but when the quotes were missing, the scan failed; the quotes aren't optional.
Note that the name has the trailing space and the quotes with the second format; those will have to be removed separately.
sscanf(s, "%*s \"%[^\"]\" %d", name, &date);

How to read specifically formatted data from a file?

I'm supposed to read inputs and arguments from a file similar to this format:
Add id:324 name:"john" name2:"doe" num1:2009 num2:5 num2:20
The problem is I'm not allowed to use fgets. I tried with fscanf but have no idea how to ignore the ":" and seperate the string ' name:"john" '.
If you know for sure the input file will be in a well-formed, very specific format, fscanf() is always an option and will do a lot of the work for you. Below I use sscanf() instead just to illustrate without having to create a file. You can change the call to use fscanf() for your file.
#define MAXSIZE 32
const char *line = "Add id:324 name:\"john\" name2:\"doe\" num1:2009 num2:5 num3:20";
char op[MAXSIZE], name[MAXSIZE], name2[MAXSIZE];
int id, num1, num2, num3;
int count =
sscanf(line,
"%s "
"id:%d "
"name:\"%[^\"]\" " /* use "name:%s" if you want the quotes */
"name2:\"%[^\"]\" "
"num1:%d "
"num2:%d "
"num3:%d ", /* typo? */
op, &id, name, name2, &num1, &num2, &num3);
if (count == 7)
printf("%s %d %s %s %d %d %d\n", op, id, name, name2, num1, num2, num3);
else
printf("error scanning line\n");
Outputs:
Add 324 john doe 2009 5 20
Otherwise, I would manually parse the input reading a character at a time or or throw it in a buffer if for whatever reason using fgets() wasn't allowed. It's always easier to have it buffered than not IMHO. Then you could use other functions like strtok() and whatnot to do the parse.
perhaps this is what you want ?
#include <stdio.h>
#include <string.h>
int main()
{
char str[200];
FILE *fp;
fp = fopen("test.txt", "r");
while(fscanf(fp, "%s", str) == 1)
{
char* where = strchr( str, ':');
if(where != NULL )
{
printf(" ':' found at postion %d in string %s\n", where-str+1, str);
}else
{
printf("COMMAND : %s\n", str);
}
}
fclose(fp);
return 0;
}
If output of it will be
COMMAND : Add
':' found at postion 3 in string id:324
':' found at postion 5 in string name:"john"
':' found at postion 6 in string name2:"doe"
':' found at postion 5 in string num1:2009
':' found at postion 5 in string num2:5
':' found at postion 5 in string num2:20

Resources