Skip to content

Instantly share code, notes, and snippets.

@rudolfovich
Last active September 21, 2023 08:50
Show Gist options
  • Star 29 You must be signed in to star a gist
  • Fork 11 You must be signed in to fork a gist
  • Save rudolfovich/f250900f1a833e715260a66c87369d15 to your computer and use it in GitHub Desktop.
Save rudolfovich/f250900f1a833e715260a66c87369d15 to your computer and use it in GitHub Desktop.
CSV file generator
#pragma once
#include <string>
#include <iostream>
#include <fstream>
#include <sstream>
class csvfile;
inline static csvfile& endrow(csvfile& file);
inline static csvfile& flush(csvfile& file);
class csvfile
{
std::ofstream fs_;
bool is_first_;
const std::string separator_;
const std::string escape_seq_;
const std::string special_chars_;
public:
csvfile(const std::string filename, const std::string separator = ";")
: fs_()
, is_first_(true)
, separator_(separator)
, escape_seq_("\"")
, special_chars_("\"")
{
fs_.exceptions(std::ios::failbit | std::ios::badbit);
fs_.open(filename);
}
~csvfile()
{
flush();
fs_.close();
}
void flush()
{
fs_.flush();
}
void endrow()
{
fs_ << std::endl;
is_first_ = true;
}
csvfile& operator << ( csvfile& (* val)(csvfile&))
{
return val(*this);
}
csvfile& operator << (const char * val)
{
return write(escape(val));
}
csvfile& operator << (const std::string & val)
{
return write(escape(val));
}
template<typename T>
csvfile& operator << (const T& val)
{
return write(val);
}
private:
template<typename T>
csvfile& write (const T& val)
{
if (!is_first_)
{
fs_ << separator_;
}
else
{
is_first_ = false;
}
fs_ << val;
return *this;
}
std::string escape(const std::string & val)
{
std::ostringstream result;
result << '"';
std::string::size_type to, from = 0u, len = val.length();
while (from < len &&
std::string::npos != (to = val.find_first_of(special_chars_, from)))
{
result << val.substr(from, to - from) << escape_seq_ << val[to];
from = to + 1;
}
result << val.substr(from) << '"';
return result.str();
}
};
inline static csvfile& endrow(csvfile& file)
{
file.endrow();
return file;
}
inline static csvfile& flush(csvfile& file)
{
file.flush();
return file;
}
#include "csvfile.h"
int main()
{
try
{
csvfile csv("MyTable.csv"); // throws exceptions!
// Hearer
csv << "X" << "VALUE" << endrow;
// Data
int i = 1;
csv << i++ << "String value" << endrow;
csv << i++ << 123 << endrow;
csv << i++ << 1.f << endrow;
csv << i++ << 1.2 << endrow;
csv << i++ << "One more string" << endrow;
csv << i++ << "\"Escaped\"" << endrow;
csv << i++ << "=HYPERLINK(\"https://playkey.net\"; \"Playkey Service\")" << endrow;
}
catch (const std::exception &ex)
{
std::cout << "Exception was thrown: " << ex.what() << std::endl;
}
return 0;
}
@nassausky
Copy link

Wow that class looks short and sweet. Can you add comments to each line to tell us what it does. I'm especially interested on how each of the overload operators work.

@rajesh23sharma
Copy link

Is there a way to append data to already existing file?

@rudolfovich
Copy link
Author

@nassausky, thanks, yes, i'll add comments.

@rajesh23sharma, just open file in append mode at line 21
fs_.open(filename, std::ofstream::app);
more info about open mode

@fabito
Copy link

fabito commented Oct 19, 2017

How can we prevent the trailing separator at the end of each line ?
Could we handle that in the endrow function ?

@rudolfovich
Copy link
Author

@fabito, I've fixed trailing separator issue.
Also added escape quotes logic.

@Sigill
Copy link

Sigill commented Jan 31, 2019

Would you consider adding a license to that?

@AlexanderMoran1995
Copy link

is this open source? id love to use this with a program im developing.

@rudolfovich
Copy link
Author

id love to use this with a program im developing

@AlexanderMoran1995, yes, you may use, change, distribute this.

@yujietang
Copy link

Very useful and thanks a lot, good luck!

@davidglavas
Copy link

Do you have something similar for reading .csv files?

@rudolfovich
Copy link
Author

@davidglavas, I don't have it.
Reading is a bit more complicated.
So you may face different types of CSV file formats, so you must be ready to parse all of them and guess what CSV file format you are parsing.
Of course you can make simple parser with only one supported format if you work with internal files at your company, but i believe that it will be bad decision.

@Wolf-SO
Copy link

Wolf-SO commented Jul 14, 2020

What about taking an already open stream in the constructor and transforming it into a csv_writer?

BTW: "Hearer" in main.cpp (8) should be "Header".

@TheUnixDude
Copy link

@rudolfovich Can you confirm you have licensed this under the BSD 3-clause?

@Shoray2002
Copy link

Thank You for the code, Worked great in a project I was working on🎉

@rudolfovich
Copy link
Author

@rudolfovich Can you confirm you have licensed this under the BSD 3-clause?

Yes, this source code may be used under BSD 3-clause.

@rudolfovich
Copy link
Author

Thank You for the code, Worked great in a project I was working on🎉

Glad to hear it, you are welcome!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment