From d69a5dea7580ccec38e58de24e272a1e91b1b6b0 Mon Sep 17 00:00:00 2001 From: JCrawfy Date: Mon, 18 Nov 2019 19:55:13 +1300 Subject: [PATCH] scan and replace '.' with ',' when decimal separator is comma --- include/xlnt/utils/numeric.hpp | 42 +++++++++ source/detail/serialization/xlsx_consumer.cpp | 88 ++----------------- 2 files changed, 47 insertions(+), 83 deletions(-) diff --git a/include/xlnt/utils/numeric.hpp b/include/xlnt/utils/numeric.hpp index 1165b2f5..5e59cc49 100644 --- a/include/xlnt/utils/numeric.hpp +++ b/include/xlnt/utils/numeric.hpp @@ -28,6 +28,7 @@ #include #include #include +#include namespace xlnt { namespace detail { @@ -115,5 +116,46 @@ bool float_equals(const LNumber &lhs, const RNumber &rhs, return ((lhs + scaled_fuzz) >= rhs) && ((rhs + scaled_fuzz) >= lhs); } +struct number_converter +{ + explicit number_converter() + : should_convert_to_comma(std::use_facet>(std::locale{}).decimal_point() == ',') + { + } + + double stold(std::string &s) const noexcept + { + assert(!s.empty()); + if (should_convert_to_comma) + { + auto decimal_pt = std::find(s.begin(), s.end(), '.'); + if (decimal_pt != s.end()) + { + *decimal_pt = ','; + } + } + return strtod(s.c_str(), nullptr); + } + + double stold(const std::string &s) const + { + assert(!s.empty()); + if (!should_convert_to_comma) + { + return strtod(s.c_str(), nullptr); + } + std::string copy(s); + auto decimal_pt = std::find(copy.begin(), copy.end(), '.'); + if (decimal_pt != s.end()) + { + *decimal_pt = ','; + } + return strtod(copy.c_str(), nullptr); + } + +private: + bool should_convert_to_comma = false; +}; + } // namespace detail } // namespace xlnt diff --git a/source/detail/serialization/xlsx_consumer.cpp b/source/detail/serialization/xlsx_consumer.cpp index 67847e54..3fe41b49 100644 --- a/source/detail/serialization/xlsx_consumer.cpp +++ b/source/detail/serialization/xlsx_consumer.cpp @@ -116,84 +116,6 @@ bool is_true(const std::string &bool_string) #endif } -// can find documentation for _strtod_l back to VS 2015 -// https://docs.microsoft.com/en-us/cpp/c-runtime-library/reference/strtod-strtod-l-wcstod-wcstod-l?view=vs-2015 -// -#if defined(_MSC_VER) && _MSC_VER >= 1900 - -#include -#include - -// Cache locale object -static int c_locale_initialized = 0; -static _locale_t c_locale; - -_locale_t get_c_locale() -{ - if (!c_locale_initialized) - { - c_locale_initialized = 1; - c_locale = _create_locale(LC_ALL, "C"); - } - return c_locale; -} - -struct number_converter -{ - double stold(const std::string &s) - { - return _strtod_l(s.c_str(), nullptr, get_c_locale()); - } -}; - -#else - -// according to various sources, something like the below *would* work for POSIX systems -// however due to uncertainty on whether it will actually work it is not used -//#include -//#include -// -//// Cache locale object -//static int c_locale_initialized = 0; -//static locale_t c_locale; -// -//locale_t get_c_locale() -//{ -// if (!c_locale_initialized) -// { -// c_locale_initialized = 1; -// c_locale = newlocale(LC_ALL_MASK, "C", NULL); -// } -// return c_locale; -//} -// -//double strtod_c(const char *nptr, char **endptr) -//{ -// return strtod_l(nptr, endptr, get_c_locale()); -//} - -// add specialisations whenever possible -// in the spreadsheet-load benchmark, strtod is roughly 10% faster -struct number_converter -{ - number_converter() - { - stream.imbue(std::locale("C")); - } - - double stold(const std::string &s) - { - stream.str(s); - stream.clear(); - stream >> result; - return result; - } - - std::istringstream stream; - double result; -}; - -#endif using style_id_pair = std::pair; @@ -388,14 +310,14 @@ Cell parse_cell(xlnt::row_t row_arg, xml::parser *parser) } // inside element -std::pair parse_row(xml::parser *parser, number_converter &converter, std::vector &parsed_cells) +std::pair parse_row(xml::parser *parser, xlnt::detail::number_converter &converter, std::vector &parsed_cells) { std::pair props; for (auto &attr : parser->attribute_map()) { if (string_equal(attr.first.name(), "dyDescent")) { - props.first.dy_descent = converter.stold(attr.second.value.c_str()); + props.first.dy_descent = converter.stold(attr.second.value); } else if (string_equal(attr.first.name(), "spans")) { @@ -403,7 +325,7 @@ std::pair parse_row(xml::parser *parser, number_conve } else if (string_equal(attr.first.name(), "ht")) { - props.first.height = converter.stold(attr.second.value.c_str()); + props.first.height = converter.stold(attr.second.value); } else if (string_equal(attr.first.name(), "s")) { @@ -467,7 +389,7 @@ std::pair parse_row(xml::parser *parser, number_conve } // inside element -Sheet_Data parse_sheet_data(xml::parser *parser, number_converter &converter) +Sheet_Data parse_sheet_data(xml::parser *parser, xlnt::detail::number_converter &converter) { Sheet_Data sheet_data; int level = 1; // nesting level @@ -1091,7 +1013,7 @@ void xlsx_consumer::read_worksheet_sheetdata() } case cell::type::number: { - ws_cell_impl->value_numeric_ = converter.stold(cell.value.c_str()); + ws_cell_impl->value_numeric_ = converter.stold(cell.value); break; } case cell::type::shared_string: