This was written down solely as a mean to practice basic C++ and isn't meant to serve any production purposes. Clearly, the implementation can't handle all input formats: it always expects the CSV to have a header line, it doesn't treat quoted commas well, the file has to have a newline in the end, hardcoded separator, etc.
I'm mostly interested in what am I doing wrong from the effectiveness and idiomatic perspective. I'm sure there's something wrong even with these 60 lines. For instance, I don't like the mech of filling data
, I feel there's too much tossing stuff around.
Do I feel right about it? Can it be done in a more concise and effective way? Anything else, maybe like a forgotten const
, or a totally non-idiomatic way to do something?
In short, how can I make this code (and subsequently, my C++) better? Thank you.
#include <iostream>
#include <vector>
#include <map>
#include <string>
#include <fstream>
#include <sstream>
#include <stdexcept>
template <typename OutputIterator>
void explode(const std::string& input, char sep, OutputIterator output) {
std::istringstream buffer(input);
std::string temp;
while (std::getline(buffer, temp, sep)) {
*output++ = temp;
}
}
class CSV {
private:
std::vector<std::string> headers;
std::vector<std::map<std::string, std::string>> data;
size_t length{};
public:
explicit CSV(const std::string& filename) {
std::ifstream ifs(filename);
std::string s;
std::getline(ifs, s, '\n');
explode(s, ',', std::back_insert_iterator<std::vector<std::string>>(headers));
std::map<std::string, std::string> temp_map;
std::vector<std::string> temp_values;
while (std::getline(ifs, s, '\n')) {
explode(s, ',', std::back_insert_iterator<std::vector<std::string>>(temp_values));
for (size_t i = 0, headers_size = headers.size(); i < headers_size; ++i) {
temp_map[headers[i]] = temp_values[i];
}
data.emplace_back(temp_map);
temp_values.clear();
temp_map.clear();
++length;
}
};
const std::map<std::string, std::string>& operator[](const size_t& index) {
if (index < 0 || index >= length) throw std::runtime_error("Invalid index");
return data[index];
}
};
std::ostream& operator<<(std::ostream& os, const std::map<std::string, std::string>& m) {
for (const auto& el: m) {
os << el.first << ": " << el.second << ", ";
}
return os;
}
int main() {
CSV c("data.csv");
std::cout << c[0] << std::endl;
return 0;
}