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("[-]. %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?
CodePudding user response:
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.