How do I read properly from a binary file? - file

I already cut my code to minimum and I think that is everything I need for my problem.
I want the user to be able to read this structure:
typedef struct {
char movieTitle[30];
unsigned int yearPublished;
char director[50];
char genre[50];
float profit;
} movie_t;
from a binary file that has already 4 movies in it, but should also be able to store max 100 movie structures like defined here:
//global var.
// ================================================================
#define MAX_STORAGE 100
movie_t movieData[MAX_STORAGE];
movie_t movieRecord;
unsigned int movieCount = 0;
With my code that I already wrote below, I can't somehow read all movies out of the file, because my "count checker" tells me every-time that just one movie was read. Also just one movie is showing in my console output code below my "readFile" function.
// main functions --- read from file
// ================================================================
int
readFile(const char* fileName)
{
//open file
FILE* read = fopen(fileName, "rb");
if (!read)
{
printf("ERROR: Could not open the file!\n");
return -1;
}
else
{
printf("%s were opened successfully.\n\n", fileName);
}
//read from file
int count = 0;
while (!feof(read))
{
size_t count = fread(&movieRecord, sizeof(movieRecord), 1, read);
if (count == 0)
{
printf("ERROR: Read process was unsuccessful or incomplete!\n");
}
if (count == 1)
{
printf("Successfully read %i movie.\n", count);
}
else
{
printf("Successfully read %i movies.\n", count);
}
return count;
}
fclose(read);
return 0;
}
This is the console output where ALL movies from the file should be showing up. Also they should be showing with a number (Num.) from 1 - 100.
// main functions --- console Output
// ================================================================
void
consoleOutput(movie_t movieData2, unsigned index)
{
printf("MOVIE DATA Num. %u\n\n", index);
printf("Movie title : %s\n", movieData2.movieTitle);
printf("Publishing year : %u\n", movieData2.yearPublished);
printf("Director : %s\n", movieData2.director);
printf("Genre : %s\n", movieData2.genre);
printf("Profit : %.2f\n", movieData2.profit);
}
This is how my main function looks like if you would like to see it:
// MAIN
// ================================================================
int main(void)
{
// defaultname for file
char fileName[50] = "movies.dat";
// variable for loop
int stopLoop = 0;
// loop
do {
// Output menu
printf("\nMENU:\n");
printf("(a) Show all movies\n");
printf("(o) Read moviefile\n");
printf("(q) Exit programm\n");
printf("\nYour choice: ");
// User input
char ch = _getch();
printf("%c\n\n", ch);
// Switch case for user input
switch (ch)
{
case 'q': // Exit programm
stopLoop = 1;
break;
case 'a': // Show all movies
consoleOutput(movieRecord, movieCount = readFile(fileName));
break;
case 'o': // Read moviefile
readFile(fileName);
break;
default:
printf("==> Invalid input!\n");
}
} while (!stopLoop);
return 0;
}
Side-note: because the user is able to input new movies, they should be able to save that data to the same file from where they read it. But that's something I want to test myself first, before I ask around for help.

You are returning from your loop body after reading only one entry. If you really wish to read all the entries in the file, you'll need to change your reading loop to reading into your array, which you can in fact get rid of all together thanks to how fread works:
// main functions --- read from file
// ================================================================
int
readFile(const char* fileName)
{
//open file
FILE* read = fopen(fileName, "rb");
if (!read)
{
printf("ERROR: Could not open the file!\n");
return 0; // note that since you'll probably assign the output to movieCount, it might be better to return 0 on failure
}
else
{
printf("%s were opened successfully.\n\n", fileName);
}
// Here's where the problem, you only ever read out one movie entry into movieRecord and returned
// You can use fread to read out the entire file into the array instead
// It would also be better not to use a global variable for this, instead taking in a pointer to an movie_t array to readFile instead but I left it using the global as per the original program
size_t count = fread(movieData, sizeof(movie_t), MAX_STORAGE, read);
if (ferror(read))
{
printf("ERROR: Read process was unsuccessful or incomplete!\n");
fclose(read);
return 0;
}
if (count == 1)
{
printf("Successfully read %i movie.\n", count);
}
else
{
printf("Successfully read %i movies.\n", count);
}
fclose(read);
return count;
}
From here, you'll have movieData populated with the data from your file and should adjust the rest of your program accordingly.

Related

Write struct into file in c

