Home > Back-end >  Maintenance of code that generate CSV files in C
Maintenance of code that generate CSV files in C

Time:12-16

I'm looking for an easier way to maintain a code that generate CSV file.

Currently, each line in the CSV file is written in the following way:

fprintf(pCsvFile,"%s,%s,%d,%d,%d",param->a, param->b, param->c, param->d, param->e);

In reality I have around 20 different values from different types that I'm writing in every CSV file row, and as you can guess its start getting really difficult to maintain the code (adding or removing parameters).

Is there any clever way to do it in C language? Thanks.

CodePudding user response:

Each value can be written with a separate call the fprintf(). Unless you are sure that the string elements do not need special handling (see https://www.rfc-editor.org/rfc/rfc4180), you should implement a function (called fprintfCsvString() below) to handle string elements correctly, i.e. enclosing in double quotes if needed and escaping double quote characters.

Writing each CSV line could then look like this:

  fprintfCsvString(pCsvFile, param->a); fprintf(pCsvFile, ",");
  fprintfCsvString(pCsvFile, param->b); fprintf(pCsvFile, ",");
  fprintf(pCsvFile, "%d", param->c); fprintf(pCsvFile, ",");
  fprintf(pCsvFile, "%d", param->d); fprintf(pCsvFile, ",");
  fprintf(pCsvFile, "%d", param->e); fprintf(pCsvFile, "\r\n");  // End of line

(the implementation of fprintfCsvString() is not within the scope of this question/answer)

CodePudding user response:

If your number of CSV fields is fixed in the whole program then you could define a single macro:

#define FPRINTCSV(X,V1,F1,V2,F2,V3,F3,V4,F4,V5,F5) \
    fprintf(X, F1 "," F2 "," F3 "," F4 "," F5 "\n", V1, V2, V3, V4, V5)

FPRINTCSV(pCsvFile,
    param->a, "%s",
    param->b, "%s",
    param->c, "%d",
    param->d, "%d",
    param->e, "%d"
);

note: the macro needs literal strings as format

You'll then be able to see or set the format of each field clearly. Adding or removing fields will require the modification of the macro though, but that should be easier than modifying a long format string and its corresponding arguments, IMHO.

CodePudding user response:

This looks like a job for a tagged union. Here is an example of how to write more dynamic records.

#include <limits.h>
#include <stdio.h>

#define FIELD_COUNT 20

enum Kind {
    String,
    Int,
    Float,
};

union Value {
    const char* s;
    int         i;
    float       f;
};

struct Tagged_Union {
    enum Kind kind;
    union Value value;
};

void print_field(struct Tagged_Union tg)
{
    switch (tg.kind) {
    case String:
        // to keep with the RFC4180 "standard," this is 
        // incorrect, but may not matter for your use case.
        printf("%s", tg.value.s);
        break;
    case Int:
        printf("%d", tg.value.i);
        break;
    case Float:
        printf("%f", tg.value.f);
    }
}

int main()
{
    struct Tagged_Union tg[FIELD_COUNT];

    // fill in your data

    int i = 0;
    for (; i < FIELD_COUNT;   i) {
        if (!i) {
            printf(",");
        }
        print_field(tg[i]);
    }
    printf("\n");
}

Well, this will only print one record, but I hope you could see how this could be applied for your use case.

  • Related