Skip to content

Commit

Permalink
CSV Parser 2.1.2: July 26, 2021 Patch (#177)
Browse files Browse the repository at this point in the history
* Fix C++-14 compile issues

* CSV Parser should now build with std=c++11 on g++-7.5

* Fixed issues with floating point writing

* Fixed newlines not being escaped

* Update single header
  • Loading branch information
vincentlaucsb committed Jul 27, 2021
1 parent a9561e0 commit 39a6af6
Show file tree
Hide file tree
Showing 13 changed files with 332 additions and 187 deletions.
3 changes: 2 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ In addition to the [Features & Examples](#features--examples) below, a [fully-fl

## Integration

This library was developed with Microsoft Visual Studio and is compatible with >g++ 6.0 and clang.
This library was developed with Microsoft Visual Studio and is compatible with >g++ 7.5 and clang.
All of the code required to build this library, aside from the C++ standard library, is contained under `include/`.

### C++ Version
Expand Down Expand Up @@ -342,6 +342,7 @@ stringstream ss; // Can also use ofstream, etc.
auto writer = make_csv_writer(ss);
// auto writer = make_tsv_writer(ss); // For tab-separated files
// DelimWriter<stringstream, '|', '"'> writer(ss); // Your own custom format
// set_decimal_places(2); // How many places after the decimal will be written for floats

writer << vector<string>({ "A", "B", "C" })
<< deque<string>({ "I'm", "too", "tired" })
Expand Down
2 changes: 1 addition & 1 deletion include/csv.hpp
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
CSV for C++, version 2.1.0
CSV for C++, version 2.1.2
https://github.com/vincentlaucsb/csv-parser
MIT License
Expand Down
12 changes: 6 additions & 6 deletions include/internal/basic_csv_parser.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ namespace csv {
* ASCII number for a character and, v[i + 128] labels it according to
* the CSVReader::ParseFlags enum
*/
HEDLEY_CONST CONSTEXPR ParseFlagMap make_parse_flags(char delimiter) {
HEDLEY_CONST CONSTEXPR_17 ParseFlagMap make_parse_flags(char delimiter) {
std::array<ParseFlags, 256> ret = {};
for (int i = -128; i < 128; i++) {
const int arr_idx = i + 128;
Expand All @@ -48,7 +48,7 @@ namespace csv {
* ASCII number for a character and, v[i + 128] labels it according to
* the CSVReader::ParseFlags enum
*/
HEDLEY_CONST CONSTEXPR ParseFlagMap make_parse_flags(char delimiter, char quote_char) {
HEDLEY_CONST CONSTEXPR_17 ParseFlagMap make_parse_flags(char delimiter, char quote_char) {
std::array<ParseFlags, 256> ret = make_parse_flags(delimiter);
ret[(size_t)quote_char + 128] = ParseFlags::QUOTE;
return ret;
Expand All @@ -58,7 +58,7 @@ namespace csv {
* ASCII number for a character c and, v[i + 128] is true if
* c is a whitespace character
*/
HEDLEY_CONST CONSTEXPR WhitespaceMap make_ws_flags(const char* ws_chars, size_t n_chars) {
HEDLEY_CONST CONSTEXPR_14 WhitespaceMap make_ws_flags(const char* ws_chars, size_t n_chars) {
std::array<bool, 256> ret = {};
for (int i = -128; i < 128; i++) {
const int arr_idx = i + 128;
Expand Down Expand Up @@ -210,11 +210,11 @@ namespace csv {
/** Indicate the last block of data has been parsed */
void end_feed();

CONSTEXPR ParseFlags parse_flag(const char ch) const noexcept {
CONSTEXPR_17 ParseFlags parse_flag(const char ch) const noexcept {
return _parse_flags.data()[ch + 128];
}

CONSTEXPR ParseFlags compound_parse_flag(const char ch) const noexcept {
CONSTEXPR_17 ParseFlags compound_parse_flag(const char ch) const noexcept {
return quote_escape_flag(parse_flag(ch), this->quote_escape);
}

Expand Down Expand Up @@ -274,7 +274,7 @@ namespace csv {
/** Where complete rows should be pushed to */
RowCollection* _records = nullptr;

CONSTEXPR bool ws_flag(const char ch) const noexcept {
CONSTEXPR_17 bool ws_flag(const char ch) const noexcept {
return _ws_flags.data()[ch + 128];
}

Expand Down
32 changes: 21 additions & 11 deletions include/internal/common.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -74,31 +74,41 @@ namespace csv {
#endif

#ifdef CSV_HAS_CXX17
#define IF_CONSTEXPR if constexpr
#define CONSTEXPR_VALUE constexpr
#define IF_CONSTEXPR if constexpr
#define CONSTEXPR_VALUE constexpr

#define CONSTEXPR_17 constexpr
#else
#define IF_CONSTEXPR if
#define CONSTEXPR_VALUE const
#define IF_CONSTEXPR if
#define CONSTEXPR_VALUE const

#define CONSTEXPR_17 inline
#endif

#ifdef CSV_HAS_CXX14
template<bool B, class T = void>
using enable_if_t = std::enable_if_t<B, T>;

#define CONSTEXPR_14 constexpr
#define CONSTEXPR_VALUE_14 constexpr
#else
template<bool B, class T = void>
using enable_if_t = typename std::enable_if<B, T>::type;

#define CONSTEXPR_14 inline
#define CONSTEXPR_VALUE_14 const
#endif

// Resolves g++ bug with regard to constexpr methods
// See: https://stackoverflow.com/questions/36489369/constexpr-non-static-member-function-with-non-constexpr-constructor-gcc-clang-d
#if defined __GNUC__ && !defined __clang__
#if (__GNUC__ >= 7 &&__GNUC_MINOR__ >= 2) || (__GNUC__ >= 8)
#define CONSTEXPR constexpr
#endif
#else
#ifdef CSV_HAS_CXX17
#define CONSTEXPR constexpr
#endif
#if (__GNUC__ >= 7 &&__GNUC_MINOR__ >= 2) || (__GNUC__ >= 8)
#define CONSTEXPR constexpr
#endif
#else
#ifdef CSV_HAS_CXX17
#define CONSTEXPR constexpr
#endif
#endif

#ifndef CONSTEXPR
Expand Down
4 changes: 2 additions & 2 deletions include/internal/csv_format.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -93,13 +93,13 @@ namespace csv {
}

/** Tells the parser how to handle columns of a different length than the others */
CONSTEXPR CSVFormat& variable_columns(VariableColumnPolicy policy = VariableColumnPolicy::IGNORE_ROW) {
CONSTEXPR_14 CSVFormat& variable_columns(VariableColumnPolicy policy = VariableColumnPolicy::IGNORE_ROW) {
this->variable_column_policy = policy;
return *this;
}

/** Tells the parser how to handle columns of a different length than the others */
CONSTEXPR CSVFormat& variable_columns(bool policy) {
CONSTEXPR_14 CSVFormat& variable_columns(bool policy) {
this->variable_column_policy = (VariableColumnPolicy)policy;
return *this;
}
Expand Down
4 changes: 2 additions & 2 deletions include/internal/csv_reader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,10 +81,10 @@ namespace csv {
iterator(CSVReader*, CSVRow&&);

/** Access the CSVRow held by the iterator */
CONSTEXPR reference operator*() { return this->row; }
CONSTEXPR_14 reference operator*() { return this->row; }

/** Return a pointer to the CSVRow the iterator has stopped at */
CONSTEXPR pointer operator->() { return &(this->row); }
CONSTEXPR_14 pointer operator->() { return &(this->row); }

iterator& operator++(); /**< Pre-increment iterator */
iterator operator++(int); /**< Post-increment ierator */
Expand Down
18 changes: 9 additions & 9 deletions include/internal/csv_row.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,24 +253,24 @@ namespace csv {
CONSTEXPR csv::string_view get_sv() const noexcept { return this->sv; }

/** Returns true if field is an empty string or string of whitespace characters */
CONSTEXPR bool is_null() noexcept { return type() == DataType::CSV_NULL; }
CONSTEXPR_14 bool is_null() noexcept { return type() == DataType::CSV_NULL; }

/** Returns true if field is a non-numeric, non-empty string */
CONSTEXPR bool is_str() noexcept { return type() == DataType::CSV_STRING; }
CONSTEXPR_14 bool is_str() noexcept { return type() == DataType::CSV_STRING; }

/** Returns true if field is an integer or float */
CONSTEXPR bool is_num() noexcept { return type() >= DataType::CSV_INT8; }
CONSTEXPR_14 bool is_num() noexcept { return type() >= DataType::CSV_INT8; }

/** Returns true if field is an integer */
CONSTEXPR bool is_int() noexcept {
CONSTEXPR_14 bool is_int() noexcept {
return (type() >= DataType::CSV_INT8) && (type() <= DataType::CSV_INT64);
}

/** Returns true if field is a floating point value */
CONSTEXPR bool is_float() noexcept { return type() == DataType::CSV_DOUBLE; };
CONSTEXPR_14 bool is_float() noexcept { return type() == DataType::CSV_DOUBLE; };

/** Return the type of the underlying CSV data */
CONSTEXPR DataType type() noexcept {
CONSTEXPR_14 DataType type() noexcept {
this->get_value();
return _type;
}
Expand All @@ -279,7 +279,7 @@ namespace csv {
long double value = 0; /**< Cached numeric value */
csv::string_view sv = ""; /**< A pointer to this field's text */
DataType _type = DataType::UNKNOWN; /**< Cached data type value */
CONSTEXPR void get_value() noexcept {
CONSTEXPR_14 void get_value() noexcept {
/* Check to see if value has been cached previously, if not
* evaluate it
*/
Expand Down Expand Up @@ -419,13 +419,13 @@ namespace csv {
* CSVRow is still alive.
*/
template<>
CONSTEXPR csv::string_view CSVField::get<csv::string_view>() {
CONSTEXPR_14 csv::string_view CSVField::get<csv::string_view>() {
return this->sv;
}

/** Retrieve this field's value as a long double */
template<>
CONSTEXPR long double CSVField::get<long double>() {
CONSTEXPR_14 long double CSVField::get<long double>() {
if (!is_num())
throw std::runtime_error(internals::ERROR_NAN);

Expand Down
59 changes: 45 additions & 14 deletions include/internal/csv_writer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@

namespace csv {
namespace internals {
static int DECIMAL_PLACES = 5;

/** to_string() for unsigned integers */
template<typename T,
csv::enable_if_t<std::is_unsigned<T>::value, int> = 0>
Expand Down Expand Up @@ -48,25 +50,52 @@ namespace csv {
typename T,
csv::enable_if_t<std::is_floating_point<T>::value, int> = 0
>
inline std::string to_string(T value) {
std::string result;
inline std::string to_string(T value) {
std::string result;

if (value < 0) result = "-";

// Integral part
size_t integral = (size_t)(std::abs(value));
result += (integral == 0) ? "0" : to_string(integral);
T integral_part;
T fractional_part = std::abs(std::modf(value, &integral_part));
integral_part = std::abs(integral_part);

// Integral part
if (value < 0) result = "-";

if (integral_part == 0) {
result = "0";
}
else {
for (short n_digits = log(integral_part) / log(10); n_digits + 1 > 0; n_digits --) {
short digit = std::fmod(integral_part, pow10(n_digits + 1)) / pow10(n_digits);
result += (char)('0' + digit);
}
}

// Decimal part
size_t decimal = (size_t)(((double)std::abs(value) - (double)integral) * 100000);
// Decimal part
result += ".";

result += ".";
result += (decimal == 0) ? "0" : to_string(decimal);
if (fractional_part > 0) {
fractional_part *= pow10(DECIMAL_PLACES);
for (short n_digits = DECIMAL_PLACES; n_digits > 0; n_digits--) {
short digit = std::fmod(fractional_part, pow10(n_digits)) / pow10(n_digits - 1);
result += (char)('0' + digit);
}
}
else {
result += "0";
}

return result;
return result;
}
}

/** Sets how many places after the decimal will be written for floating point numbers
*
* @param precision Number of decimal places
*/
inline static void set_decimal_places(int precision) {
internals::DECIMAL_PLACES = precision;
}

/** @name CSV Writing */
///@{
/**
Expand Down Expand Up @@ -102,6 +131,7 @@ namespace csv {
* @param _out Stream to write to
* @param _quote_minimal Limit field quoting to only when necessary
*/

DelimWriter(OutputStream& _out, bool _quote_minimal = true)
: out(_out), quote_minimal(_quote_minimal) {};

Expand Down Expand Up @@ -213,7 +243,7 @@ namespace csv {
bool quote_escape = false;

for (auto ch : in) {
if (ch == Quote || ch == Delim) {
if (ch == Quote || ch == Delim || ch == '\r' || ch == '\n') {
quote_escape = true;
break;
}
Expand All @@ -222,9 +252,10 @@ namespace csv {
if (!quote_escape) {
if (quote_minimal) return std::string(in);
else {
std::string ret(Quote, 1);
std::string ret(1, Quote);
ret += in.data();
ret += Quote;
return ret;
}
}

Expand Down
Loading

0 comments on commit 39a6af6

Please sign in to comment.