fscanf () read error - c

I have a text file which I'm reading in first and last names into a character array of size 20. I'm creating a struct array which holds the "People's" information.
The text file looks as follows:
John Robbins
Teresa Jones
my struct is defined as such:
struct people {
char name[20];
};
Declaration of Persons struct:
struct people *persons[2];
After declaring a new struct I read in the names with the following code:
for(i=0; i<2; i++)
{
fscanf(dp, "%[^\n]s", &persons[i].name[20]);
}
However, once I output the names to the console I receive the following:
hn Robbins
sa Jones
I've done extensive research and cannot find the solution to this problem. Has anyone experienced this problem?

fscanf(dp, "%[^\n]s", &persons[i].name[20]);
This reads the line up to a newline, and then attempts to read an s which will fail. The line up to the newline will be stored after the end of the name array in your people struct (which means it will overwrite into the next element of the persons array.
You want something like:
fscanf(dp, " %19[^\n]%*[^\n]", persons[i].name);
instead -- the initial space skips leading whitespace (including any newline from a previous line.) The %19[^\n] reads up to 19 characters or up to a newline and stores it, followed by a terminating NULL (so will use up the entire 20 byte name array, but no more). The %*[^\n] will read any additional characters on the line up to (and not including) the newline and throw them away.
You also want to check the return value of the fscanf call to make sure it doesn't get an error or end of file:
#define MAX_PEOPLE 2
struct people persons[MAX_PEOPLE];
i = 0;
while (i < MAX_PEOPLE && fscanf(dp, " %19[^\n]%*[^\n]", persons[i].name) > 0)
i++;

Related

Is it possible to use a strcpy inside a scanf or a sscanf? (in C language)

I read somewhere that when we use structs we can't just write something like «example1.name = "Jim";», instead of it we should write «strcpy(example1.name, "Jim");»
The thing is that I need to use a struct and I need to scanf (and right after that sscanf) some information that corresponds to a string and I don't know how I should do it.
Could somebody help me, please?
Edit:
My code isn't complete and I know it's wrong, but I'll post it here so that you know what I am talking about:
int main(){
struct Cards{
int value;
char type[4];
};
for(i=1, 0 < i && i < 11, i++){
struct Cards cardi;
}
scanf("%d %s", &cardi.value, cardi.type);
/*at this point I just know it's wrong but I am really bugged.
I thought about something like «scanf("%d %s", &cardi.value, strcpy(cardi.type, "%s"));»
but I just know it's very wrong */
return 0;
}
This isn't true only about structs, but for all strings. You can use = for strings, only when you initialize them. You can scanf a string and place it in a string. Example:
scanf("%s", my_struct.str);
If you already have a string and you want to pass it in a struct, or in an other string variable you then need strcpy:
char str1[] = "abc", str2[4];
strcpy(str2, str1);
or
strcpy(my_struct.str, str1);
Edit:
for(i=1, 0 < i && i < 11, i++) {
struct Cards cardi;
}
In your code cardi is not card0, card1 etc, it is a struct Cards variable with the name cardi.
If you want to store 10 cards, you sould make an array of struct Cards with capacity of 10 like:
struct Cards array[10];
for (i = 0; i < 10; i++) {
scanf("%d %s", &array[i].value, array[i].type);
}
Anyway i suggest that you focus on learning the basics on arrays, strings and pointers before you use structs.
Edit 2: You don't want to define structs inside your main, because in that way, if you write a function it will not "see" your struct. Structs usually are written in the top of the code.
Your use of scanf() is correct. However scanning strings using %s is dangerous and discouraged. This is because the type member of your structure has space for only 4 characters, including the terminating '\0' (NUL) character. Entering a string longer than 3 characters will result in the extra characters being written into an unknown area of memory likely corrupting other variables and leading to incorrect program execution or a program crash. You can correct this by adding a string length limit, simply replace %s with %3s, where 3 is the maximum numbers of characters that will be accepted (scanf will automatically add a '\0' (NUL) character following the last scanned character).
Below are some additional comments:
If you want to declare an array for 10 Cards, you could do it this way without any loops:
struct Cards cardi[10];
To scan values into the first card (C arrays are 0-based):
// this will scan into the first card; type field is restricted to at most 3 characters
scanf("%d %3s", &cardi[0].value, cardi[0].type);
At the top of the file you'll want to add:
#include <stdio.h>
This header file provides a prototype (declaration) for the scanf function (among many others).

Consecutive scanf is adding up strings [duplicate]

This question already has answers here:
How to prevent scanf causing a buffer overflow in C?
(6 answers)
Closed 6 years ago.
I've been having a problem with a very simple program and I really don't know why. There is a struct for a person:
typedef struct {
char name[50];
char p_id[11];
char cel[11];
int by;
int id;
} Person;
Now, there is another struct which stands for the list of contacts:
typedef struct {
Person * people;
} lContacts;
I've been trying to include the person's data to it, and add that person to the contact list. The person is being added normally, so I won't post the code here, but there is something wrong happening when I read the string:
void include(lContacts * myContacts)
{
Person p;
scanf("%s", p.name);
scanf("%d", &p.by); //birth year
scanf("%s", p.p_id);
printf("TEST P_ID: %s\n\n", p.p_id);
scanf("%s", p.cel);
printf("TEST P_ID AGAIN: %s\n\n", p.p_id);
myContacts->people[index]=p; //don't worry about the index, there is a piece of code I'm omitting to make it easier to read, just assume it is right.
}
}
Notice that I have a print test there, because when I listed my contacts, the contact p_id had itself concatenated with the cel, so I printed the whole code until I found the mistake was there.
Here is a input example:
Name
1991
11111111111
<console prints| TEST P_ID: 11111111111>
22222222222
<console prints| TEST P_ID AGAIN: 1111111111122222222222>
however, if I print p.cel, it is correctly printed
<console prints 22222222222>
Any ideas? Maybe I should use '&' when scanning strings? (I read about it and the way I understood, there is no need.. is that right?)
This is actually a very minute mistake here. Basically, you are experiencing a Buffer Overflow in the string p.p_id.
When you input the value for p.p_id as 11111111111 (11 times 1), you must realise that the actual capacity for the p_id string is declared as just 11 which must include the NULL character at the end.
If you assign 11111111111 to p_id there will be no space left for the NULL character in that string and so there will NOT be any at its end.
When you input the value for next member string of the structure, the same happens there, too.
Now, when you are trying to print the value of p.p_id the value will print until a '\0' (NULL character) is found in the string. But, there is none here, so the next string will start printing! (The buffer or string p_id overflows)
This is happening due to the member alignment done in structures in C. The next string will be stored in a consecutive memory cell, so the printing will continue. (If there was a third consecutive string member, then due to the buffer overflow in the second string, it will print too!)
But, the printing stops after second string because either there is no data in the next consecutive memory cell due to certain struct alignment or there may be an actual 0 value in the next cell which is interpreted as NULL character.
To avoid, either use a larger size for the character array or use dynamically allocated strings.
As #skrtbhtngr pointed out, you have a buffer overflow. In order to prevent this in the future, you should use fgets instead of scanf on unknown string inputs. This example will exit(1) if the input is corrupted.
int getsafestring(char* s, int maxlen)
{
if(fgets(s,maxlen,stdin) == NULL) return 1; // read error
if(strlen(s) == 0) return 2; // other read error
if(s[strlen(s)-1] !='\n') return 3; // buffer overflow error
s[strlen(s)-1]=0; // replace newline with null
return 0;
}
if(Getsafestring(p.name,sizeof(p.name))) exit(1);
if(scanf("%d", &p.by) != 1) exit(1);
if(getsafestring(p.p_id,sizeof(p.p_id))) exit(1);
if(getsafestring(p.cel,sizeof(p.cel))) exit(1);