I am trying to write struct to file. I have done the part where I defined struct I read text from file and then I saved it in struct now I am on a point where I want to make change in struct: students[i].name and then I want the change to be reflected (write) in file but it has invalid encoding.
file.txt has this structure:
U,Virat Kohli,Virat Kohli,
U,Serena Williams,Virat Kohli,
G,Wayne Gretzky,Virat Kohli,
U,Virat Kohli,Virat Kohli,
U,Serena Williams,Virat Kohli,
G,Wayne Gretzky,Virat Kohli,
Here is my code:
#include <stdio.h>
#include <string.h>
typedef struct
{
// members for the student's type, name, surname
char type;
char name[50];
char surname[50];
} Student;
int main(void)
{
// for comparing strcmp
int result;
// file pointer variable for accessing the file
FILE *file;
// attempt to open file.txt in read mode to read the file contents
file = fopen("file.txt", "r");
// if the file failed to open, exit with an error message and status
if (file == NULL)
{
printf("Error opening file.\n");
return 1;
}
// array of structs for storing the Student data from the file
Student students[100];
// read will be used to ensure each line/record is read correctly
int read = 0;
// records will keep track of the number of Student records read from the file
int records = 0;
// read all records from the file and store them into the students array
while (fscanf(file, " %c , %49[^,], %49[^,],", &students[records].type, students[records].name, students[records].surname) == 3)
{
// if fscanf read 3 values from the file then we've successfully read
records++;
// if there was an error reading from the file exit with an error message
// and status
if (ferror(file))
{
printf("Error reading file.\n");
return 1;
}
}
// close the file as we are done working with it
fclose(file);
// print out the number of records read
printf("\n%d records read.\n\n", records);
// print out each of the records that was read
for (int i = 0; i < records; i++)
printf("%c %s %s\n",
students[i].type,
students[i].name,
students[i].surname);
printf("\n");
// change first record's name to Elena Heis
for (int i = 0; i < 1; i++)
{
if (students[i].name == students[i].name)
{
printf("%s\n",
students[i].name);
strcpy(students[i].name, "Elena Heis");
printf("%s\n",
students[i].name);
}
}
// write changes to file
file = fopen("file.txt", "wb");
if (file != NULL)
{
fwrite(students, sizeof(Student), 1, file);
fclose(file);
}
return 0;
}
After write file has broken encoding like this
It should be
Your code is clean and your format string is almost perfect, yet parsing the csv file (or any file in general) with fscanf() is not recommended as it is very difficult to recover from errors and newlines are mostly indistinguishable from other white space characters. In particular, the \n at the end of the format string will match any possibly empty sequence of white space characters.
Testing ferror() and feof() as you do seems fine but insufficient to ensure reliable parsing: if fscanf() returns a short code, for example because of an empty field, parsing will continue from the middle of the offending line and neither ferror() nor feof() will cause the loop to end.
You should instead read one line at a time with fgets() and use sscanf() to parse the line.
Also note these remarks:
the csv file does not seem to contain name and surname fields but rather the full names of opponents.
this file seems to have trailing , after the third field. If this is expected, the format string ensuring record validity should be changed to "%c,%49[^,],%49[^,\n]%1[,\n]"
you should check that records < 100 to avoid a buffer overflow.
the test if (students[i].name == students[i].name) is useless and always true. No test is needed to change the name of the first student.
you cannot write the text file with fwrite(students, sizeof(Student), 1, file), you should instead use fprintf as for the output to the terminal.
Here is a modified version:
#include <errno.h>
#include <stdio.h>
#include <string.h>
typedef struct
{
// members for the student's type, name, surname
char type;
char name[50];
char surname[50];
} Student;
int main(void)
{
// file pointer variable for accessing the file
FILE *file;
// attempt to open file.txt in read mode to read the file contents
file = fopen("file.txt", "r");
// if the file failed to open, exit with an error message and status
if (file == NULL)
{
fprintf(stderr, "Error opening file %s for reading: %s\n", "file.txt", strerror(errno));
return 1;
}
// array of structs for storing the Student data from the file
Student students[100];
// length of the students array
int nrecords = sizeof(students) / sizeof(*students);
// buffer to read one line at a time
char buffer[256];
// records will keep track of the number of Student records read from the file
int records = 0;
// read all records from the file and store them into the students array
while (records < nrecords && fgets(buffer, sizeof buffer, file))
{
// Read a line/record from the file
// if it was able to read successfully which we expect read will be 3
char newline[3]; // for characters at end of line: `,\n` or `\n`
char extra; // to ensure no more characters are present
// there are 5 conversion specifiers, but sscanf should parse
// exactly 4 fields, including the trailing `,` and the newline
int read = sscanf(buffer,
"%c,%49[^,],%49[^,\n]%2[,\n]%c",
&students[records].type,
students[records].name,
students[records].surname,
newline, &extra);
// if fscanf read 3 values and a newline from the file then we've successfully read
// in another record
if (read == 4) {
records++;
} else {
fprintf(stderr, "invalid record: %s", buffer);
}
}
// if there was an error reading from the file exit with an error message
// and status
if (ferror(file))
{
fprintf(stderr, "Error reading file: %s\n", strerror(errno));
fclose(file);
return 1;
}
// close the file as we are done working with it
fclose(file);
// print out the number of records read
printf("\n%d records read.\n\n", records);
// print out each of the records that was read
for (int i = 0; i < records; i++)
{
printf("%c %s %s\n",
students[i].type,
students[i].name,
students[i].surname);
}
printf("\n");
// change first record's name to Elena Heis
strcpy(students[0].name, "Elena Heis");
printf("%s\n", students[0].name);
// write changes to file
file = fopen("file.txt", "w");
if (file == NULL)
{
fprintf(stderr, "Error opening file %s for writing: %s\n", "file.txt", strerror(errno));
return 1;
}
// print out each of the records that was read
for (int i = 0; i < records; i++)
{
fprintf(file, "%c,%s,%s,\n",
students[i].type,
students[i].name,
students[i].surname);
}
fclose(file);
return 0;
}

