Home > other >  Structs for file I/O
Structs for file I/O

Time:05-29

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 a File pointer is easy to customize File.
  • trim() releases all pointers not in use
  • show() accepts a title, for convenience
  • get_pos() returns the position of the provided file in the array
  • destroy() returns NULL 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.

  • Related