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{
.
.
.
}
}
Related
First Post...
Ive finally upped my game after doing a course in C on Udemy, this is my first application.
Ive created a ToDo list in the command line, with basic functionality, it cannot read the todo list, if you exit the program, the array of strings i created goes back to being empty.
I have made it so a file is saved from the array when the application is exited by pressing 4.
Here is my program.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_STRING_LENGTH 101
#define MAX_TODO 20
int main() {
int input;
int loop;
FILE *f;
char list[MAX_TODO][MAX_STRING_LENGTH] = {};
while(1){
printf("\n==================== ToDo List ====================\n");
printf("Do not leave the app, keep it running!\n");
printf("You will lose all your todos!\n\n");
printf(" - Please enter a number - \n");
printf("1. See todos \n");
printf("2. Add todo \n");
printf("3. Delete todo \n");
printf("4. Leave\n\n");
scanf("%d", &input);
if(input == 4){
f = fopen("list.bin", "w");
fwrite(list, sizeof(char), sizeof(list), f);
fclose(f);
return EXIT_SUCCESS;
}else if (input == 1 || input == 2 || input == 3){
switch(input){
case 1:
for (int loop = 0; loop < MAX_TODO; loop++)
{
printf("[%2d]. %s\n", loop + 1, list[loop]);
}
break;
case 2:
printf("Please enter the number you would like to replace! ");
int j;
scanf("%d", &j);
getchar();
scanf("%[^\n]", list[j - 1]);
break;
case 3:
printf("Please enter the number you would like to remove! ");
int k;
scanf("%d", &k);
strncpy(list[k-1], " ", 101);
printf("[%d] ---> DELETED!!!", k);
break;
}
}else {
printf("Please enter 1, 2, 3, or 4...\n");
}
}
return EXIT_SUCCESS;
}
Any thoughts on how to implement a read process so that my ToDo list works effectively?
Arrays
#define MAX_LINE_LENGTH 101
#define MAX_LINES 20
char lines[MAX_LINES][MAX_LINE_LENGTH];
int num_lines = 0;
In this case, notice that the variables here are global. Your program is a program with express purpose of maintaining a list of TODO lines: it is OK to make that a global object. Every function of your program works on that data.
Next, break the task down — make some useful functions:
Read a line from file
bool read_line( FILE * f )
{
if (num_lines >= MAX_LINES) return false;
if (!fgets( lines[num_lines], MAX_LINE_LENGTH, f )) return false;
char * p = strptok( lines[num_lines], "\r\n" );
if (!p) return false;
*p = '\0';
num_lines += 1;
return true;
}
In particular, we presume input lines are short enough to fit in our list. If some external process has done something to that it isn’t our fault and we can simply fail.
If you wish you can do things like complain to the user and explain what happened, or even skip to the end of the current line (read characters until you get a newline) and then continue reading lines. However you do it, just document it somewhere for your user.
Write a line to file
void write_line( FILE * f, int n )
{
fprintf( f, "%s\n", lines[n] );
}
Read all lines from file
void read_all_lines( const char * filename )
{
FILE * f = fopen( filename, "r" );
if (!f) return;
while (read_line( f ))
;
fclose( f );
}
It could be argued that the array bounds checking should be done here and the read_line() function should be more general. You could do it that way, of course.
Write all lines to file
void write_all_lines( const char * filename )
{
FILE * f = fopen( filename, "w" );
if (!f) return;
for (int n = 0; n < num_lines; n++)
write_line( f, n );
fclose( f );
}
Use it
int main(void)
{
...
// Load the TODO list
read_all_lines( "TODO.txt" );
...
// Save the TODO list
write_all_lines( "TODO.txt" );
...
}
Further Thought
This is not the only way to do it, nor is it the One True Way™. This is convenient because it is a line-by-line method that does not use extra space on disk: the generated file is a standard textual file that can easily be edited by other programs.
You could, as suggested above, just read and write the entire block:
// Load the lines array from file
fread( lines, MAX_LINES, MAX_LINE_LENGTH, f );
// Save the lines array to file
fwrite( lines, MAX_LINES, MAX_LINE_LENGTH, f );
You could load the entire file as a single string then have an array of pointers into the loaded memory block. I won’t provide an example for that.
I am a fan of the “break your problem down into functions” method of learning to do stuff.
I would like to write a program that can continuously + 1 to ID once user registered a user. I use for loop to design it, but I don't know how to break the loop after registered a user. Please help me.
Expected Data in idtest.txt:
SID001:USER1
SID002:USER2
SID003:USER3
.
.
and so on
Actual Data in idtest.txt:
SID001:USER1
SID002:USER1
SID003:USER1
.
until SID00999:USER1
My Code:
int main()
{
FILE* f;
char data[1024], user[50];
int num = 0, c, buf;
printf("Enter User: ");
scanf("%s", user);
f = fopen("idtest.txt", "a+");
while (fgets(data, sizeof data, f));
for (buf = 1; buf <= 999; buf++)
{
c = fgetc(f);
if (c == EOF)
{
fprintf(f, "SID00%d:%s\n", num, user);
}
else if (num >= 1)
{
fprintf(f, "SID00%d:%s\n", num, user);
}
else if (num >= 9)
{
fprintf(f, "SID0%d:%s\n", num, user);
}
else if (num >= 99)
{
fprintf(f, "SID%d:%s\n", num, user);
}
break;
}
return 0;
}
There are multiple problems in your code:
you do not test for errors
as coded, num is always 0: the program cannot produce the file contents you posted.
it makes no sense to try an read the next byte with fgetc(f) since the previous loop reached the end of the file already. This call returns EOF, which explains why you always write 2 leading zeroes.
you cannot mix reading and writing operations without an intervening seek operation, it has undefined behavior. Your code manages to write to the file, but it might behave differently with a different compiler / library / operating system...
you can simplify the output code using %03d to format numbers with leading zeroes.
naming an int variable buf is very confusing.
If you intend for the program to register one user at a time, you should read the file and count the number of lines, then write a single entry at the end.
Here is a modified version:
#include <stdio.h>
int main() {
char user[50];
char data[1024];
FILE *f;
int num;
printf("Enter User: ");
if (scanf("%49s", user) != 1) {
fprintf(stderr, "invalid or missing input\n");
return 1;
}
f = fopen("idtest.txt", "a+");
if (f == NULL) {
fprintf(stderr, "cannot open file idtest.txt\n");
return 1;
}
/* count the number of users */
num = 1;
rewind(f);
while (fgets(data, sizeof data, f)) {
num++;
}
/* switch to writing mode */
// No need to seek because the above loop hit the end of file.
// which is a special case where the next call can be a write operation
//fseek(f, 0L, SEEK_CUR);
fprintf(f, "SID%03d:%s\n", num, user);
fclose(f);
return 0;
}
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.
I think the title sums it up. I am pretty new in C programming and this is my first assignment with files. My input files are as follows:
(The one to check)
The quick brown fox jumps over the lazy dog
(The "list" file)
the The
quick Quick
jumps Jumps
My output file:
|brown|
|fox|
|jumps|
|over|
|the|
|lazy|
|dog|
Problems with the behaviour of my program which I am unsure how to fix: It does check words but only if they are in order, as soon as it runs into a word that is in the list, it just prints the rest of the file.
Do I need to implement a linked list, the contents of which are the words from the list? I do know what it is but I failed to implement it in this program.
Here is what I have so far(without my failed attempt at a linked list):
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int CheckMissing(FILE* dictionary, char dif[30]) {
char miss[30];
while (fscanf(dictionary, "%s", miss) != EOF) {
if (strcmp(dif, miss) == 0) {
return 1;
}
}
return 0;
}
int main()
{
FILE* text;
FILE* dictionary;
FILE* diff;
char path_t[1000];
char path_d[1000];
char path_diff[1000];
char word[30];
printf("Path of file to check: ");
scanf("%s", path_t);
/*FILE TO READ*/
text = fopen(path_t, "r");
if (text == NULL) {
printf("ERROR OPENING TEXT FILE!");
exit(1);
}
printf("Path of file to check from: ");
scanf("%s", path_d);
/*LIST OF WORDS*/
dictionary = fopen(path_d, "r");
if (dictionary == NULL) {
printf("ERROR OPENING DICTIONARY!");
}
printf("Path of file to write differences: ");
scanf("%s", path_diff);
/*THE NEW FILE*/
diff = fopen(path_diff, "a");
if (diff == NULL) {
printf("ERROR OPENING FILE TO WRITE!");
exit(1);
}
/*Get word and send to function*/
while (fscanf(text, "%s", word) != EOF) {
if (CheckMissing(dictionary, word) == 1) {
}
else {
//The word is printed on another file
fprintf(diff, "|%s|\n", word);
}
}
fclose(text);
fclose(dictionary);
fclose(diff);
return 0;
}
In C when you open a file, it will assign a pointer for you to iterate over it. In your case, you are opening it and never reseting this pointer and when it finishes the file, it doesn't check any words more.
If you want to reset this file pointer you can close the file and open it again. But its not an efficient solution. Better way is using fseek() function. Which takes 3 argument
int fseek(FILE *stream, long int offset, int whence)
To reset it to the start of file you need to use:
fseek(fp, 0, SEEK_SET)
Only thing you need to change in your code is CheckMissing function. For 1 or 0 results you need to reset File Pointers position. I added also a print line for you, you can see how this pointer works.
Below is fixed code:
int CheckMissing(FILE* dictionary, char dif[30]) {
char miss[30];
while (fscanf(dictionary, "%s", miss) != EOF) {
printf("checking %s and %s\n", miss, dif);
if (strcmp(dif, miss) == 0) {
fseek(dictionary, 0, SEEK_SET);
return 1;
}
}
fseek(dictionary, 0, SEEK_SET);
return 0;
}
For further resource you can check:
https://www.tutorialspoint.com/c_standard_library/c_function_fseek.htm
As others already pointed out, your logic was sound, but you must restart from the beginning of the dictionary file every time.
I suggest that you fix a bunch of other stuff in your program, in order to avoid other common mistakes.
The full program is at the end, but here you have the various pieces:
int CheckMissing(FILE* dictionary, const char *dif)
{
rewind(dictionary); // <-- This was the key element missing
char miss[30];
while (fscanf(dictionary, "%29s", miss) == 1) {
if (strcmp(dif, miss) == 0) {
return 0;
}
}
return 1;
}
Here I just added the already mentioned "start from beginning every time". Moreover, I suggest that you really understand that in C you cannot have arrays as function parameters, and that when you write char dif[30], dif is a pointer to char. Moreover use const as much as possible!
Then when you scan in a buffer always limit the maximum amount of characters that can be read (the %29s instead of just %s).
The fscanf() function returns the number of fields correctly read, so it's more general to test if the result matches the number of fields to be read, instead of EOF. In this case it's the same thing, but as soon as you try to read numbers it will make a huge difference.
Finally I inverted the output, since in my understanding a CheckMissing should return "true" if the word is missing.
Then split your main() in functions:
void OutputWordsNotInList(FILE *text, FILE *dictionary, FILE *diff)
{
/*Get word and send to function*/
char word[30];
while (fscanf(text, "%29s", word) == 1) {
if (CheckMissing(dictionary, word)) {
//The word is printed on another file
fprintf(diff, "|%s|\n", word);
}
}
}
Not much to say here, but the fact that the if statement becomes cleaner with the inverted logic. Of course I limit the maximum characters to be read also here and check for 1 instead of != EOF.
Then don't copy and paste! This will lead to inconsistencies. In the second check you missed an exit(1). Make a function for that.
FILE *fopen_ExitOnFail(const char *filename, const char *mode, const char *error_message)
{
FILE *f = fopen(filename, mode);
if (f == NULL) {
puts(error_message);
exit(1);
}
return f;
}
In the main() file don't ask user input during development. This will force you to type the input over and over again and make you walk away from debugging, which should be what you do most of the time.
I just made a conditional compilation block that can be activated when the rest is ok:
//#define INTERACTIVE
int main(void)
{
char path_t[1000] = "input.txt";
char path_d[1000] = "dictionary.txt";
char path_diff[1000] = "output.txt";
#ifdef INTERACTIVE
printf("Path of file to check: ");
scanf("%999s", path_t);
printf("Path of file to check from: ");
scanf("%999s", path_d);
printf("Path of file to write differences: ");
scanf("%999s", path_diff);
#endif // INTERACTIVE
/*FILE TO READ*/
FILE *text = fopen_ExitOnFail(path_t, "r", "ERROR OPENING TEXT FILE!");
/*LIST OF WORDS*/
FILE *dictionary = fopen_ExitOnFail(path_d, "r", "ERROR OPENING DICTIONARY!");
/*THE NEW FILE*/
FILE *diff = fopen_ExitOnFail(path_diff, "w", "ERROR OPENING FILE TO WRITE!"); // <-- I changed this to "w", to ease debugging.
OutputWordsNotInList(text, dictionary, diff);
fclose(text);
fclose(dictionary);
fclose(diff);
return 0;
}
The full code is here:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int CheckMissing(FILE* dictionary, const char *dif)
{
rewind(dictionary);
char miss[30];
while (fscanf(dictionary, "%29s", miss) == 1) {
if (strcmp(dif, miss) == 0) {
return 0;
}
}
return 1;
}
void OutputWordsNotInList(FILE *text, FILE *dictionary, FILE *diff)
{
/*Get word and send to function*/
char word[30];
while (fscanf(text, "%29s", word) == 1) {
if (CheckMissing(dictionary, word)) {
//The word is printed on another file
fprintf(diff, "|%s|\n", word);
}
}
}
FILE *fopen_ExitOnFail(const char *filename, const char *mode, const char *error_message)
{
FILE *f = fopen(filename, mode);
if (f == NULL) {
puts(error_message);
exit(1);
}
return f;
}
//#define INTERACTIVE
int main(void)
{
char path_t[1000] = "input.txt";
char path_d[1000] = "dictionary.txt";
char path_diff[1000] = "output.txt";
#ifdef INTERACTIVE
printf("Path of file to check: ");
scanf("%999s", path_t);
printf("Path of file to check from: ");
scanf("%999s", path_d);
printf("Path of file to write differences: ");
scanf("%999s", path_diff);
#endif // INTERACTIVE
/*FILE TO READ*/
FILE *text = fopen_ExitOnFail(path_t, "r", "ERROR OPENING TEXT FILE!");
/*LIST OF WORDS*/
FILE *dictionary = fopen_ExitOnFail(path_d, "r", "ERROR OPENING DICTIONARY!");
/*THE NEW FILE*/
FILE *diff = fopen_ExitOnFail(path_diff, "w", "ERROR OPENING FILE TO WRITE!"); // <-- I changed this to "w", to ease debugging.
OutputWordsNotInList(text, dictionary, diff);
fclose(text);
fclose(dictionary);
fclose(diff);
return 0;
}
I have a text file and I wanted to extract only a specific part of it at a particular time.For that ,I used ftell() while writing to note the start and end positions and then use fseek() to jump to that particular location.
int main()
{
FILE *fp=fopen("myt","w+");
char s[80];
printf ( "\nEnter a few lines of text:\n" ) ;
while ( strlen ( gets ( s ) ) > 0 ) //user inputs random data
{ //till enter is pressed
fputs ( s, fp ) ;
fputs ( "\n", fp ) ;
}
long int a=ftell(fp);
fputs("this line is supposed to be printed only ",fp);//line to be
// displayed
fputs("\n",fp);
long int b=ftell(fp);
printf("start is %ld",a);
printf("\nend is %ld",b);
printf("here is the data...\n");
rewind(fp);
fseek(fp,a,SEEK_CUR); //move to the starting position of text to be
//displayed
char x[1000];
fgets(x,b-a,SEEK_CUR);
printf("%s",x);
return 1;
}
I tried this but face a unexpected abnormal termination of program.Please guide me as to how correctly implement my task.
You want this:
Comments starting with //// are mine
#include <stdio.h> //// include required header files
#include <string.h>
int main()
{
FILE *fp = fopen("myt", "w+");
if (fp == NULL) //// test if file has been opened sucessfully
{
printf("Can't open file\n");
return 1; //// return 1 in case of failure
}
char s[80];
printf("\nEnter a few lines of text:\n");
while (strlen(gets(s)) > 0) //user inputs random data
{ //till enter is pressed
fputs(s, fp);
fputs("\n", fp);
}
long int a = ftell(fp);
fputs("this line is supposed to be printed only ", fp);//line to be
// displayed
fputs("\n", fp);
long int b = ftell(fp);
printf("start is %ld", a);
printf("\nend is %ld", b);
printf("here is the data...\n");
rewind(fp);
fseek(fp, a, SEEK_CUR); //move to the starting position of text to be
//displayed
char x[1000];
fgets(x, sizeof(x), fp); //// the usage of fgets was totally wrong
printf("%s", x);
return 0; //// return 0 in case of success, no one
}
Disclaimer: The first part reading the strings using gets is still sloppy, you should never use gets, it's an old deprecated function. Use fgets instead.