Passing a filepath to fopen from an Array Fails in C

I have a two filepaths input by the user and stored in an array. However, when I try to use one of these filepaths to open a file using fopen the code exits as if the file does not exist. If I hard code the filepath into the fopen function eveything proceeds perfectly.
For Example:
//Libraries
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
//Global Functions
//Main Function
int main()
{
//Local Variables
char * user_input_full = NULL;
char user_input = 'a';
size_t len = 0; //Pointer for user_input_full
int size = 0; //Length of the input_use_full array
char *input_array[2]; //This hold the filepaths
int i=0; //A loop counter
//Carve out an initial array for the use input string (it is of unknown length)
user_input_full = calloc(16,sizeof(char));
size = 16;
//Take user input until a newline is encountered.
while(user_input != '\n')
{
scanf("%c", &user_input);
user_input_full[len] = user_input;
len = len+1;
if(len==size)
{
#ifdef DEBUG_MODE
printf("The input stream is being reallocated\n");
#endif
user_input_full = realloc(user_input_full,sizeof(char)*(size+16));
if(user_input_full == NULL)
{
//realloc failed, return a fail status
printf("Realloc of the input stream failed.\n");
return EXIT_FAILURE;
}
}
}
user_input_full[len] = '\0';
#ifdef DEBUG_MODE
printf("The user input was: %s \n", user_input_full);
#endif
//Parse out the file streams
input_array[i] = strtok(user_input_full," ");
while(input_array[i]!=NULL)
{
input_array[++i] = strtok(NULL," ");
}
#ifdef DEBUG_MODE
printf("Input array Values:\n");
for (i=0;i<2;i++)
{
printf("%s \n",input_array[i]);
}
#endif
//open the file specified in input_array[0]
if(fopen(input_array[0],"r") == NULL)
{
printf("Open of the input file failed.\n");
printf("Tried to open: %s ", input_array[0]);
return EXIT_FAILURE;
}
else
{
printf("The file opened.\n");
}
return EXIT_SUCCESS;
}
Would print the error block. However:
//Open the file specified in array[0]
if(fopen("test.txt","r") == NULL)
{
printf("Failed to open the input file.\n");
return EXIT_FAILURE;
}
else
{
printf("The file opened.\n");
}
Works perfectly fine.
I also tried passing the value in input_array[0] to a const char*, but that did not work either. I feel like I am missing some fundamental concept here.
EDIT: Clarified one of the comments in the code block
The real reason it is failing is due to trailing newline in the filename. All you need is to add newline as string token.
Change this line:
input_array[i] = strtok(user_input_full," ");
to:
input_array[i] = strtok(user_input_full," \n");
and this line:
input_array[++i] = strtok(NULL," ");
to:
input_array[++i] = strtok(NULL," \n");
This will remove the trailing newlines.
First mistake I note is in the following code block. it's inputing 3 three strings to input_array and going out of bound.
//Parse out the file streams
input_array[i] = strtok(user_input_full," ");
while(input_array[i]!=NULL)
{
input_array[++i] = strtok(NULL," ");
}
Change this code block to something as shown here.
input_array[i] = strtok(user_input_full," ");
for(i=1;i<2;i++)
{
input_array[i] = strtok(NULL," ");
}

Is it possible to copy a searched string from one file and print into another?

