0

I want to compile a CSV file into my Binary so that when I run my EXE application it does not need the required CSV file in the folder, and has the CSV files data within the EXE already.

For instance I have multiple CSV files with 2 columns and 150+ rows of strings which I want to, at runtime, be parsed into a C++ map. When I use this application I dont want it to be in a folder with multiple CSV files, rather just the EXE which can be more portable. If the data in these CSV files had to change then I would simply re-build the solution with updated CSV files.

Is C++ able to do this? If so how would I do this? I'm not looking for alternatives to how I want this designed, I would like to have it set out how I have described, and if it is not possible I will just create an array or enum within a header file.

5
  • 3
    It sounds like you should create a compiler for .csv files that outputs .cpp and/or .hpp files. Sure, it can be done in C++ or by a simple script. Commented Aug 16, 2019 at 7:27
  • 1
    Yes it it possible in general. There are a few approaches. What do you use to build your program? Visual Studio? CMake? do you use Qt in your project? Commented Aug 16, 2019 at 7:27
  • Have a look at the options given here: Is there a cross platform way to embed resources in a binary application written with C++? Commented Aug 16, 2019 at 7:31
  • 2
    I have actually had to do something like this with shaders for OpenGL. The simplest way to do this is to literally store your CSV values into a string in your source code somewhere, this can then be used in the parser later on. Now a more sophisticated and flexible solution would be as @TedLyngmo said: Create a CVS to .cpp and header files which automatically runs with your pre build scripts - the cpp would include the big string with the contents and the header a declaration to the string with the contents, so you can include it in other scripts. Commented Aug 16, 2019 at 9:56
  • ... and if you have .csv files with different formats that you want transformed into different C++ types, you could create a file describing how the conversion should be done. foo.csv could have a foo.csv2cpp that your .csv compiler reads. Jacking in the compiler and build result into the build dependency chain should then be easy. Commented Aug 16, 2019 at 10:23

1 Answer 1

2

I like the proposal of some guys here and created a working example for a "csv" to "c++ header" file compiler.

It will even deal with different column sized CSVs.

It stores the result as compile-time-constexpr array of array of std::string_view.

Maybe it can give you the basic idea:

#include <iostream>
#include <string>
#include <array>
#include <vector>
#include <regex>
#include <sstream>
#include <fstream>
#include <algorithm>

// The delimiter
const std::regex re(",");

std::istringstream sourceCSV1{R"(A00,A01,A02
A10,A11,A12
A20,A21,A22)"};

std::istringstream sourceCSV2{R"(B00,B01
B10,B11,B12,B13,B14
B20,B21,B22,B23)"};


// Define Alias for Easier Reading
using Columns = std::vector<std::string>;
using CSV = std::vector<Columns>;


// Proxy for the input Iterator
struct ColumnProxy {    
    // Overload extractor. Read a complete line
    friend std::istream& operator>>(std::istream& is, ColumnProxy& cp) {

        // Read a line
        std::string line; cp.columns.clear();
        std::getline(is, line);

        // Split values and copy into resulting vector
        std::copy(std::sregex_token_iterator(line.begin(), line.end(), re, -1),
            std::sregex_token_iterator(),
            std::back_inserter(cp.columns));
        return is;
    }

    // Type cast operator overload.  Cast the type 'Columns' to std::vector<std::string>
    operator std::vector<std::string>() const { return columns; }
protected:
    // Temporary to hold the read vector
    Columns columns{};
};

void convertCSV2Hpp(std::istream& is, std::ostream& os, std::string& variableName)
{
   // Read complete CSV File
   CSV csvFile { std::istream_iterator<ColumnProxy>(is), std::istream_iterator<ColumnProxy>() };
   // Get maximumn number of columns in CSV file
   size_t maxCols = std::max_element(csvFile.begin(),csvFile.end(),[](const Columns& c1, const Columns& c2){ return c1.size() < c2.size();})->size();

   // Build output header file 
   std::string includeGuard(variableName);
   std::transform(variableName.begin(), variableName.end(),includeGuard.begin(), ::toupper);

   // Print header of file
   os << "#ifndef " << includeGuard << "_HPP" << "\n#define " << includeGuard << "_HPP\n\n#include <string>\n#include <array>\n\n" 
       << "constexpr size_t " << variableName << "NumberOfRows {" << csvFile.size() << "U};\n"
       << "constexpr size_t " << variableName << "NumberOfColumns {" << maxCols << "U};\n\n"
       << "constexpr std::array<std::array<std::string_view," << variableName << "NumberOfColumns" << ">," << variableName << "NumberOfRows" << "> " << variableName << " {{";

    // Print data
    for (size_t row = 0U; row < csvFile.size(); ++row) {
        os << "\n{";
        for (size_t col=0U; col<maxCols; ++col) {
            os << "\"" << ((col < csvFile[row].size())?csvFile[row][col]:"") << "\"" << ((col==maxCols-1)?"":", "); 
        }
        os <<  "},";
    }
    os << "\n}};\n\n#endif\n\n";
}

int main()
{
    std::string name("csv1");
    convertCSV2Hpp(sourceCSV1,std::cout,name);
    name = "csv2";
    convertCSV2Hpp(sourceCSV2,std::cout,name);
    return 0;
}

Because I do not have files here on SO, I used std::istringstream as input and std::cout as output file. You can of course use whatever file (stream) you like.

Sign up to request clarification or add additional context in comments.

1 Comment

Hi thanks for all the effort everyone has put into helping me with this question! I haven't been able to implement this yet but am excited to try it!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.