FOSSredder 0.5.0
Loading...
Searching...
No Matches
ParserHeuristics.cpp
Go to the documentation of this file.
1
5
8
9#include <array>
10#include <vector>
11
14
16
17namespace {
18
19std::string normalizeAlphaNumeric(const std::string &value) {
20 std::string normalized;
21 normalized.reserve(value.size());
22 for (unsigned char c : core::utils::lowerAscii(value)) {
23 if ((c >= 'a' && c <= 'z') || (c >= '0' && c <= '9'))
24 normalized.push_back(static_cast<char>(c));
25 }
26 return normalized;
27}
28
29bool looksLikeAmountToken(const std::string &token) {
30 const auto separator = token.find_last_of(".,");
31 if (separator == std::string::npos || separator == 0 ||
32 separator + 3 != token.size())
33 return false;
34
35 const auto isDigit = [](unsigned char c) { return c >= '0' && c <= '9'; };
36 if (!isDigit(static_cast<unsigned char>(token[separator + 1])) ||
37 !isDigit(static_cast<unsigned char>(token[separator + 2])))
38 return false;
39
40 bool hasIntegralDigit = false;
41 for (size_t i = 0; i < separator; ++i) {
42 const unsigned char c = static_cast<unsigned char>(token[i]);
43 if (isDigit(c)) {
44 hasIntegralDigit = true;
45 continue;
46 }
47 if (c != '.' && c != ',')
48 return false;
49 }
50
51 return hasIntegralDigit;
52}
53
54bool containsAmountToken(const std::string &value) {
55 const auto tokens = core::utils::splitWhitespace(value);
56 return std::any_of(
57 tokens.begin(), tokens.end(),
58 [](const std::string &token) { return looksLikeAmountToken(token); });
59}
60
61} // namespace
62
63bool isHeaderNoiseLine(const std::string &line) {
64 const auto l = lowerAscii(line);
65 if (l.size() <= 2)
66 return true;
67
68 const auto toks = core::utils::splitWhitespace(l);
69 if (toks.empty())
70 return false;
71
72 std::vector<std::string> normalizedTokens;
73 normalizedTokens.reserve(toks.size());
74 for (const auto &token : toks)
75 normalizedTokens.push_back(normalizeAlphaNumeric(token));
76
77 static const std::array<std::string, 7> noise = {
78 "kontoauszug", "auszugnr", "seitenr", "ustidnr",
79 "ansprechpartner", "telefonnummer", "kontowahrung",
80 };
81
82 int matches = 0;
83 for (const auto &n : noise) {
84 for (const auto &t : normalizedTokens) {
85 if (t == n) {
86 ++matches;
87 break;
88 }
89 }
90 if (matches >= 2)
91 return true;
92 }
93
94 return false;
95}
96
97static bool containsAny(const std::string &hayLower,
98 std::initializer_list<const char *> needlesLower) {
99 for (auto n : needlesLower) {
100 if (hayLower.find(n) != std::string::npos)
101 return true;
102 }
103 return false;
104}
105
106bool isFooterLine(const std::string &line) {
107 const auto l = lowerAscii(line);
108 return containsAny(
109 l, {"folgeseite", "vsa000", "fil200", "ktea", "folgende seite"});
110}
111
112bool isPostTransactionFootnote(const std::string &line) {
113 const auto l = lowerAscii(line);
114 if (containsAmountToken(l))
115 return false;
116
117 const std::initializer_list<const char *> strong = {
118 "guthaben sind als",
119 "einlagensicherungsgesetzes",
120 "informationsbogen",
121 "der angegebene kontostand",
122 "wertstellung der buchungen",
123 "kontoüberziehung",
124 "neuer kontostand",
125 "alter kontostand",
126 "folgeseite",
127 };
128 for (auto p : strong)
129 if (l.find(p) != std::string::npos)
130 return true;
131
132 int weakMatches = 0;
133 if (l.find("kontostand") != std::string::npos)
134 ++weakMatches;
135 if (l.find("guthaben") != std::string::npos)
136 ++weakMatches;
137 if (l.find("einlagen") != std::string::npos)
138 ++weakMatches;
139 if (l.find("betrag") != std::string::npos)
140 ++weakMatches;
141 return weakMatches >= 2;
142}
143
144bool isTransactionsSectionHeader(const std::string &line) {
145 const auto l = lowerAscii(line);
146 if (l.find("angaben zu den umsätzen") != std::string::npos)
147 return true;
148 const bool hasValuta = (l.find("valuta") != std::string::npos);
149 const bool hasUmsaetze = (l.find("umsätzen") != std::string::npos) ||
150 (l.find("umsaetzen") != std::string::npos);
151 const bool hasAngaben = (l.find("angaben") != std::string::npos);
152 return hasValuta && (hasUmsaetze || hasAngaben);
153}
154
155bool isDebitCreditHeaderLine(const std::string &line) {
156 const auto n = normalizeAlphaNumeric(line);
157 return (n.find("zuihrenlasten") != std::string::npos) &&
158 (n.find("zuihrengunsten") != std::string::npos);
159}
160
161} // namespace core::application::importing::internal
std::string lowerAscii(std::string s)
Converts ASCII letters in a string to lowercase.
Declares heuristics used to classify statement parser lines.
Declares private string helpers shared across core implementation files.
static bool containsAny(const std::string &hayLower, std::initializer_list< const char * > needlesLower)
bool isTransactionsSectionHeader(const std::string &line)
Detects whether a line marks the start of the transactions section.
bool isPostTransactionFootnote(const std::string &line)
Detects whether a line belongs to the post-transaction footnote area.
bool isFooterLine(const std::string &line)
Detects whether a line marks a footer section.
bool isDebitCreditHeaderLine(const std::string &line)
Detects whether a line looks like a debit/credit table header.
bool isHeaderNoiseLine(const std::string &line)
Detects whether a line looks like header noise.
std::vector< std::string > tokens(const std::string &text)
Splits normalized text into tokens.
std::vector< std::string > splitWhitespace(const std::string &s)
Splits a string on ASCII whitespace.
std::string lowerAscii(std::string s)
Converts ASCII letters in a string to lowercase.
std::string trim(std::string s)
Removes leading and trailing ASCII whitespace from a string.