The following function is only used to display certain areas of a file. In this case I am trying to copy every line that has the word "EMPTY" from the main file to another file. Other than by opening a file to write to I use for char arrays to look for the proper string needed. At this point, I try to use the data from that file(In which the string was searched for) and try to copy it into another file. At this point some of the new file prints to the screen which is the first part at the top under where I created the file but when I try to add the string from the other file they do no print. Can you print certain string fro one file to another?
void emptySeats(void)
{
int position = 0;
int AvalabileOne = 0;
int countGone = 0;
int confirm = 0;
char testOne[] = "EMPTY";
char StrOne[10], StrTwo[10], StrThree[10], StrFour[10];
FILE *fEmptyseatArrangement;
fEmptyseatArrangement = fopen("airlineEmptySeatingArrangment.txt", "w+");
fprintf(fseatArrangement, "\t\t\tEcono-Airlines Passangers System\n\n\t Seat Number\t Seatavalability Last Name First Name\n");
do
{
fseatArrangement = fopen("airlineSeatingArrangment.txt", "r");
fseek(fseatArrangement, 102, SEEK_SET);
while (AvalabileOne < FULL)
{
fscanf(fseatArrangement, "%s%s%s%s\n", &StrOne, &StrTwo, &StrThree, &StrFour);
if (strcmp(StrThree, testOne))
{
fprintf(fEmptyseatArrangement, "\t\t%s\t\t%s\t\t%s\t\t%s\n", StrOne, StrTwo, StrThree, StrFour);
countGone = countGone + 1;
}
AvalabileOne = AvalabileOne + 1;
}
fclose(fEmptyseatArrangement);
fclose(fseatArrangement);
fEmptyseatArrangement = fopen("airlineEmptySeatingArrangment.txt", "r");
while (fgets(econoAirEmptySeatArrangement, 1000, fEmptyseatArrangement) != NULL)
printf("%s", econoAirEmptySeatArrangement);
fclose(fEmptyseatArrangement);
printf("There are %d seats vacant at the moment\n", FULL - countGone);
printf("Enter Zero(0) Key to return to menu at anytime.");
scanf(" %d", &position);
if (position == 0)
{
system("cls");
menu();
}
else
{
system("cls");
printf("INVALID INPUT! Please try again.\n");
}
} while (AvalabileOne != 0);
system("pause");
return;
}

Printing all variants of a word

So here is the the program which i am trying to make:
Create password generator, which uses dictionary and match files to generate passwords by replacing letters in the dictionary words with their matches.
Example:
Dictionary file:
apple
loop
Match file:
a 4
e 3
o 0
Output:
4pple
appl3
4ppl3
l0op
lo0p
l00p
And this is my solution:
#include <stdio.h>
#include <string.h>
/* function prototypes */
int read_source(char* ,char* ,char* );
char* shift_string(char* ,char* );
int write_shifted(char* ,char* );
int main(int argc, char *argv[])
{
/* argv[1]: the source file with the original strings
* argv[2]: the shift file which indicates which characters are to be
* replaced with what
* argv[3]: the file in which the new strings are to be stored*/
if(!argv[1])
{
printf("No source file, can do nothing. Exiting.\n");
return 1;
}
else if (!argv[2])
{
printf("No shift file given, can do nothing. Exiting.\n");
return 2;
}
else if (!argv[3])
{
/* If no output file has been specified, print the
* output directly to the console. */
read_source(argv[1],argv[2],NULL);
return 0;
}
if (read_source(argv[1],argv[2],argv[3])!=0)
{
printf("There has been an error. Exiting.\n");
return 3;
}
if (argv[3])
{
printf("Everything seems to have gone according to plan.\n");
printf("Your output has been stored in \"%s\"\n",argv[3]);
}
return 0;
}
int read_source(char* source_file,char* shift_file,char* out_file)
{
FILE *file_pointer;
file_pointer=fopen(source_file, "r");
/* Exit gracefully if source_file cannot be found. */
if (file_pointer == NULL)
{
printf("Couldn't open \"%s\" for reading.\n",source_file);
return 1;
}
char* new_line;
/* maximum line length, can be changed if needed */
char line[256];
/* Go through the source file. */
while (fgets(line,sizeof line,file_pointer) != NULL)
{
new_line = shift_string(shift_file,line);
if (new_line==NULL)
{
printf("There has been an error with replacing the characters.\n");
return 2;
}
write_shifted(new_line,out_file);
}
int fclose(FILE *file_pointer);
return 0;
}
char* shift_string(char* shift_file,char* source_string)
{
/* This function replaces certain characters in a given source_string as
* specified by shift_file */
/* Open shift file. */
FILE *file_pointer;
char i,j;
file_pointer=fopen(shift_file, "r");
/* Exit gracefully if shift_file cannot be found. */
if (file_pointer == NULL)
{
printf("Couldn't open \"%s\" for reading.\n",shift_file);
return NULL;
}
int k;
/* Determine how long the source_string is for the loop below. */
int length = strlen(source_string);
while (fscanf(file_pointer, "%c %c\n", &i, &j)==2)
{
/* This loop actually does the replacing. */
for (k=0;k<length;k++)
{
if (source_string[k]==i)
{
source_string[k]=j;
}
}
}
int fclose(FILE *file_pointer);
return source_string;
}
int write_shifted(char* new_line,char* out_file)
{
/* This function writes the new strings to out_file
* If no out_file has been given, it will write the
* output to the console.*/
if (out_file==NULL)
{
printf("%s",new_line);
return 0;
}
FILE *file_pointer;
/* Open in a+ mode. If out_file does not yet exist, it will be created.
* If it does exist, it will be appended to instead of overwritten. */
file_pointer = fopen(out_file,"a+");
/* Write the new line */
fprintf(file_pointer,"%s",new_line);
fclose(file_pointer);
return 0;
}
So the problem is that now it prints only the final states (4ppl3 l00p) but i also need the middle states (4pple appl3 l0op lo0p). Can someone give me some clues how to make it. thanks in advance. :)
recursion works well for this type of problem. Basically, take whether or not to have the first letter substituted, and recurse with the rest of the word for each case - substituted and not substituted. Do this until you don't have any letters left, and you'll have all your combinations.
// terribly pseudo-codey, but should give the idea
string='abc';
stringfunction("", string);
// simplified, just shifts each letter by one
stringfunction(processed, unprocessed)
if unprocessed is empty
print processed
return
stringfunction(strcat(processed, unprocessed[0]), unprocessed[1]);
stringcunction(strcat(processed, unprocessed[0]+1), unprocessed[1]);
this will print out abc, abd, acc, acd, bbc, bbd, bcc, bcd
of course, for your case, instead of shifting the letter, you'd either do or not do the substitution.

