13 Star 69 Fork 22

johnsonyl/cpps

加入 Gitee
与超过 1400万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
文件
克隆/下载
csv.h 60.25 KB
一键复制 编辑 原始数据 按行查看 历史
johnsonyl 提交于 2021-02-18 14:08 +08:00 . 2021-02-18 更新
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456
// Copyright: (2012-2015) Ben Strasser <code@ben-strasser.net>
// License: BSD-3
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// 2. Redistributions in binary form must reproduce the above copyright notice,
// this list of conditions and the following disclaimer in the documentation
// and/or other materials provided with the distribution.
//
// 3. Neither the name of the copyright holder nor the names of its contributors
// may be used to endorse or promote products derived from this software
// without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
// POSSIBILITY OF SUCH DAMAGE.
#ifndef CSV_H
#define CSV_H
#include <vector>
#include <string>
#include <cstring>
#include <algorithm>
#include <utility>
#include <cstdio>
#include <exception>
#ifndef CSV_IO_NO_THREAD
#include <mutex>
#include <thread>
#include <condition_variable>
#endif
#include <memory>
#include <cassert>
#include <cerrno>
#include <istream>
#include <cpps/cpps.h>
namespace cpps{
////////////////////////////////////////////////////////////////////////////
// LineReader //
////////////////////////////////////////////////////////////////////////////
namespace csv_error{
struct base : std::exception{
virtual void format_error_message()const = 0;
const char*what()const noexcept override{
format_error_message();
return error_message_buffer;
}
mutable char error_message_buffer[512];
};
const int max_file_name_length = 255;
struct with_file_name{
with_file_name(){
std::memset(file_name, 0, sizeof(file_name));
}
void set_file_name(const char*file_name){
if(file_name != nullptr){
// This call to strncpy has parenthesis around it
// to silence the GCC -Wstringop-truncation warning
(strncpy(this->file_name, file_name, sizeof(this->file_name)));
this->file_name[sizeof(this->file_name)-1] = '\0';
}else{
this->file_name[0] = '\0';
}
}
char file_name[max_file_name_length+1];
};
struct with_file_line{
with_file_line(){
file_line = -1;
}
void set_file_line(int file_line){
this->file_line = file_line;
}
int file_line;
};
struct with_errno{
with_errno(){
errno_value = 0;
}
void set_errno(int errno_value){
this->errno_value = errno_value;
}
int errno_value;
};
struct can_not_open_file :
base,
with_file_name,
with_errno{
void format_error_message()const override{
if(errno_value != 0)
std::snprintf(error_message_buffer, sizeof(error_message_buffer),
"Can not open file \"%s\" because \"%s\"."
, file_name, std::strerror(errno_value));
else
std::snprintf(error_message_buffer, sizeof(error_message_buffer),
"Can not open file \"%s\"."
, file_name);
}
};
struct line_length_limit_exceeded :
base,
with_file_name,
with_file_line{
void format_error_message()const override{
std::snprintf(error_message_buffer, sizeof(error_message_buffer),
"Line number %d in file \"%s\" exceeds the maximum length of 2^24-1."
, file_line, file_name);
}
};
}
class ByteSourceBase{
public:
virtual int read(char*buffer, int size)=0;
virtual ~ByteSourceBase(){}
};
namespace detail{
class OwningStdIOByteSourceBase : public ByteSourceBase{
public:
explicit OwningStdIOByteSourceBase(FILE*file):file(file){
// Tell the std library that we want to do the buffering ourself.
std::setvbuf(file, 0, _IONBF, 0);
}
int read(char*buffer, int size){
return (int)std::fread(buffer, 1, size, file);
}
~OwningStdIOByteSourceBase(){
std::fclose(file);
}
private:
FILE*file;
};
class NonOwningIStreamByteSource : public ByteSourceBase{
public:
explicit NonOwningIStreamByteSource(std::istream&in):in(in){}
int read(char*buffer, int size){
in.read(buffer, size);
return (int)in.gcount();
}
~NonOwningIStreamByteSource(){}
private:
std::istream&in;
};
class NonOwningStringByteSource : public ByteSourceBase{
public:
NonOwningStringByteSource(const char*str, long long size):str(str), remaining_byte_count(size){}
int read(char*buffer, int desired_byte_count){
int to_copy_byte_count = desired_byte_count;
if(remaining_byte_count < to_copy_byte_count)
to_copy_byte_count = (int)remaining_byte_count;
std::memcpy(buffer, str, to_copy_byte_count);
remaining_byte_count -= to_copy_byte_count;
str += to_copy_byte_count;
return to_copy_byte_count;
}
~NonOwningStringByteSource(){}
private:
const char*str;
long long remaining_byte_count;
};
#ifndef CSV_IO_NO_THREAD
class AsynchronousReader{
public:
void init(std::unique_ptr<ByteSourceBase>arg_byte_source){
std::unique_lock<std::mutex>guard(lock);
byte_source = std::move(arg_byte_source);
desired_byte_count = -1;
termination_requested = false;
worker = std::thread(
[&]{
std::unique_lock<std::mutex>guard(lock);
try{
for(;;){
read_requested_condition.wait(
guard,
[&]{
return desired_byte_count != -1 || termination_requested;
}
);
if(termination_requested)
return;
read_byte_count = byte_source->read(buffer, desired_byte_count);
desired_byte_count = -1;
if(read_byte_count == 0)
break;
read_finished_condition.notify_one();
}
}catch(...){
read_error = std::current_exception();
}
read_finished_condition.notify_one();
}
);
}
bool is_valid()const{
return byte_source != nullptr;
}
void start_read(char*arg_buffer, int arg_desired_byte_count){
std::unique_lock<std::mutex>guard(lock);
buffer = arg_buffer;
desired_byte_count = arg_desired_byte_count;
read_byte_count = -1;
read_requested_condition.notify_one();
}
int finish_read(){
std::unique_lock<std::mutex>guard(lock);
read_finished_condition.wait(
guard,
[&]{
return read_byte_count != -1 || read_error;
}
);
if(read_error)
std::rethrow_exception(read_error);
else
return read_byte_count;
}
~AsynchronousReader(){
if(byte_source != nullptr){
{
std::unique_lock<std::mutex>guard(lock);
termination_requested = true;
}
read_requested_condition.notify_one();
worker.join();
}
}
private:
std::unique_ptr<ByteSourceBase>byte_source;
std::thread worker;
bool termination_requested;
std::exception_ptr read_error;
char*buffer;
int desired_byte_count;
int read_byte_count;
std::mutex lock;
std::condition_variable read_finished_condition;
std::condition_variable read_requested_condition;
};
#endif
class SynchronousReader{
public:
void init(std::unique_ptr<ByteSourceBase>arg_byte_source){
byte_source = std::move(arg_byte_source);
}
bool is_valid()const{
return byte_source != nullptr;
}
void start_read(char*arg_buffer, int arg_desired_byte_count){
buffer = arg_buffer;
desired_byte_count = arg_desired_byte_count;
}
int finish_read(){
return byte_source->read(buffer, desired_byte_count);
}
private:
std::unique_ptr<ByteSourceBase>byte_source;
char*buffer;
int desired_byte_count;
};
}
class LineReader{
private:
static const int block_len = 1<<20;
std::unique_ptr<char[]>buffer; // must be constructed before (and thus destructed after) the reader!
#ifdef CSV_IO_NO_THREAD
detail::SynchronousReader reader;
#else
detail::AsynchronousReader reader;
#endif
int data_begin;
int data_end;
char file_name[csv_error::max_file_name_length+1];
unsigned file_line;
static std::unique_ptr<ByteSourceBase> open_file(const char*file_name){
// We open the file in binary mode as it makes no difference under *nix
// and under Windows we handle \r\n newlines ourself.
FILE*file = std::fopen(file_name, "rb");
if(file == 0){
int x = errno; // store errno as soon as possible, doing it after constructor call can fail.
csv_error::can_not_open_file err;
err.set_errno(x);
err.set_file_name(file_name);
throw err;
}
return std::unique_ptr<ByteSourceBase>(new detail::OwningStdIOByteSourceBase(file));
}
void init(std::unique_ptr<ByteSourceBase>byte_source){
file_line = 0;
buffer = std::unique_ptr<char[]>(new char[3*block_len]);
data_begin = 0;
data_end = byte_source->read(buffer.get(), 2*block_len);
// Ignore UTF-8 BOM
if(data_end >= 3 && buffer[0] == '\xEF' && buffer[1] == '\xBB' && buffer[2] == '\xBF')
data_begin = 3;
if(data_end == 2*block_len){
reader.init(std::move(byte_source));
reader.start_read(buffer.get() + 2*block_len, block_len);
}
}
public:
LineReader() = delete;
LineReader(const LineReader&) = delete;
LineReader&operator=(const LineReader&) = delete;
explicit LineReader(const char*file_name){
set_file_name(file_name);
init(open_file(file_name));
}
explicit LineReader(const std::string&file_name){
set_file_name(file_name.c_str());
init(open_file(file_name.c_str()));
}
LineReader(const char*file_name, std::unique_ptr<ByteSourceBase>byte_source){
set_file_name(file_name);
init(std::move(byte_source));
}
LineReader(const std::string&file_name, std::unique_ptr<ByteSourceBase>byte_source){
set_file_name(file_name.c_str());
init(std::move(byte_source));
}
LineReader(const char*file_name, const char*data_begin, const char*data_end){
set_file_name(file_name);
init(std::unique_ptr<ByteSourceBase>(new detail::NonOwningStringByteSource(data_begin, data_end-data_begin)));
}
LineReader(const std::string&file_name, const char*data_begin, const char*data_end){
set_file_name(file_name.c_str());
init(std::unique_ptr<ByteSourceBase>(new detail::NonOwningStringByteSource(data_begin, data_end-data_begin)));
}
LineReader(const char*file_name, FILE*file){
set_file_name(file_name);
init(std::unique_ptr<ByteSourceBase>(new detail::OwningStdIOByteSourceBase(file)));
}
LineReader(const std::string&file_name, FILE*file){
set_file_name(file_name.c_str());
init(std::unique_ptr<ByteSourceBase>(new detail::OwningStdIOByteSourceBase(file)));
}
LineReader(const char*file_name, std::istream&in){
set_file_name(file_name);
init(std::unique_ptr<ByteSourceBase>(new detail::NonOwningIStreamByteSource(in)));
}
LineReader(const std::string&file_name, std::istream&in){
set_file_name(file_name.c_str());
init(std::unique_ptr<ByteSourceBase>(new detail::NonOwningIStreamByteSource(in)));
}
void set_file_name(const std::string&file_name){
set_file_name(file_name.c_str());
}
void set_file_name(const char*file_name){
if(file_name != nullptr){
strncpy(this->file_name, file_name, sizeof(this->file_name)-1);
this->file_name[sizeof(this->file_name)-1] = '\0';
}else{
this->file_name[0] = '\0';
}
}
const char*get_truncated_file_name()const{
return file_name;
}
void set_file_line(unsigned file_line){
this->file_line = file_line;
}
unsigned get_file_line()const{
return file_line;
}
char*next_line(){
if(data_begin == data_end)
return nullptr;
++file_line;
assert(data_begin < data_end);
assert(data_end <= block_len*2);
if(data_begin >= block_len){
std::memcpy(buffer.get(), buffer.get()+block_len, block_len);
data_begin -= block_len;
data_end -= block_len;
if(reader.is_valid())
{
data_end += reader.finish_read();
std::memcpy(buffer.get()+block_len, buffer.get()+2*block_len, block_len);
reader.start_read(buffer.get() + 2*block_len, block_len);
}
}
int line_end = data_begin;
while(buffer[line_end] != '\n' && line_end != data_end){
++line_end;
}
if(line_end - data_begin + 1 > block_len){
csv_error::line_length_limit_exceeded err;
err.set_file_name(file_name);
err.set_file_line(file_line);
throw err;
}
if(buffer[line_end] == '\n' && line_end != data_end){
buffer[size_t(line_end)] = '\0';
}else{
// some files are missing the newline at the end of the
// last line
++data_end;
buffer[size_t(line_end)] = '\0';
}
// handle windows \r\n-line breaks
if(line_end != data_begin && buffer[size_t(size_t(line_end)-1)] == '\r')
buffer[size_t(size_t(line_end) - 1)] = '\0';
char*ret = buffer.get() + data_begin;
data_begin = line_end+1;
return ret;
}
};
////////////////////////////////////////////////////////////////////////////
// CSV //
////////////////////////////////////////////////////////////////////////////
namespace csv_error{
const int max_column_name_length = 63;
struct with_column_name{
with_column_name(){
std::memset(column_name, 0, max_column_name_length+1);
}
void set_column_name(const char*column_name){
if(column_name != nullptr){
std::strncpy(this->column_name, column_name, max_column_name_length);
this->column_name[max_column_name_length] = '\0';
}else{
this->column_name[0] = '\0';
}
}
char column_name[max_column_name_length+1];
};
const int max_column_content_length = 63;
struct with_column_content{
with_column_content(){
std::memset(column_content, 0, max_column_content_length+1);
}
void set_column_content(const char*column_content){
if(column_content != nullptr){
std::strncpy(this->column_content, column_content, max_column_content_length);
this->column_content[max_column_content_length] = '\0';
}else{
this->column_content[0] = '\0';
}
}
char column_content[max_column_content_length+1];
};
struct extra_column_in_header :
base,
with_file_name,
with_column_name{
void format_error_message()const override{
std::snprintf(error_message_buffer, sizeof(error_message_buffer),
R"(Extra column "%s" in header of file "%s".)"
, column_name, file_name);
}
};
struct missing_column_in_header :
base,
with_file_name,
with_column_name{
void format_error_message()const override{
std::snprintf(error_message_buffer, sizeof(error_message_buffer),
R"(Missing column "%s" in header of file "%s".)"
, column_name, file_name);
}
};
struct duplicated_column_in_header :
base,
with_file_name,
with_column_name{
void format_error_message()const override{
std::snprintf(error_message_buffer, sizeof(error_message_buffer),
R"(Duplicated column "%s" in header of file "%s".)"
, column_name, file_name);
}
};
struct header_missing :
base,
with_file_name{
void format_error_message()const override{
std::snprintf(error_message_buffer, sizeof(error_message_buffer),
"Header missing in file \"%s\"."
, file_name);
}
};
struct too_few_columns :
base,
with_file_name,
with_file_line{
void format_error_message()const override{
std::snprintf(error_message_buffer, sizeof(error_message_buffer),
"Too few columns in line %d in file \"%s\"."
, file_line, file_name);
}
};
struct too_many_columns :
base,
with_file_name,
with_file_line{
void format_error_message()const override{
std::snprintf(error_message_buffer, sizeof(error_message_buffer),
"Too many columns in line %d in file \"%s\"."
, file_line, file_name);
}
};
struct escaped_string_not_closed :
base,
with_file_name,
with_file_line{
void format_error_message()const override{
std::snprintf(error_message_buffer, sizeof(error_message_buffer),
"Escaped string was not closed in line %d in file \"%s\"."
, file_line, file_name);
}
};
struct integer_must_be_positive :
base,
with_file_name,
with_file_line,
with_column_name,
with_column_content{
void format_error_message()const override{
std::snprintf(error_message_buffer, sizeof(error_message_buffer),
R"(The integer "%s" must be positive or 0 in column "%s" in file "%s" in line "%d".)"
, column_content, column_name, file_name, file_line);
}
};
struct no_digit :
base,
with_file_name,
with_file_line,
with_column_name,
with_column_content{
void format_error_message()const override{
std::snprintf(error_message_buffer, sizeof(error_message_buffer),
R"(The integer "%s" contains an invalid digit in column "%s" in file "%s" in line "%d".)"
, column_content, column_name, file_name, file_line);
}
};
struct integer_overflow :
base,
with_file_name,
with_file_line,
with_column_name,
with_column_content{
void format_error_message()const override{
std::snprintf(error_message_buffer, sizeof(error_message_buffer),
R"(The integer "%s" overflows in column "%s" in file "%s" in line "%d".)"
, column_content, column_name, file_name, file_line);
}
};
struct integer_underflow :
base,
with_file_name,
with_file_line,
with_column_name,
with_column_content{
void format_error_message()const override{
std::snprintf(error_message_buffer, sizeof(error_message_buffer),
R"(The integer "%s" underflows in column "%s" in file "%s" in line "%d".)"
, column_content, column_name, file_name, file_line);
}
};
struct invalid_single_character :
base,
with_file_name,
with_file_line,
with_column_name,
with_column_content{
void format_error_message()const override{
std::snprintf(error_message_buffer, sizeof(error_message_buffer),
R"(The content "%s" of column "%s" in file "%s" in line "%d" is not a single character.)"
, column_content, column_name, file_name, file_line);
}
};
}
using ignore_column = unsigned int;
static const ignore_column ignore_no_column = 0;
static const ignore_column ignore_extra_column = 1;
static const ignore_column ignore_missing_column = 2;
template<char ... trim_char_list>
struct trim_chars{
private:
constexpr static bool is_trim_char(char){
return false;
}
template<class ...OtherTrimChars>
constexpr static bool is_trim_char(char c, char trim_char, OtherTrimChars...other_trim_chars){
return c == trim_char || is_trim_char(c, other_trim_chars...);
}
public:
static void trim(char*&str_begin, char*&str_end){
while(str_begin != str_end && is_trim_char(*str_begin, trim_char_list...))
++str_begin;
while(str_begin != str_end && is_trim_char(*(str_end-1), trim_char_list...))
--str_end;
*str_end = '\0';
}
};
struct no_comment{
static bool is_comment(const char*){
return false;
}
};
template<char ... comment_start_char_list>
struct single_line_comment{
private:
constexpr static bool is_comment_start_char(char){
return false;
}
template<class ...OtherCommentStartChars>
constexpr static bool is_comment_start_char(char c, char comment_start_char, OtherCommentStartChars...other_comment_start_chars){
return c == comment_start_char || is_comment_start_char(c, other_comment_start_chars...);
}
public:
static bool is_comment(const char*line){
return is_comment_start_char(*line, comment_start_char_list...);
}
};
struct empty_line_comment{
static bool is_comment(const char*line){
if(*line == '\0')
return true;
while(*line == ' ' || *line == '\t'){
++line;
if(*line == 0)
return true;
}
return false;
}
};
template<char ... comment_start_char_list>
struct single_and_empty_line_comment{
static bool is_comment(const char*line){
return single_line_comment<comment_start_char_list...>::is_comment(line) || empty_line_comment::is_comment(line);
}
};
template<char sep>
struct no_quote_escape{
static const char*find_next_column_end(const char*col_begin){
while(*col_begin != sep && *col_begin != '\0')
++col_begin;
return col_begin;
}
static void unescape(char*&, char*&){
}
};
template<char sep, char quote>
struct double_quote_escape{
static const char*find_next_column_end(const char*col_begin){
while(*col_begin != sep && *col_begin != '\0')
if(*col_begin != quote)
++col_begin;
else{
do{
++col_begin;
while(*col_begin != quote){
if(*col_begin == '\0')
throw csv_error::escaped_string_not_closed();
++col_begin;
}
++col_begin;
}while(*col_begin == quote);
}
return col_begin;
}
static void unescape(char*&col_begin, char*&col_end){
if(col_end - col_begin >= 2){
if(*col_begin == quote && *(col_end-1) == quote){
++col_begin;
--col_end;
char*out = col_begin;
for(char*in = col_begin; in!=col_end; ++in){
if(*in == quote && (in+1) != col_end && *(in+1) == quote){
++in;
}
*out = *in;
++out;
}
col_end = out;
*col_end = '\0';
}
}
}
};
struct throw_on_overflow{
template<class T>
static void on_overflow(T&){
throw csv_error::integer_overflow();
}
template<class T>
static void on_underflow(T&){
throw csv_error::integer_underflow();
}
};
struct ignore_overflow{
template<class T>
static void on_overflow(T&){}
template<class T>
static void on_underflow(T&){}
};
struct set_to_max_on_overflow{
template<class T>
static void on_overflow(T&x){
// using (std::numeric_limits<T>::max) instead of std::numeric_limits<T>::max
// to make code including windows.h with its max macro happy
x = (std::numeric_limits<T>::max)();
}
template<class T>
static void on_underflow(T&x){
x = (std::numeric_limits<T>::min)();
}
};
namespace detail{
template<class quote_policy>
void chop_next_column(
char*&line, char*&col_begin, char*&col_end
){
assert(line != nullptr);
col_begin = line;
// the col_begin + (... - col_begin) removes the constness
col_end = col_begin + (quote_policy::find_next_column_end(col_begin) - col_begin);
if(*col_end == '\0'){
line = nullptr;
}else{
*col_end = '\0';
line = col_end + 1;
}
}
template<class trim_policy, class quote_policy>
void parse_line(
char*line,
char**sorted_col,
const std::vector<int>&col_order
){
for (int i : col_order) {
if(line == nullptr)
throw ::cpps::csv_error::too_few_columns();
char*col_begin, *col_end;
chop_next_column<quote_policy>(line, col_begin, col_end);
if (i != -1) {
trim_policy::trim(col_begin, col_end);
quote_policy::unescape(col_begin, col_end);
sorted_col[i] = col_begin;
}
}
if(line != nullptr)
throw ::cpps::csv_error::too_many_columns();
}
template< class trim_policy, class quote_policy>
void parse_header_line(
unsigned column_count,
char*line,
std::vector<int>&col_order,
const std::string*col_name,
ignore_column ignore_policy
){
col_order.clear();
std::vector<bool> found; found.resize(column_count);
for (auto it = found.begin(); it != found.end(); ++it) *it = false;
while(line){
char*col_begin,*col_end;
chop_next_column<quote_policy>(line, col_begin, col_end);
trim_policy::trim(col_begin, col_end);
quote_policy::unescape(col_begin, col_end);
for(unsigned i=0; i<column_count; ++i)
if(col_begin == col_name[i]){
if(found[i]){
csv_error::duplicated_column_in_header err;
err.set_column_name(col_begin);
throw err;
}
found[i] = true;
col_order.push_back(i);
col_begin = 0;
break;
}
if(col_begin){
if(ignore_policy & ::cpps::ignore_extra_column)
col_order.push_back(-1);
else{
csv_error::extra_column_in_header err;
err.set_column_name(col_begin);
throw err;
}
}
}
if(!(ignore_policy & ::cpps::ignore_missing_column)){
for(unsigned i=0; i<column_count; ++i){
if(!found[i]){
csv_error::missing_column_in_header err;
err.set_column_name(col_name[i].c_str());
throw err;
}
}
}
}
template<class overflow_policy>
void parse(char*col, char &x){
if(!*col)
throw csv_error::invalid_single_character();
x = *col;
++col;
if(*col)
throw csv_error::invalid_single_character();
}
template<class overflow_policy>
void parse(char*col, std::string&x){
x = col;
}
template<class overflow_policy>
void parse(char*col, const char*&x){
x = col;
}
template<class overflow_policy>
void parse(char*col, char*&x){
x = col;
}
template<class overflow_policy, class T>
void parse_unsigned_integer(const char*col, T&x){
x = 0;
while(*col != '\0'){
if('0' <= *col && *col <= '9'){
T y = *col - '0';
if(x > ((std::numeric_limits<T>::max)()-y)/10){
overflow_policy::on_overflow(x);
return;
}
x = 10*x+y;
}else
throw csv_error::no_digit();
++col;
}
}
template<class overflow_policy>void parse(char*col, unsigned char &x)
{parse_unsigned_integer<overflow_policy>(col, x);}
template<class overflow_policy>void parse(char*col, unsigned short &x)
{parse_unsigned_integer<overflow_policy>(col, x);}
template<class overflow_policy>void parse(char*col, unsigned int &x)
{parse_unsigned_integer<overflow_policy>(col, x);}
template<class overflow_policy>void parse(char*col, unsigned long &x)
{parse_unsigned_integer<overflow_policy>(col, x);}
template<class overflow_policy>void parse(char*col, unsigned long long &x)
{parse_unsigned_integer<overflow_policy>(col, x);}
template<class overflow_policy, class T>
void parse_signed_integer(const char*col, T&x){
if(*col == '-'){
++col;
x = 0;
while(*col != '\0'){
if('0' <= *col && *col <= '9'){
T y = *col - '0';
if(x < ((std::numeric_limits<T>::min)()+y)/10){
overflow_policy::on_underflow(x);
return;
}
x = 10*x-y;
}else
throw csv_error::no_digit();
++col;
}
return;
}else if(*col == '+')
++col;
parse_unsigned_integer<overflow_policy>(col, x);
}
template<class overflow_policy>void parse(char*col, signed char &x)
{parse_signed_integer<overflow_policy>(col, x);}
template<class overflow_policy>void parse(char*col, signed short &x)
{parse_signed_integer<overflow_policy>(col, x);}
template<class overflow_policy>void parse(char*col, signed int &x)
{parse_signed_integer<overflow_policy>(col, x);}
template<class overflow_policy>void parse(char*col, signed long &x)
{parse_signed_integer<overflow_policy>(col, x);}
template<class overflow_policy>void parse(char*col, signed long long &x)
{parse_signed_integer<overflow_policy>(col, x);}
template<class T>
void parse_float(const char*col, T&x){
bool is_neg = false;
if(*col == '-'){
is_neg = true;
++col;
}else if(*col == '+')
++col;
x = 0;
while('0' <= *col && *col <= '9'){
int y = *col - '0';
x *= 10;
x += y;
++col;
}
if(*col == '.'|| *col == ','){
++col;
T pos = 1;
while('0' <= *col && *col <= '9'){
pos /= 10;
int y = *col - '0';
++col;
x += y*pos;
}
}
if(*col == 'e' || *col == 'E'){
++col;
int e;
parse_signed_integer<set_to_max_on_overflow>(col, e);
if(e != 0){
T base;
if(e < 0){
base = T(0.1);
e = -e;
}else{
base = T(10);
}
while(e != 1){
if((e & 1) == 0){
base = base*base;
e >>= 1;
}else{
x *= base;
--e;
}
}
x *= base;
}
}else{
if(*col != '\0')
throw csv_error::no_digit();
}
if(is_neg)
x = -x;
}
template<class overflow_policy> void parse(char*col, float&x) { parse_float(col, x); }
template<class overflow_policy> void parse(char*col, double&x) { parse_float(col, x); }
template<class overflow_policy> void parse(char*col, long double&x) { parse_float(col, x); }
template<class overflow_policy, class T>
void parse(char*col, T&x){
// Mute unused variable compiler warning
(void)col;
(void)x;
// GCC evalutes "false" when reading the template and
// "sizeof(T)!=sizeof(T)" only when instantiating it. This is why
// this strange construct is used.
static_assert(sizeof(T)!=sizeof(T),
"Can not parse this type. Only buildin integrals, floats, char, char*, const char* and std::string are supported");
}
}
template<
class trim_policy = trim_chars<' ', '\t'>,
class quote_policy = no_quote_escape<','>,
class overflow_policy = throw_on_overflow,
class comment_policy = no_comment
>
class CSVReader{
private:
LineReader in;
unsigned column_count;
char**row;
std::string *column_names;
std::vector<int>col_order;
template<class ...ColNames>
void set_column_names(std::string s, ColNames...cols){
column_names[column_count-sizeof...(ColNames)-1] = std::move(s);
set_column_names(std::forward<ColNames>(cols)...);
}
void set_column_names(){}
public:
CSVReader() = delete;
CSVReader(const CSVReader&) = delete;
CSVReader&operator=(const CSVReader&);
~CSVReader() {
if (row) {
delete[] row;
row = NULL;
}
if (column_names) {
delete[] column_names;
column_names = NULL;
}
col_order.clear();
}
void init(unsigned count) {
if (row) return;
column_count = count;
row = new char* [column_count];
column_names = new std::string[column_count];
std::fill(row, row + column_count, nullptr);
col_order.resize(column_count);
for (unsigned i = 0; i < column_count; ++i)
col_order[i] = i;
for (unsigned i = 1; i <= column_count; ++i)
column_names[i - 1] = "col" + std::to_string(i);
}
template<class ...Args>
explicit CSVReader(Args&&...args):in(std::forward<Args>(args)...){
row = NULL;
column_count = 0;
column_names = NULL;
}
char*next_line(){
return in.next_line();
}
void read_header(ignore_column ignore_policy, std::vector<std::string> &list){
init((cpps::usint32)list.size());
try{
size_t idx = 0;
for (auto &s : list){
column_names[idx++] = s;
}
char*line;
do{
line = in.next_line();
if(!line)
throw csv_error::header_missing();
}while(comment_policy::is_comment(line));
detail::parse_header_line
< trim_policy, quote_policy>
(column_count,line, col_order, column_names, ignore_policy);
}catch(csv_error::with_file_name&err){
err.set_file_name(in.get_truncated_file_name());
throw;
}
}
template<class ...ColNames>
void read_header(ignore_column ignore_policy, ColNames...cols){
init((cpps::usint32)(sizeof...(ColNames)));
try{
set_column_names(std::forward<ColNames>(cols)...);
char*line;
do{
line = in.next_line();
if(!line)
throw csv_error::header_missing();
}while(comment_policy::is_comment(line));
detail::parse_header_line
< trim_policy, quote_policy>
(column_count,line, col_order, column_names, ignore_policy);
}catch(csv_error::with_file_name&err){
err.set_file_name(in.get_truncated_file_name());
throw;
}
}
void set_header(std::vector<std::string>& list){
init((cpps::usint32)list.size());
size_t idx = 0;
for (auto& s : list) {
column_names[idx++] = s;
}
std::fill(row, row + column_count, nullptr);
col_order.resize(column_count);
for (unsigned i = 0; i < column_count; ++i)
col_order[i] = i;
}
template<class ...ColNames>
void set_header(ColNames...cols) {
init((cpps::usint32)(sizeof...(ColNames)));
set_column_names(std::forward<ColNames>(cols)...);
std::fill(row, row + column_count, nullptr);
col_order.resize(column_count);
for (unsigned i = 0; i < column_count; ++i)
col_order[i] = i;
}
bool has_column(const std::string&name) const {
for (size_t i = 0; i < column_count; i++) {
if (column_names[i] == name) {
return std::end(col_order) != std::find(std::begin(col_order), std::end(col_order), i);
}
}
return false;
}
void set_file_name(const std::string&file_name){
in.set_file_name(file_name);
}
void set_file_name(const char*file_name){
in.set_file_name(file_name);
}
const char*get_truncated_file_name()const{
return in.get_truncated_file_name();
}
void set_file_line(unsigned file_line){
in.set_file_line(file_line);
}
unsigned get_file_line()const{
return in.get_file_line();
}
private:
void parse_helper(std::size_t){}
template<class T, class ...ColType>
void parse_helper(std::size_t r, T&t, ColType&...cols){
if(row[r]){
try{
try{
::cpps::detail::parse<overflow_policy>(row[r], t);
}catch(csv_error::with_column_content&err){
err.set_column_content(row[r]);
throw;
}
}catch(csv_error::with_column_name&err){
err.set_column_name(column_names[r].c_str());
throw;
}
}
parse_helper(r+1, cols...);
}
void parse_helper(std::size_t r, std::vector<std::string> &list) {
if (r < column_count && row[r]) {
try {
try {
::cpps::detail::parse<overflow_policy>(row[r], list[r]);
}
catch (csv_error::with_column_content& err) {
err.set_column_content(row[r]);
throw;
}
}
catch (csv_error::with_column_name& err) {
err.set_column_name(column_names[r].c_str());
throw;
}
parse_helper(r + 1, list);
}
}
public:
bool read_row(std::vector<std::string> &list) {
try {
try {
char* line;
do {
line = in.next_line();
if (!line)
return false;
} while (comment_policy::is_comment(line));
detail::parse_line<trim_policy, quote_policy>
(line, row, col_order);
list.resize(column_count);
parse_helper(0, list);
}
catch (csv_error::with_file_name& err) {
err.set_file_name(in.get_truncated_file_name());
throw;
}
}
catch (csv_error::with_file_line& err) {
err.set_file_line(in.get_file_line());
throw;
}
return true;
}
template<class ...ColType>
bool read_row(ColType& ...cols) {
static_assert(sizeof...(ColType) >= column_count,
"not enough columns specified");
static_assert(sizeof...(ColType) <= column_count,
"too many columns specified");
try {
try {
char* line;
do {
line = in.next_line();
if (!line)
return false;
} while (comment_policy::is_comment(line));
detail::parse_line<trim_policy, quote_policy>
(line, row, col_order);
parse_helper(0, cols...);
}
catch (csv_error::with_file_name& err) {
err.set_file_name(in.get_truncated_file_name());
throw;
}
}
catch (csv_error::with_file_line& err) {
err.set_file_line(in.get_file_line());
throw;
}
return true;
}
};
}
//bindding
namespace cpps {
struct cpps_csvreader
{
virtual ~cpps_csvreader() {
close();
}
void open(std::string csvpath) {
reader = new CSVReader<>(csvpath.c_str());
}
void read_header(C*c,cpps::object headers,object _ignore_column)
{
ignore_column v = ignore_extra_column;
if (_ignore_column.isint()) v = (ignore_column) _ignore_column.toint();
std::vector<std::string> headers_vct;
for (cpps::object cv : cpps::object::vector(headers)) {
headers_vct.push_back(cv.tostring());
}
try{
reader->read_header(v, headers_vct);
}
catch (...){
cpps::error(c, "csverror");
}
}
bool has_column(std::string columnname) {
return reader->has_column(columnname);
}
void write_header(C* c, cpps::object headers)
{
std::vector<std::string> headers_vct;
for (cpps::object cv : cpps::object::vector(headers)) {
headers_vct.push_back(cv.tostring());
}
try {
reader->set_header(headers_vct);
}
catch (...) {
cpps::error(c, "csverror");
}
}
cpps_integer get_file_line() {
return (cpps_integer)reader->get_file_line();
}
void set_file_line(cpps_integer line) {
reader->set_file_line((usint32)line);
}
cpps::tuple read_row(C*c)
{
std::vector<std::string> row_values;
bool has = reader->read_row(row_values);
cpps::tuple ret;
ret.push_back(object::create(c, has));
for (auto& v : row_values)
ret.push_back(object::create(c, v));
return ret;
}
void close() {
if (reader) {
delete reader;
reader = NULL;
}
}
CSVReader<> *reader;
};
}
#endif
Loading...
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
C++
1
https://gitee.com/cppscript/cpps.git
git@gitee.com:cppscript/cpps.git
cppscript
cpps
cpps
master

搜索帮助