#include "squirrel.h" #include SQ_OPT_STRING_STRLEN(); #include #include #include #include /// loads a CSV record from the stream is /// /// * leading and trailing white space is removed outside of // quoted sections when trimWhiteSpace is true /// * line breaks are preserved in quoted sections /// * quote literals consist of two adjacent quote characters /// * quote literals must be in quoted sections /// /// input stream for CSV records /// trims white space on unquoted fields /// field delimiter. defaults to ',' for CSV /// record delimiter. defaults to '\n' for CSV /// delimiter for quoted fields. defaults to '"' /// a list of fields in the record std::vector CsvGetLine(std::istream& is, bool trimWhiteSpace=true, const char fieldDelim=',', const char recordDelim='\n', const char quote='"') { using namespace std; vector record; // result record list. default empty string field; // temporary field construction zone int start = -1, // start of a quoted section for trimming end = -1; // end of a quoted section for trimming char ch; while (is.get(ch)) { if (ch == fieldDelim || ch == recordDelim) // fieldDelim and recordDelim mark the end of a // field. save the field, reset for the next field, // and break if there are no more fields { if (trimWhiteSpace) // trim all external white space // exclude chars between start and end { const string wsList = " \t\n\f\v\r"; int ePos, sPos; // order dependency: right trim before let trim // left trim will invalidate end's index value if ((ePos = field.find_last_not_of(wsList)) != string::npos) { // ePos+1 because find_last_not_of stops on white space field.erase((end > ePos) ? end : ePos + 1); } if ((sPos = field.find_first_not_of(wsList)) != string::npos) { field.erase(0, (start != -1 && start < sPos) ? start : sPos); } // reset the quoted section start = end = -1; } // save the new field and reset the temporary record.push_back(field); field.clear(); // exit case 1: !is, managed by loop condition // exit case 2: recordDelim, managed here if (ch == recordDelim) break; } else if (ch == quote) { // save the start of the quoted section start = field.length(); while (is.get(ch)) { if (ch == '"') { // consecutive quotes are an escaped quote literal // only applies in quoted fields // 'a""b""c' becomes 'abc' // 'a"""b"""c' becomes 'a"b"c' // '"a""b""c"' becomes 'a"b"c' if (is.peek() != '"') { // save the end of the quoted section end = field.length(); break; } else field.push_back(is.get()); } else field.push_back(ch); } } else field.push_back(ch); } return record; } static const SQChar sq_parse_csv_TAG[] = _SC("ParseCSV"); static SQRESULT sq_parse_csv_release_hook(SQUserPointer p, SQInteger size, void */*ep*/) { std::ifstream *self = (std::ifstream *)p; if(self) delete self; return 0; } /* ** Creates a new ParseCSV. */ static SQRESULT sq_parse_csv_constructor (HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); SQ_GET_STRING(v, 2, fname); std::ifstream *self = new std::ifstream(fname); if(self->is_open()){ sq_setinstanceup(v, 1, self); sq_setreleasehook(v, 1, sq_parse_csv_release_hook); return 1; } delete self; return sq_throwerror(v, _SC("failed to open %s"), fname); } static SQRESULT sq_parse_csv_next_row(HSQUIRRELVM v) { SQ_FUNC_VARS_NO_TOP(v); SQ_GET_INSTANCE_VAR(v, 1, std::ifstream, self, sq_parse_csv_TAG); std::vector row_fields = CsvGetLine(*self); size_t rsize = row_fields.size(); if (rsize == 0) sq_pushnull(v); else { sq_newarray(v, rsize); for(size_t i=0; i