I was trying to figure out the best way to keep a record of file pointers as well as individual information for each file such as a file path.
My question is, having a folder struct that holds an array of file pointers, and a file struct that holds information for files, how could merge these two concepts so that can I hold an array of file pointers, and store the file path for each of those files?
Header file for a folder struct below:
#ifndef FOLDER_STRUCT_H
#define FOLDER_STRUCT_H
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
struct folder_
{
FILE **files;
size_t current_size;
size_t capacity;
};
typedef struct folder_ folder_t;
folder_t *folder_create(size_t initial_size); //-----------------------Create a folder struct
void folder_destroy(folder_t *folder); //------------------------------Destroy a folder struct
bool folder_insert_file(folder_t *const folder, FILE *const file); //--Insert a file
FILE *folder_get_file(folder_t *const folder, size_t index); //--------Get a file by index
FILE **folder_get_file_list(folder_t *const folder); //----------------Get a list of files
int folder_get_size(folder_t *folder); //------------------------------Get folder size
int folder_get_total_capacity(folder_t *folder); //--------------------Get folder capacity
#endif
Header file for a file struct used to record file information such as a file path:
#ifndef FILE_H
#define FILE_H
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
struct file_
{
FILE *file;
char *file_path;
};
typedef struct file_ file_t;
#endif
CodePudding user response:
The generic approach is if you need both at the same time use another struct, and if you need either use a union probably with a tag field to tell which of the struct it holds. In your case, it sounds like you have 1
folder to n files, so your folder_file_
struct would have an array of files_
. If you have more than one folder, then you need an array of folder_file_
.
FILE *
usually implies an open file handle. Is that really your use case? Opposed to a bunch of paths, and you only open the file as you have to operate on it. Also, why is FILE **
opposed to a FILE *
?
CodePudding user response:
Usually is best to let the system manage the file pointers. The file can be reopened outside your struct
for example. Or just closed. The struct
can get a copy of a FILE*
but there is no way to know if it points to the same file as of the moment of creation.
Anyway, sometimes we may need a way to store info for a collection of files.
An EXAMPLE
typedef struct
{
char* name;
FILE* F;
} File;
typedef struct
{
size_t size;
size_t cap; // capacity
size_t inc; // increment
File** info; // data array
} Folder;
Inside File
we can store sizes, dates, maybe permissions.
Folder
is an array of pointers to File
. cap
and size
are the usual, inc
is the size of increment of Folder
in files.
A few functions are implemented:
Folder* create(size_t capacity, size_t step);
Folder* destroy(Folder*);
int insert(File*, Folder*);
int show(Folder*, const char*);
int trim(Folder*);
int get_pos(const char* f_name, Folder*);
insert()
accepts aFile
pointer is easy to customizeFile
.trim()
releases all pointers not in useshow()
accepts a title, for convenienceget_pos()
returns the position of the provided file in the arraydestroy()
returnsNULL
to ease in invalidate the pointer in the same line
main
for this test
int main(void)
{
const char tst_size = 10;
const char* scan[] = {"File_7533.tst", "File_7500.tst"};
srand(220520);
Folder* tst = create(4, 4);
show(tst, "just created");
char f_name[20] = {0};
for (int i = 0; i < tst_size; i = 1)
{
sprintf(f_name, "File_d.tst", 1 rand()000);
insert_this(f_name,NULL,tst);
}
sprintf(f_name, "%d files inserted", tst_size);
show(tst, f_name);
trim(tst);
show(tst, "after trim()");
for (int i = 0; i < sizeof(scan) / sizeof(scan[0]);i =1)
printf( "search por \"%s\" returned %d\n", scan[i],
get_pos(scan[i], tst));
tst = destroy(tst);
return 0;
}
- a Folder is created with size 4 and incrementable in groups of 4
- 10 files are inserted
- the contents are listed
- search for 2 files
- the thing is destroyed
test output
just created
[0/4 files (step = 4)]
insert() size extended to 8
insert() size extended to 12
10 files inserted
[10/12 files (step = 4)]
0 File_2037.tst
1 File_5785.tst
2 File_6602.tst
3 File_7231.tst
4 File_0854.tst
5 File_7102.tst
6 File_7533.tst
7 File_6460.tst
8 File_1717.tst
9 File_1948.tst
trim() new size: 10
after trim()
[10/10 files (step = 4)]
0 File_2037.tst
1 File_5785.tst
2 File_6602.tst
3 File_7231.tst
4 File_0854.tst
5 File_7102.tst
6 File_7533.tst
7 File_6460.tst
8 File_1717.tst
9 File_1948.tst
search for "File_7533.tst" returned 6
search for "File_7500.tst" returned -1
Folder structure destroyed
stuff.h
#ifndef STUFF_H
#define STUFF_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct
{
char* name;
FILE* F;
} File;
typedef struct
{
size_t size;
size_t cap; // capacity
size_t inc; // increment
File** info; // data array
} Folder;
Folder* create(size_t capacity, size_t step);
Folder* destroy(Folder*);
int insert(File*, Folder*);
int show(Folder*, const char*);
int trim(Folder*);
int get_pos(const char* f_name, Folder*);
#endif
stuff.c
#include "stuff.h"
Folder* create(size_t capacity, size_t step)
{
Folder* nw = (Folder*)malloc(sizeof(Folder));
if (nw == NULL) return NULL;
nw->size = 0;
nw->cap = capacity;
nw->inc = step;
nw->info = (File**) malloc(nw->cap*sizeof(File*));
for (int i = 0; i < nw->cap; i = 1)
nw->info[i] = NULL;
return nw;
}
Folder* destroy(Folder* f)
{
if (f == NULL) return NULL;
for (int i = 0; i < f->size; i = 1)
{
free(f->info[i]->name);
free(f->info[i]);
}
free(f->info);
free(f);
printf("Folder structure destroyed\n");
return NULL;
}
int insert(File* item, Folder* f)
{
if (f == NULL) return -1;
if (item == NULL) return -2;
if (f->size >= f->cap)
{ // extends folder struct size
size_t new_size = f->cap f->inc;
File* p = realloc((void*)f->info, new_size*sizeof(File*));
if (p == NULL) return -3; // error extending
f->info = (File**) p;
f->cap = new_size;
printf("insert() size extended to %zd\n", f->cap);
};
// ok, new File then
File* nw = (File*)malloc(sizeof(File));
nw->name = (char*)malloc(1 strlen(item->name));
strcpy(nw->name, item->name);
nw->F = item->F;
f->info[f->size] = nw;
f->size = 1;
return (int)f->size;
}
int show(Folder* f, const char* msg)
{
if (msg != NULL) printf("%s\n", msg);
printf(
"[%zd/%zd files (step = %zd)\n", f->size, f->cap,
f->inc);
for (int i = 0; i < f->size; i = 1)
{
printf("= %s\n", i, f->info[i]->name);
}
printf("\n");
return 0;
}
int trim(Folder* f)
{
if (f == NULL) return -1;
if (f->cap == f->size) return 0; // nothing to do
File* p = realloc((void*)f->info, f->size * sizeof(File*));
if (p == NULL) return -3; // error extending
f->info = (File**) p;
f->cap = f->size;
printf("trim() new size: %zd\n", f->cap);
return 0;
};
int get_pos(const char* f_name, Folder* f)
{
if (f_name == NULL) return -1;
for (int i = 0; i < f->size; i = 1)
if (strcmp(f_name, f->info[i]->name) == 0) return i;
return -1;
}
Note to SO "code reviewers": I do cast all malloc()
pointers, as I do not like implicit things and I like them as additional reminders of things to come.