Program crashes when trying to scan a string - c

I'm having trouble scanning data from a .dat file containing arbitrary game results (for testing the program)
The results are formatted as follows: (# representing the score as an integer)
team_a # team_b #
team_a # team_b #
team_a # team_b #
.
.
.
team_a # team_b #
Each row is a different game.
What I'm trying to do with my code at the moment is to use the fgets() function to scan each game/row, then use the sscanf_s() function to take the data from each row (seeing as though i know the way it is formatted) and store it into the data structure i've defined.
I'm more than happy to take any advice on changes i should make to the way i'm going about getting the data into the struct if there is an easier, faster and/more reliable (foolproof) way to do it.
Any help is greatly appreciated.

Microsoft's secure sscanf_s has a slightly different way of interpreting format specifiers and arguments: In order to prevent buffer overflows, each string format (%s, %c and %[) must pass the corresponding buffer size after the buffer.
So your scan command should read:
sscanf_s(data_file_line[i],
"%s %d %s %d",
game_results[i].first_team_name,
sizeof(game_results[i].first_team_name),
&game_results[i].first_team_score,
game_results[i].second_team_name,
sizeof(game_results[i].second_team_name),
&game_results[i].second_team_score);
There are some other issues with your code:
You should check the return value of sscanf_s so you know that the line has been parsed successfully. The return value is the number of items converted, so in your case it should be 4. Also note that %s scans words and team names like "Man Utd" and "Stoke City" have two words and will not parse correctly.
As others have noted, the feof construct will make you read the file once too many. Forget about foef and use the return values of the reading functions instead. For eample, fgets returns NULL when the end of the file is reached, so you can use that as loop condition: while (fgets(buf, sizeof(buf), f)) ...
You don't check whether i overflows. If you have a long file, i might not be big enough.
If you are parsing and storing the lines right away, there's no need to have an array of lines; just use one line buffer over and over.

I simplified a little and it works fine. Your work is not finished. Please try this,
#include <usual.h>
#define MAX_NAME_CHARS 15
#define MAX_DATA_FILE_LINE_LENGTH 32
#define MAX_GAME_RESULTS 128
int main( void )
{
FILE *inp2b;
typedef struct game_results
{
char first_team_name[MAX_NAME_CHARS];
int first_team_score;
char second_team_name[MAX_NAME_CHARS];
int second_team_score;
} game_results_t;
game_results_t game_results[MAX_GAME_RESULTS];
char data_file_line[MAX_DATA_FILE_LINE_LENGTH][MAX_DATA_FILE_LINE_LENGTH];
int errorcode = 0;
int i = 0;
//errorcode = fopen_s(&inp2b,"C:\\Users\\Cody\\Documents\\Visual Studio 2012\\DATAFILES FOR PA2\\input2b.dat","r");
inp2b = fopen( "C:\\testdat\\input2b.dat", "r" );
if ( inp2b == NULL )
errorcode = 1;
if ( errorcode != 0 )
{
printf( "Error opening 2nd data file!\n\n" );
return ( 0 );
}
else
{
printf( "\n\n\nFile was opened successfully!\n\n" );
}
i = 0;
while ( !feof( inp2b ) )
{
fgets( data_file_line[i], MAX_DATA_FILE_LINE_LENGTH, inp2b );
puts( data_file_line[i] );
printf( "\n" );
// sscanf_s(data_file_line[i],"%s %d %s %d",game_results[i].first_team_name,&game_results[i].first_team_score,game_results[i].second_team_name,&game_results[i].second_team_score);
sscanf( data_file_line[i], "%s %d %s %d", game_results[i].first_team_name,
&game_results[i].first_team_score,
game_results[i].second_team_name,
&game_results[i].second_team_score );
printf( "\n\n %s %d %s %d \n\n", game_results[i].first_team_name,
game_results[i].first_team_score,
game_results[i].second_team_name,
game_results[i].second_team_score );
i++;
}
fclose( inp2b );
return ( 0 );
}

Related

How do I read a text file and store it in an array in C programming (values seperated by a comma)?

I need help with getting datapoints, x and y values from a txt file into two arrays.
Currently, the text file consists of 5 lines like:
0.116
0.118
0.12
0.122
0.124
This is my code:
#include <stdio.h>
#include <stdlib.h>
main(void)
{
FILE *inp; /* pointer to input file */
double item;
int cnt=0,y,d,i;
double array[300],swap;
/* Prepare files for input */
inp = fopen("testdoc.txt", "r");
/* Read each item */
while ( (fscanf(inp, "%lf", &item) == 1) && (!feof(inp)) ) {
array[cnt] = item;
cnt++;
}
for (int i = 0; i < cnt; i++)
{
printf("%lf\n",array[i]);
}
printf("The total number of inputs is %d",cnt);
fclose(inp); /* Close the files */
return (0);
}
This only reads the first half of the file, which are the x values. Of which output is
0.116000
0.118000
0.120000
0.122000
The total number of inputs is 4
However, I want to read a text file and store the values in two different arrays for x and y values.
The new text file will look like this
0.116,-0.84009
0.118,4.862
0.12,-1.0977
0.122,0.22946
0.124,3.3173
How do i go changing my code above to recognize the Y values after "," sign? And to add both into two arrays at once?
I tried compiling your code posted on pastebin and received an error because of a missing bracket in your while statement.
That's an easy fix.
The larger issue is in the condition of the while loop.
fscanf returns the number of input items converted and assigned on each call.
When you modified your code to return two values, the condition in the while loop fscanf(inp, "%lf,%lf", &v1,&v2) == 1 would fail and the loop will not be entered.
Please modify the while statement to (have included the missing "(" too)..
while ( (fscanf(inp, "%lf, %lf", &v1, &v2) == 2) && (!feof(inp)) )
and you should be good to go!!!
In addition it would be a good practice to include the return type of int for the main function.

fscanf not saving the data to struct?

I have an array of structs and they get saved into a file. Currently there are two lines in the file:
a a 1
b b 2
I am trying to read in the file and have the data saved to the struct:
typedef struct book{
char number[11];//10 numbers
char first[21]; //20 char first/last name
char last[21];
} info;
info info1[500]
into num = 0;
pRead = fopen("phone_book.dat", "r");
if ( pRead == NULL ){
printf("\nFile cannot be opened\n");
}
else{
while ( !feof(pRead) ) {
fscanf(pRead, "%s%s%s", info1[num].first, info1[num].last, info1[num].number);
printf{"%s%s%s",info1[num].first, info1[num].last, info1[num].number); //this prints statement works fine
num++;
}
}
//if I add a print statement after all that I get windows directory and junk code.
This makes me think that the items are not being saved into the struct. Any help would be great. Thanks!
EDIT: Okay so it does save it fine but when I pass it to my function it gives me garbage code.
When I call it:
sho(num, book);
My show function:
void sho (int nume, info* info2){
printf("\n\n\nfirst after passed= %s\n\n\n", info2[0].first); //i put 0 to see the first entry
}
I think you meant int num = 0;, instead of into.
printf{... is a syntax error, printf(... instead.
Check the result of fscanf, if it isn't 3 it hasn't read all 3 strings.
Don't use (f)scanf to read strings, at least not without specifying the maximum length:
fscanf(pRead, "%10s%20s%20s", ...);
But, better yet, use fgets instead:
fgets(info1[num].first, sizeof info1[num].first, pRead);
fgets(info1[num].last, sizeof info1[num].last, pRead);
fgets(info1[num].number, sizeof info1[num].number, pRead);
(and check the result of fgets, of course)
Make sure num doesn't go higher than 499, or you'll overflow info:
while(num < 500 && !feof(pRead)){.
1.-For better error handling, recommend using fgets(), using widths in your sscanf(), validating sscanf() results.
2.-OP usage of feof(pRead) is easy to misuse - suggest fgets().
char buffer[sizeof(info)*2];
while ((n < 500) && (fgets(buffer, sizeof buffer, pRead) != NULL)) {
char sentinel; // look for extra trailing non-whitespace.
if (sscanf(buffer, "%20s%20s%10s %c", info1[num].first,
info1[num].last, info1[num].number, &sentinel) != 3) {
// Handle_Error
printf("Error <%s>\n",buffer);
continue;
}
printf("%s %s %s\n", info1[num].first, info1[num].last, info1[num].number);
num++;
}
BTW: using %s does not work well should a space exists within a first name or within a last name.

Can fseek() be used to insert data into the middle of a file? - C

I know that the function fseek() can be used to output data to a specific location in a file. But I was wondering if I use fseek() to move to the middle of the file and then output data. Would the new data overwrite the old data? For example if I had a file containing 123456789 and I used fseek() to output newdata after the 5 would the file contain 12345newdata6789 or would it contain 12345newdata.
Writing data in the "middle" of a file will overwrite existing data. So you would have '12345newdata'.
EDIT: As mentioned in the comments below, it should be noted that this overwrites data without truncating the rest of the file. As an extended version of your example, if you wrote newdata after the 5 in a file containing 1234567890ABCDEFG, you would then have 12345newdataCDEFG, not 12345newdata.
Yes it lets you do that, and those files are called "Random Access Files". Imagine you have already a set file ( with the structure but empty ), in that case you can fill the "slots" you want, or in the case the slot is filled with data you can overwrite on it.
typedef struct{
int number;
char name[ 20 ];
char lastname[ 20 ];
float score;
}students_t;
/* Supposing that you formatted the file already and the file is opened. */
/* Imagine the students are listed each one has a record. */
void modifyScore( FILE * fPtr ){
students_t student = { 0, "", "", 0.0 };
int nrecord;
float nscore;
printf( "Enter the number of the student:" );
scanf( "%d", &record )
printf( "Enter the new Score:" );
scanf( "%f", &nscore ); // this is a seek example so I will not complicate things.
/*Seek the file ( record - 1 ), because the file starts in position 0 but the list starts in 1*/
fseek( fPtr, ( record - 1 ) * sizeof ( students_t ), SEEK_SET );
/* Now you can read and copy the slot */
fread( fPtr, "%d%s%s%f", &student.number, student.name, student.lastname, &student.score );
/* Seek again cause the pointer moved. */
fseek( fPtr, ( record - 1 ) * sizeof ( students_t ), SEEK_SET );
student.score = nscore;
/*Overwrite his information, only the score will be altered. */
fwrite( &student, sizeof( student_t ), 1, fPtr );
}
This is how it works (picture obtained from Deitel-How to program in C 6th Edition):
You probably know this but fseek() merely moves the associated position indicator and doesn't dictate per se whether the proceeding output function will overwrite or insert.
You're probably using fwrite() or some other plain vanilla output function, and these will overwrite, giving you "12345newdata" instead of the inserted variant.
On the other hand, you could roll your own inserting function (I don't think there's a stock stdio.h function for this), and call this after fseek() to get your desired insertion.
Something like this could suffice:
insert(const void *ptr, size_t len, FILE *fp) {
char tmp[len];
size_t tmplen;
while (len) {
// save before overwriting
tmplen = fread(tmp, 1, sizeof(tmp), fp);
fseek(fp, -tmplen, SEEK_CUR);
// overwrite
fwrite(ptr, len, 1, fp);
// reloop to output saved data
ptr = tmp;
len = tmplen;
}
}
(Error handling on fread() and fwrite() left out for verbosity.)

Using a function to read in a file

I have the code below which compiles fine in xcode, but when I take it across to Microsoft Visual studio I get a bunch of errors.
void openfile(int mapArray[MAX_HEIGHT][MAX_WIDTH], int *interest, int *dimension1, int *dimension2)
{
int counter = 0;
char buffer;
int rowss, colss;
*interest = 0;
FILE *f;
f = fopen(FILENAME, "r");
if (f==NULL) {
printf("Map file could not be opened");
return 0;
}
// create char array the dimensions of the map
fscanf(f, "%d %d" , dimension1, dimension2 );
// printf("%d %d\n" , dimensions[0], dimensions[1]);
// Reads the spaces at the end of the line till the map starts
buffer=fgetc(f);
while (buffer!='*') {
buffer=fgetc(f);
}
// Read the txt file and print it out while storing it in a char array
while (buffer!=EOF) {
mapArray[rowss][colss]=buffer;
colss++;
// Count up the points of interest
if (((buffer>64)&&(buffer<90))||(buffer=='#') ) {
counter++;
}
// resets column counter to zero after newline
if (buffer=='\n') {
colss=0;
rowss++;
}
buffer=fgetc(f);
}
// Closes the file
fclose(f);
*interest=counter;
}
Which parts are creating all the errors?
I get this list of errors when attempting to compile
Thanks in advance.
I see a few immediate problems. First, you're not initialising rowss or colss before you use them, hence they could contain any value.
Second, fgetc() returns an int so that you can detect end of file. By using a char to hold the return value, you're breaking the contract with the standard library.
Thirdly, you return a 0 if the filename couldn't be opened, despite the fact that the function is specified to return void (ie, nothing).
No doubt those are three of the errors the compiler picked up on, there may be others, and you should probably post the error list with your question for a more exhaustive analysis.

Why does my program read an extra structure?

I'm making a small console-based rpg, to brush up on my programming skills.
I am using structures to store character data. Things like their HP, Strength, perhaps Inventory down the road. One of the key things I need to be able to do is load and save characters. Which means reading and saving structures.
Right now I'm just saving and loading a structure with first name and last name, and attempting to read it properly.
Here is my code for creating a character:
void createCharacter()
{
char namebuf[20];
printf("First Name:");
if (NULL != fgets(namebuf, 20, stdin))
{
char *nlptr = strchr(namebuf, '\n');
if (nlptr) *nlptr = '\0';
}
strcpy(party[nMember].fname,namebuf);
printf("Last Name:");
if (NULL != fgets(namebuf, 20, stdin))
{
char *nlptr = strchr(namebuf, '\n');
if (nlptr) *nlptr = '\0';
}
strcpy(party[nMember].lname,namebuf);
/*Character created, now save */
saveCharacter(party[nMember]);
printf("\n\n");
loadCharacter();
}
And here is the saveCharacter function:
void saveCharacter(character party)
{
FILE *fp;
fp = fopen("data","a");
fwrite(&party,sizeof(party),1,fp);
fclose(fp);
}
and the loadCharacter function
void loadCharacter()
{
FILE *fp;
character tempParty[50];
int loop = 0;
int count = 1;
int read = 2;
fp= fopen("data","r");
while(read != 0)
{
read=fread(&tempParty[loop],sizeof(tempParty[loop]),1,fp);
printf("%d. %s %s\n",count,tempParty[loop].fname,tempParty[loop].lname);
loop++;
count++;
}
fclose(fp);
}
So the expected result of the program is that I input a name and last name such as 'John Doe', and it gets appended to the data file. Then it is read in, maybe something like
1. Jane Doe
2. John Doe
and the program ends.
However, my output seems to add one more blank structure to the end.
1. Jane Doe
2. John Doe
3.
I'd like to know why this is. Keep in mind I'm reading the file until fread returns a 0 to signify it's hit the EOF.
Thanks :)
Change your loop:
while( fread(&tempParty[loop],sizeof(tempParty[loop]),1,fp) )
{
// other stuff
}
Whenever you write file reading code ask yourself this question - "what happens if I read an empty file?"
You have an algorithmic problem in your loop, change it to:
read=fread(&tempParty[loop],sizeof(tempParty[loop]),1,fp);
while(read != 0)
{
//read=fread(&tempParty[loop],sizeof(tempParty[loop]),1,fp);
printf("%d. %s %s\n",count,tempParty[loop].fname,tempParty[loop].lname);
loop++;
count++;
read=fread(&tempParty[loop],sizeof(tempParty[loop]),1,fp);
}
There are ways to ged rid of the double fread but first get it working and make sure you understand the flow.
Here:
read=fread(&tempParty[loop],sizeof(tempParty[loop]),1,fp);
printf("%d. %s %s\n",count,tempParty[loop].fname,tempParty[loop].lname);
You are not checking whether the read was successful (the return value of fread()).
while( 1==fread(&tempParty[loop],sizeof*tempParty,1,fp) )
{
/* do anything */
}
is the correct way.
use fopen("data","rb")
instead of fopen("data","r") which is equivalent to fopen("data","rt")
You've got the answer to your immediate question but it's worth pointing out that blindly writing and reading whole structures is not a good plan.
Structure layouts can and do change depending on the compiler you use, the version of that compiler and even with the exact compiler flags used. Any change here will break your ability to read files saved with a different version.
If you have ambitions of supporting multiple platforms issues like endianness also come into play.
And then there's what happens if you add elements to your structure in later versions ...
For robustness you need to think about defining your file format independently of your code and having your save and load functions handle serialising and de-serialising to and from this format.

Resources