Reading a file into a C program

The program I have reads the file, displays it accordingly, but then when trying to view the contents again, they are gone.
Structure:
typedef struct friends_contact {
char *First_Name;
char *Last_Name;
char *home;
char *cell;
} fr;
Main:
int main() {
fr friends[5];
char buffer[BUFFSIZE];
int counter = 0;
int i = 0;
menu(friends, &counter, i, buffer);
getch();
return 0;
}
If statement in the menu function used to open up the file:
if (user_entry1 == 1) {
printf("Please enter a file name");
scanf("%s", user_entry3);
read = fopen(user_entry3, "r");
}
If statement that allows the user to choose to look at the contacts of the address book.
if (user_entry == 4 ) {
print_contact(friends, counter, i, user_entry3);
if (user_entry1 == 1) {
file2(friends, counter, i, buffer, read);
}
}
Read function:
char file2(fr *friends, int *counter, int i, char buffer[], FILE *read) {
while (fscanf(read, "%s", buffer) != EOF) {
friends[i].First_Name = malloc(BUFFSIZE * strlen(buffer));
strcpy(friends[i].First_Name, buffer);
return printf("\n""%s ", friends[i].First_Name);
}
fclose(read);
}
If you need me to provide any more code. I'm trying to get it to read the content from the file into the contacts list so I can view, search, or delete them.
P.S. I understand some of the other code may not be perfect, but right now I'm just concerned about figuring this out :).
The file2 function reads each line of the file into friends[i] but never increments i. So each new record would overwrite the previous one ... except that the function returns during the first iteration of the loop. Better to use if than while in this case.
Other suggestions:
Instead of using }else; you can just close the if block with }. An if needn't be followed by else.
Instead of the idiom
if (foo == 1) {
// do stuff
} else if (foo == 2) {
// do other stuff
} else {
// do default stuff
}
you can use
switch (foo) {
case 1:
// do stuff
break;
case 2:
// do other stuff
break;
default:
// do default stuff
}
Instead of
printf("\n""%s ",friends[i].First_Name);
it's more common to use a single string, with the newline at the end:
printf("%s\n", friends[i].First_Name);
You have to reset your file pointer using fseek to the beginning of the file again. Your read function should start like:
fseek(read, 0, SEEK_SET); // set file pointer to the beginning of the file
You shouldn't forget to test if the file could be opened or not:
if ( user_entry1==1 ){
printf( "Please enter a file name" );
scanf( "%s", user_entry3 );
if ( ( read = fopen( user_entry3, "r" ) ) != NULL ){
printf( "The file couldn't be opened" );
}
else{
.
.
.
}
}

Resources