flatten 20260225
This commit is contained in:
168
tjp/core/csv/Csv.cpp
Normal file
168
tjp/core/csv/Csv.cpp
Normal file
@@ -0,0 +1,168 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#include "Csv.hpp"
|
||||
|
||||
// Adapted from
|
||||
// https://stackoverflow.com/questions/1120140/how-can-i-read-and-parse-csv-files-in-c
|
||||
|
||||
#include <tjp/core/iterators/enumerate.hpp>
|
||||
#include <sstream>
|
||||
|
||||
namespace tjp {
|
||||
namespace core {
|
||||
|
||||
CSV::CSV(std::istream& str_, bool readHeader_) :
|
||||
str(str_)
|
||||
{
|
||||
if (readHeader_)
|
||||
readHeader();
|
||||
}
|
||||
|
||||
void CSV::readHeader ()
|
||||
{
|
||||
readNextRow();
|
||||
int i=0;
|
||||
for (auto &s : m_data)
|
||||
{
|
||||
m_header[s] = i;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
std::string const& CSV::operator[](std::size_t index) const
|
||||
{
|
||||
return m_data[index];
|
||||
}
|
||||
|
||||
std::string const& CSV::operator[](const std::string &index) const
|
||||
{
|
||||
auto i = m_header.find(index);
|
||||
return m_data[i->second];
|
||||
}
|
||||
|
||||
std::size_t CSV::size() const
|
||||
{
|
||||
return m_data.size();
|
||||
}
|
||||
|
||||
bool CSV::readNextRow()
|
||||
{
|
||||
std::string line;
|
||||
std::getline(str, line);
|
||||
if (!line.empty() && line.back() == '\r')
|
||||
line.resize(line.size()-1);
|
||||
|
||||
std::stringstream lineStream(line);
|
||||
|
||||
std::string cell;
|
||||
|
||||
m_data.clear();
|
||||
while(std::getline(lineStream, cell, ','))
|
||||
{
|
||||
m_data.push_back(cell);
|
||||
}
|
||||
// This checks for a trailing comma with no data after it.
|
||||
if (!lineStream && cell.empty())
|
||||
{
|
||||
// If there was a trailing comma then add an empty element.
|
||||
m_data.push_back("");
|
||||
}
|
||||
|
||||
return str.good();
|
||||
}
|
||||
|
||||
FastCSV::FastCSV(std::istream& str_, bool readHeader_) :
|
||||
str(str_),
|
||||
lineTokens(Tokenizer::Tokens("\n")),
|
||||
csvTokens(Tokenizer::Tokens(","))
|
||||
{
|
||||
const auto bufferSize = 1024;
|
||||
char buffer[bufferSize];
|
||||
while (str_)
|
||||
{
|
||||
str_.read(buffer, bufferSize);
|
||||
data.insert(data.end(), buffer, buffer + str_.gcount());
|
||||
}
|
||||
|
||||
tokenizer = Tokenizer(data.data(), data.size(), lineTokens);
|
||||
|
||||
if (readHeader_)
|
||||
readHeader();
|
||||
}
|
||||
|
||||
void FastCSV::readHeader ()
|
||||
{
|
||||
readNextRow();
|
||||
int i=0;
|
||||
for (auto &s : m_data)
|
||||
{
|
||||
m_header[s] = i;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
FastCSV::Result FastCSV::operator[](std::size_t index) const
|
||||
{
|
||||
return Result(m_data[index]);
|
||||
}
|
||||
|
||||
FastCSV::Segment FastCSV::segment(std::size_t index) const
|
||||
{
|
||||
return m_data[index];
|
||||
};
|
||||
|
||||
std::size_t FastCSV::index(const Segment &index) const
|
||||
{
|
||||
auto i = m_header.find(index);
|
||||
if (i == m_header.end())
|
||||
return -1;
|
||||
|
||||
return i->second;
|
||||
}
|
||||
|
||||
FastCSV::Result FastCSV::operator[](const Segment &index) const
|
||||
{
|
||||
auto i = m_header.find(index);
|
||||
return Result(m_data[i->second]);
|
||||
}
|
||||
|
||||
std::size_t FastCSV::size() const
|
||||
{
|
||||
return m_data.size();
|
||||
}
|
||||
|
||||
bool FastCSV::readNextRow()
|
||||
{
|
||||
Segment line;
|
||||
char token;
|
||||
bool result = tokenizer.next(line, token);
|
||||
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
if (!line.empty() && line.back() == '\r')
|
||||
line.remove_suffix(1);
|
||||
|
||||
Tokenizer lineTokenizer(line.data(), line.size(), csvTokens);
|
||||
|
||||
Segment cell;
|
||||
char cellToken;
|
||||
m_data.clear();
|
||||
|
||||
while (lineTokenizer.next(cell, cellToken))
|
||||
{
|
||||
m_data.push_back(cell);
|
||||
}
|
||||
// This checks for a trailing comma with no data after it.
|
||||
if (cell.empty())
|
||||
{
|
||||
// If there was a trailing comma then add an empty element.
|
||||
m_data.push_back(Segment());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
208
tjp/core/csv/Csv.h
Normal file
208
tjp/core/csv/Csv.h
Normal file
@@ -0,0 +1,208 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iterator>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include <tjp/core/string/Tokenizer.hpp>
|
||||
|
||||
// Adapted from
|
||||
// https://stackoverflow.com/questions/1120140/how-can-i-read-and-parse-csv-files-in-c
|
||||
|
||||
#include <tjp/core/iterators/enumerate.hpp>
|
||||
|
||||
namespace tjp {
|
||||
namespace core {
|
||||
|
||||
class CSV
|
||||
{
|
||||
public:
|
||||
CSV(std::istream& str_, bool readHeader_ = true) :
|
||||
str(str_)
|
||||
{
|
||||
if (readHeader_)
|
||||
readHeader();
|
||||
}
|
||||
|
||||
void readHeader ()
|
||||
{
|
||||
readNextRow();
|
||||
int i=0;
|
||||
for (auto &s : m_data)
|
||||
{
|
||||
m_header[s] = i;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
std::string const& operator[](std::size_t index) const
|
||||
{
|
||||
return m_data[index];
|
||||
}
|
||||
|
||||
std::string const& operator[](const std::string &index) const
|
||||
{
|
||||
auto i = m_header.find(index);
|
||||
return m_data[i->second];
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
return m_data.size();
|
||||
}
|
||||
|
||||
bool readNextRow()
|
||||
{
|
||||
std::string line;
|
||||
std::getline(str, line);
|
||||
if (!line.empty() && line.back() == '\r')
|
||||
line.resize(line.size()-1);
|
||||
|
||||
std::stringstream lineStream(line);
|
||||
|
||||
std::string cell;
|
||||
|
||||
m_data.clear();
|
||||
while(std::getline(lineStream, cell, ','))
|
||||
{
|
||||
m_data.push_back(cell);
|
||||
}
|
||||
// This checks for a trailing comma with no data after it.
|
||||
if (!lineStream && cell.empty())
|
||||
{
|
||||
// If there was a trailing comma then add an empty element.
|
||||
m_data.push_back("");
|
||||
}
|
||||
|
||||
return str.good();
|
||||
}
|
||||
|
||||
protected:
|
||||
std::istream& str;
|
||||
std::map<std::string, size_t> m_header;
|
||||
std::vector<std::string> m_data;
|
||||
};
|
||||
|
||||
class FastCSV
|
||||
{
|
||||
protected:
|
||||
typedef std::string_view Segment;
|
||||
typedef std::string Result;
|
||||
|
||||
std::vector<char> data;
|
||||
|
||||
typedef Tokenizer<Segment, TokenVector> Tokenizer;
|
||||
|
||||
Tokenizer::Tokens lineTokens;
|
||||
Tokenizer::Tokens csvTokens;
|
||||
Tokenizer tokenizer;
|
||||
|
||||
public:
|
||||
FastCSV(std::istream& str_, bool readHeader_ = true) :
|
||||
str(str_),
|
||||
lineTokens(Tokenizer::Tokens("\n")),
|
||||
csvTokens(Tokenizer::Tokens(","))
|
||||
{
|
||||
const auto bufferSize = 1024;
|
||||
char buffer[bufferSize];
|
||||
while (str_)
|
||||
{
|
||||
str_.read(buffer, bufferSize);
|
||||
data.insert(data.end(), buffer, buffer + str_.gcount());
|
||||
}
|
||||
|
||||
tokenizer = Tokenizer(data.data(), data.size(), lineTokens);
|
||||
|
||||
if (readHeader_)
|
||||
readHeader();
|
||||
}
|
||||
|
||||
void readHeader ()
|
||||
{
|
||||
readNextRow();
|
||||
int i=0;
|
||||
for (auto &s : m_data)
|
||||
{
|
||||
m_header[s] = i;
|
||||
++i;
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
Result operator[](std::size_t index) const
|
||||
{
|
||||
return Result(m_data[index]);
|
||||
}
|
||||
|
||||
Segment segment(std::size_t index) const
|
||||
{
|
||||
return m_data[index];
|
||||
};
|
||||
|
||||
std::size_t index(const Segment &index) const
|
||||
{
|
||||
auto i = m_header.find(index);
|
||||
if (i == m_header.end())
|
||||
return -1;
|
||||
|
||||
return i->second;
|
||||
}
|
||||
|
||||
Result operator[](const Segment &index) const
|
||||
{
|
||||
auto i = m_header.find(index);
|
||||
return Result(m_data[i->second]);
|
||||
}
|
||||
|
||||
std::size_t size() const
|
||||
{
|
||||
return m_data.size();
|
||||
}
|
||||
|
||||
bool readNextRow()
|
||||
{
|
||||
Segment line;
|
||||
char token;
|
||||
bool result = tokenizer.next(line, token);
|
||||
|
||||
if (!result)
|
||||
return false;
|
||||
|
||||
if (!line.empty() && line.back() == '\r')
|
||||
line.remove_suffix(1);
|
||||
|
||||
Tokenizer lineTokenizer(line.data(), line.size(), csvTokens);
|
||||
|
||||
Segment cell;
|
||||
char cellToken;
|
||||
m_data.clear();
|
||||
|
||||
while (lineTokenizer.next(cell, cellToken))
|
||||
{
|
||||
m_data.push_back(cell);
|
||||
}
|
||||
// This checks for a trailing comma with no data after it.
|
||||
if (cell.empty())
|
||||
{
|
||||
// If there was a trailing comma then add an empty element.
|
||||
m_data.push_back(Segment());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
std::istream& str;
|
||||
std::map<Segment, size_t> m_header;
|
||||
std::vector<Segment> m_data;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
82
tjp/core/csv/Csv.hpp
Normal file
82
tjp/core/csv/Csv.hpp
Normal file
@@ -0,0 +1,82 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <iosfwd>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include <tjp/core/string/Tokenizer.hpp>
|
||||
|
||||
// Adapted from
|
||||
// https://stackoverflow.com/questions/1120140/how-can-i-read-and-parse-csv-files-in-c
|
||||
|
||||
#include <tjp/core/iterators/enumerate.hpp>
|
||||
|
||||
namespace tjp {
|
||||
namespace core {
|
||||
|
||||
class CSV
|
||||
{
|
||||
public:
|
||||
CSV(std::istream& str_, bool readHeader_ = true);
|
||||
|
||||
void readHeader ();
|
||||
|
||||
public:
|
||||
std::string const& operator[](std::size_t index) const;
|
||||
|
||||
std::string const& operator[](const std::string &index) const;
|
||||
|
||||
std::size_t size() const;
|
||||
|
||||
bool readNextRow();
|
||||
|
||||
protected:
|
||||
std::istream& str;
|
||||
std::map<std::string, size_t> m_header;
|
||||
std::vector<std::string> m_data;
|
||||
};
|
||||
|
||||
class FastCSV
|
||||
{
|
||||
protected:
|
||||
typedef std::string_view Segment;
|
||||
typedef std::string Result;
|
||||
|
||||
std::vector<char> data;
|
||||
|
||||
typedef core::Tokenizer<Segment, TokenVector> Tokenizer;
|
||||
|
||||
Tokenizer::Tokens lineTokens;
|
||||
Tokenizer::Tokens csvTokens;
|
||||
Tokenizer tokenizer;
|
||||
|
||||
public:
|
||||
FastCSV(std::istream& str_, bool readHeader_ = true);
|
||||
|
||||
void readHeader ();
|
||||
|
||||
public:
|
||||
Result operator[](std::size_t index) const;
|
||||
|
||||
Segment segment(std::size_t index) const;
|
||||
|
||||
std::size_t index(const Segment &index) const;
|
||||
|
||||
Result operator[](const Segment &index) const;
|
||||
|
||||
std::size_t size() const;
|
||||
|
||||
bool readNextRow();
|
||||
|
||||
protected:
|
||||
std::istream& str;
|
||||
std::map<Segment, size_t> m_header;
|
||||
std::vector<Segment> m_data;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
} // namespace
|
||||
|
||||
16
tjp/core/csv/_tests/Csv.cpp
Normal file
16
tjp/core/csv/_tests/Csv.cpp
Normal file
@@ -0,0 +1,16 @@
|
||||
// TJP COPYRIGHT HEADER
|
||||
|
||||
#include <tjp/core/csv/Csv.h>
|
||||
|
||||
using namespace tjp::core;
|
||||
|
||||
void compile()
|
||||
{
|
||||
std::ifstream file("plop.csv");
|
||||
|
||||
CSV csv(file);
|
||||
while(csv.readNextRow())
|
||||
{
|
||||
std::cout << "4th Element(" << csv[3] << ")\n";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user