From ab1e28078244d397aedfbdf67f1373a5b30a2d24 Mon Sep 17 00:00:00 2001 From: Alexis Placet <2400067+Alex-PLACET@users.noreply.github.com> Date: Thu, 30 Apr 2026 10:42:30 +0200 Subject: [PATCH] Fix CSV reader to handle 8-bit integers and add comprehensive tests --- include/xtensor/io/xcsv.hpp | 14 ++++++- test/test_xcsv.cpp | 83 +++++++++++++++++++++++++++++++++++++ 2 files changed, 96 insertions(+), 1 deletion(-) diff --git a/include/xtensor/io/xcsv.hpp b/include/xtensor/io/xcsv.hpp index 080ccaf54..2fd347745 100644 --- a/include/xtensor/io/xcsv.hpp +++ b/include/xtensor/io/xcsv.hpp @@ -66,7 +66,7 @@ namespace xt } size_t last = cell.find_last_not_of(' '); - return cell.substr(first, last == std::string::npos ? cell.size() : last + 1); + return cell.substr(first, last == std::string::npos ? cell.size() : last - first + 1); } template <> @@ -93,6 +93,18 @@ namespace xt return std::stoi(cell); } + template <> + inline signed char lexical_cast(const std::string& cell) + { + return static_cast(std::stoi(cell)); + } + + template <> + inline unsigned char lexical_cast(const std::string& cell) + { + return static_cast(std::stoul(cell)); + } + template <> inline long lexical_cast(const std::string& cell) { diff --git a/test/test_xcsv.cpp b/test/test_xcsv.cpp index 767718fe1..148b40cd3 100644 --- a/test/test_xcsv.cpp +++ b/test/test_xcsv.cpp @@ -43,6 +43,20 @@ namespace xt ASSERT_TRUE(all(equal(res, exp))); } + TEST(xcsv, load_binary_matrix) + { + const std::string source = "1,0,1\n" + "0,1,1"; + + std::stringstream source_stream(source); + + const xtensor res = load_csv(source_stream); + + const xtensor exp{{1, 0, 1}, {0, 1, 1}}; + + ASSERT_TRUE(all(equal(res, exp))); + } + TEST(xcsv, load_double_with_options) { std::string source = "A B C D\n" @@ -60,6 +74,56 @@ namespace xt ASSERT_TRUE(all(equal(res, exp))); } + TEST(xcsv, load_string_trims_cells) + { + const std::string source = " alpha , beta,gamma \n delta, epsilon , zeta "; + + std::stringstream source_stream(source); + + const xtensor res = load_csv(source_stream); + + ASSERT_EQ(res.shape()[0], std::size_t(2)); + ASSERT_EQ(res.shape()[1], std::size_t(3)); + ASSERT_EQ(res(0, 0), "alpha"); + ASSERT_EQ(res(0, 1), "beta"); + ASSERT_EQ(res(0, 2), "gamma"); + ASSERT_EQ(res(1, 0), "delta"); + ASSERT_EQ(res(1, 1), "epsilon"); + ASSERT_EQ(res(1, 2), "zeta"); + } + + TEST(xcsv, load_file_uses_config) + { + const std::string source = "metadata\n" + "//ignore this row\n" + "1;2;3\n" + "4;5;6\n" + "7;8;9"; + + std::stringstream source_stream(source); + xcsv_config config; + config.delimiter = ';'; + config.skip_rows = 1; + config.max_rows = 2; + config.comments = "//"; + + xtensor res = {{0, 0, 0}}; + load_file(source_stream, res, config); + + const xtensor exp{{1, 2, 3}, {4, 5, 6}}; + + ASSERT_TRUE(all(equal(res, exp))); + } + + TEST(xcsv, load_inconsistent_rows_throws) + { + const std::string source = "1,2,3\n4,5"; + + std::stringstream source_stream(source); + + XT_EXPECT_THROW(load_csv(source_stream), std::runtime_error); + } + TEST(xcsv, dump_1D) { xtensor data{{1.0, 2.0, 3.0, 4.0}}; @@ -79,4 +143,23 @@ namespace xt dump_csv(res, data); ASSERT_EQ("1,2,3,4\n10,12,15,18\n", res.str()); } + + TEST(xcsv, dump_file_matches_dump_csv) + { + xtensor data{{1, 2}, {3, 4}}; + std::stringstream res; + xcsv_config config; + + dump_file(res, data, config); + + ASSERT_EQ("1,2\n3,4\n", res.str()); + } + + TEST(xcsv, dump_higher_dimension_throws) + { + xtensor data{{{1.0, 2.0}, {3.0, 4.0}}}; + std::stringstream res; + + XT_EXPECT_THROW(dump_csv(res, data), std::runtime_error); + } }