summaryrefslogtreecommitdiffstats
path: root/src/csv.cpp
blob: 32b959122f0f884e9cf2eb4d80b1336b47fb563a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
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