// 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));elsestd::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∈};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_THREADclass 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);elsereturn 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;};#endifclass 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_THREADdetail::SynchronousReader reader;#elsedetail::AsynchronousReader reader;#endifint 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 BOMif(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 breaksif(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 happyx = (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 constnesscol_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;}elsethrow 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;}elsethrow 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;}};}//binddingnamespace 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
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。
1. 开源生态
2. 协作、人、软件
3. 评估模型