How to properly fetch data from tab separated fields in text file

I am trying to learn how to import data from tab separated fields in a text file. Here it is an example of what I am trying to fetch from an external file called users.in:
1 joshmith mypwd John Smith Awesome Road 103
2 jane_doe strongpwd Jane Doe Lucky Street 201
3 august84 goodpwd August May Red Boulevard 24
here it is the structure that is supposed to keep the data...
typedef struct User
{
int id;
char username[20];
char password[40];
char firstname[20];
char lastname[20];
char address[120];
} User;
... and of course the code that should handle the operation:
User *u = (User *)malloc(sizeof(User)*4);
int i = 0;
while (6 == fscanf(data_file, "%d\t%[^\t]\t%[^\t]\t%[^\t]\t%[^\t]\t%[^\t]\n", &(u+i)->id, (u+i)->username, (u+i)->password, (u+i)->firstname, (u+i)->lastname, (u+i)->address))
{
fprintf(stdout, "%d %s %s %s %s %s\n", (u+i)->id, (u+i)->username, (u+i)->password, (u+i)->firstname, (u+i)->lastname, (u+i)->address);
i++;
}
the loop manages to go through the first iteration... and then it stops. Here it is the output:
1 joshmith mypwd John Smith Awesome Road 103
2
can anyone help me figure out why is this happening? What is the proper way to import such formatted data?
I would use fgets to read each line into a string and then use strtok with \t as a delimiter character to extract the tokens; the first token in each line can be converted to a number using atoi.
NOTE: using atoi() means that an invalid number will be returned as a zaero value, so you cannot distinguish between these without extra logic
The problem with your format string is that the last scanset you're using is %[^\t] while most likely ends with a \n, although of course it could possibly be that it ends with a \t. If it is certain that it ends with a \n, then simply changing that last one should suffice:
"%d\t%[^\t]\t%[^\t]\t%[^\t]\t%[^\t]\t%[^\n]\n"
// changed this ^ from t to n
If it may also be a \t, then you may use the following:
"%d\t%[^\t]\t%[^\t]\t%[^\t]\t%[^\t]\t%[^\n\t]%*[\n\t]"
// %[^\n\t] discards and assigns whatever found until a '\t' or '\n' is encountered
// %*[\n\t] discards and only discards '\n's and '\t's
// ... until something else is encountered
As an additional information, a space ' ' inside a format string matches to zero or more of any whitespace character and discards them. It essentially is like %*[ \t\n] telling the -scanf to: match any (if any) ' ', '\t' and '\n' until you encounter something else and discard them.

