summaryrefslogtreecommitdiffstats
path: root/src/csv.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/csv.cpp')
-rw-r--r--src/csv.cpp72
1 files changed, 72 insertions, 0 deletions
diff --git a/src/csv.cpp b/src/csv.cpp
new file mode 100644
index 0000000..32b9591
--- /dev/null
+++ b/src/csv.cpp
@@ -0,0 +1,72 @@
+#include "csv.hpp"
+
+#include <cstddef>
+
+namespace cepis::detail {
+
+std::vector<std::vector<std::string>> parse_csv(std::string_view text) {
+ if (text.compare(0, 3, "\xEF\xBB\xBF") == 0) {
+ text.remove_prefix(3);
+ }
+
+ std::vector<std::vector<std::string>> rows;
+ std::vector<std::string> row;
+ std::string field;
+ bool in_quotes = false;
+ bool row_has_content = false;
+
+ const auto end_row = [&] {
+ if (!row_has_content) {
+ field.clear();
+ row.clear();
+ return;
+ }
+ row.push_back(std::move(field));
+ field.clear();
+ rows.push_back(std::move(row));
+ row.clear();
+ row_has_content = false;
+ };
+
+ for (std::size_t i = 0; i < text.size(); ++i) {
+ const char c = text[i];
+
+ if (in_quotes) {
+ row_has_content = true;
+ if (c != '"') {
+ field.push_back(c);
+ } else if (i + 1 < text.size() && text[i + 1] == '"') {
+ field.push_back('"');
+ ++i;
+ } else {
+ in_quotes = false;
+ }
+ continue;
+ }
+
+ switch (c) {
+ case '"':
+ row_has_content = true;
+ in_quotes = true;
+ break;
+ case ',':
+ row_has_content = true;
+ row.push_back(std::move(field));
+ field.clear();
+ break;
+ case '\r':
+ break;
+ case '\n':
+ end_row();
+ break;
+ default:
+ row_has_content = true;
+ field.push_back(c);
+ }
+ }
+
+ end_row();
+ return rows;
+}
+
+} // namespace cepis::detail