Reading values from CSV file into variables

I am trying to write a simple piece of code to read values from a CSV file with a max of 100 entries into an array of structs.
Example of a line of the CSV file:
1,Mr,James,Quigley,Director,200000,0
I use the following code to read in the values, but when I print out the values they are incorrect
for(i = 0; i < 3; i++) /*just assuming number of entries here to demonstrate problem*/
{
fscanf(f, "%d,%s,%s,%s,%s,%d,%d", &inArray[i].ID, inArray[i].salutation, inArray[i].firstName, inArray[i].surName, inArray[i].position, &inArray[i].sal, &inArray[i].deleted);
}
Then when I print out the first name, the values are all assigned to the first name:
for(j = 0; j < 3; j++) /* test by printing values*/
{
printf("Employee name is %s\n", inArray[j].firstName);
}
Gives ames,Quigley,Director,200000,0 and so on in that way. I am sure it's how i format the fscanf line but I can't get it to work.
Here is my struct I'm reading into:
typedef struct Employee
{
int ID;
char salutation[4];
char firstName[21];
char surName[31];
char position[16];
int sal;
int deleted;
} Employee;
This is because a string %s can contain the comma, so it gets scanned into the first string. There's no "look-ahead" in the scanf() formatting specifier, the fact that the %s is followed by a comma in the format specification string means nothing.
Use character groups (search the manual for [).
const int got = fscanf(f, "%d,%[^,],%[^,],%[^,],%[^,],%d,%d", &inArray[i].ID,
inArray[i].salutation, inArray[i].firstName,
inArray[i].surName, inArray[i].position, &inArray[i].sal,
&inArray[i].deleted);
And learn to check the return value, since I/O calls can fail! Don't depend on the data being valid unless got is 7.
To make your program read the entire file (multiple records, i.e. lines), I would recommend loading entire lines into a (large) fixed-size buffer with fgets(), then using sscanf() on that buffer to parse out the column values. That is much easier and will ensure that you really do scan separate lines, calling fscanf() in a loop will not, since to fscanf() a linefeed is just whitespace.
Might as well post my comment as an answer:
%s reads a full word by default.
It finds the %d, the integer part, then the ,, and then it has to read a string. , is considered valid in a word (it is not a whitespace), so it reads until the end of the line (there is no whitespace until then), not until the first comma... And the rest remains empty. (From this answer)
You have to change the separator with specifying a regex:
fscanf(f, "%d,%[^,],%[^,],%[^,],%[^,],%d,%d", &inArray[i].ID, inArray[i].salutation, inArray[i].firstName, inArray[i].surName, inArray[i].position, &inArray[i].sal, &inArray[i].deleted);
Instead of %s, use %[^,], which means "grab all chars, and stop when found a ,".
EDIT
%[^,]s is bad, it would need a literal s after the end of the scanset... Thanks #MichaelPotter
(From Changing the scanf() delimiter and Reading values from CSV file into variables )

reading multiple variable types from single line in file C

Alright I've been at this all day and can't for the life of me get this down, maybe you chaps can help. I have a file that reads as follows
1301,105515018,"Boatswain","Michael R.",ABC, 123,="R01"
1301,103993269,"Castille","Michael Jr",ABC, 123,="R03"
1301,103993267,"Castille","Janice",ABC, 123,="R03"
1301,104727546,"Bonczek","Claude",ABC, 123,="R01"
1301,104731479,"Cruz","Akeem Mike",ABC, 123,="R01"
1301,105415888,"Digiacomo","Stephen",ABC, 123,="R02"
1301,106034479,"Annitto Grassis","Susan",ABC, 123,="R04"
1301,106034459,"Als","Christian",ABC, 123,="R01"
And here is my code...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_NAME 15
#define MAX_SUBSEC 3
#define N 128
//void printArr(struct *students);
struct student{
int term;
int id;
char lastname[MAX_NAME];
char firstname[MAX_NAME];
char subjectname[MAX_SUBSEC];
int catalog;
char section[MAX_SUBSEC];
}students[10];
int main(){
int term;
int id;
char lastname[MAX_NAME];
char firstname[MAX_NAME];
char sub[MAX_SUBSEC];
int cat;
char sec[MAX_SUBSEC];
char fname[N];
FILE *inputf;
printf("Enter the name of the text file: ");
scanf("%123s",fname);
strcat(fname,".txt");
inputf = fopen(fname,"r");
if (inputf == NULL){
printf("I couldn't open the file for reading.\n");
exit(0);
}
//TROUBLE HERE!
fscanf(inputf, "%d,%d,%[^,]s", &students[0].term, &students[0].id,students[0].lastname);
printf("%d\n", students[0].term);
printf("%d\n", students[0].id);
printf("%s\n", students[0].lastname);
/*for (j = 1 ; j <= 10-1 ; j++){
for(k = 0 ; k <= 10-2 ; k++){
if(students[k] > students[k+1]){
temp = students[k];
students[k] = students[k+1];
students[k+1] = temp;
}
}
}*/
fclose(inputf);
system("pause");
return 0;
}
void printArr(int a[], int tally){
int i;
for(i = 0 ; i < tally ; i++){
printf("%d ", a[i]);
}
printf("\n");
}
My objective is to take each one of those values in the text file and input it to where it belongs in the struct and subsequently the struct array, but I can't get passed the first 2 ints.
Getting the lastname string, because it is a max of 15 characters, it spills over into the first name string right after it and takes what remaining characters it needs in order to fill up the lastname char array. Obviously I do not want this. As you can see I have tried strtok but it doesnt do anything, not sure what I have to do though as I have never used it before. Also have tried just including all the variables into fscanf statement, but I either get the same output, or it becomes a mess. As it is, I am extremely lost, how do I get these values into the variables they belong?!
EDIT: updated my code, I have gotten a little farther but not much. I can now print out just the last name but can not more farther from there, I cant get to the firstname string or any of the variables beyond it.
What you have there is a CSV file with quoted strings, and so I would recommend you use a CSV parser (or roll your own) rather than trying to do it all with scanf (since scanf cannot deal with quotes, e.g. commas within quoted strings). A quick Google search turns up libcsv.c which you may be able to use in your project.
With the fscanf format string "%d,%d,\"%[^\"]\",\"%[^\"]\",%[^,],%d,=\"%[^\"]\"" we can read a whole line's data. Besides, you have to define
char lastname[MAX_NAME+1];
char firstname[MAX_NAME+1];
char subjectname[MAX_SUBSEC+1];
int catalog;
char section[MAX_SUBSEC+1];
— the +1 to account for the terminating '\0' character.
I have a question for you... If you want to know how to use a diamond cutter, do you try it and see, or do you consult the manual? The problem here isn't the result of your choice, but your choice itself. Believe it or not, I have answered these questions so often that I'm tired of repeating myself. The answer is all in the manual.
Read the POSIX 2004 scanf manual — or the POSIX 2008/2013 version — and the answer this question and you'll have some idea of what you're not doing that you should be. Even fscanf code should use assert as a debugging aid to ensure the number of items read was correct.
%[^,]s It seems as though there's a mistake here. Perhaps you meant %[^,]. The %[ format specifier is a different format specifier to the %s format specifier, hence in the presumably mistaken code there are two directives: %[^,] and s. The s directive tells scanf to read an 's' and discard it.
1.There is a syntax error in
while(result != NULL){
printf(".....);
......
}
}//error
fscanf(inputf, "%s", lastname); can't read a line ,fscanf will stop when it comes across an space
fscanf reads one line at a time, and you can easily capture the contents of each line because your file is formatted pretty nicely, especially due to the comma separation (really useful if none of your separated values contain a comma).
You can pass fscanf a format like you're doing with "%d" to capture an int, "%s" to capture a string (ends at white space, be weary of this when for example trying to find a name like "Annitto Grassis, which would require 2 %s's), etc, from the currently read line of the file. You can be more advanced and use regex patterns to define the contents you want captured as chars, such as "Boatswain", a sequence comprised chars from the sets {A-Z}, {a-z}, and the {"}. You'll want to scan the file until you reach the end (signified by EOF in C) so you can do such and capture the contents of the line and appropriately assign the values to variables like so:
while( fscanf(inputf, "%d,%d,%[\"A-Za-z ],%[\"A-Za-z .]", &term, &id, lastname, firstname) != EOF) {
.... //do something with term, id, lastname, firstname - put them in a student struct
}
For more about regex, Mastering Regex by Jeff Friedl is a good book for learning about the topic.

Resources