SourcePP
Several modern C++20 libraries for sanely parsing Valve's formats.
Loading...
Searching...
No Matches
String.cpp
Go to the documentation of this file.
1#include <sourcepp/String.h>
2
3#include <algorithm>
4#include <cctype>
5#include <format>
6#include <locale>
7#include <random>
8#include <ranges>
9#include <sstream>
10
11namespace {
12
13std::mt19937& getRandomGenerator() {
14 static std::random_device random_device{};
15 static std::mt19937 generator{random_device()};
16 return generator;
17}
18
19} // namespace
20
21using namespace sourcepp;
22
23bool string::isNewLine(char c) {
24 return c == '\r' || c == '\n';
25}
26
27bool string::isNewLine(std::string_view str) {
28 return std::ranges::all_of(str, [](char c) { return isNewLine(c); });
29}
30
32 return std::isspace(c, std::locale::classic());
33}
34
35bool string::isWhitespace(std::string_view str) {
36 return std::ranges::all_of(str, [](char c) { return isWhitespace(c); });
37}
38
39bool string::isNumber(char c) {
40 return std::isdigit(c, std::locale::classic());
41}
42
43bool string::isNumber(std::string_view str) {
44 return std::ranges::all_of(str, [](char c) { return isNumber(c); });
45}
46
48 return std::isalnum(c, std::locale::classic());
49}
50
51bool string::isAlphaNumeric(std::string_view str) {
52 return std::ranges::all_of(str, [](char c) { return isAlphaNumeric(c); });
53}
54
55bool string::contains(std::string_view s, char c) {
56 return std::ranges::find(s, c) != s.end();
57}
58
59bool string::matches(std::string_view in, std::string_view search, bool ignoreCase) {
60 int inPos = 0, searchPos = 0;
61 for ( ; inPos < in.length() && searchPos < search.length(); inPos++, searchPos++) {
62 if (search[searchPos] == '%') {
63 if (++searchPos == search.length()) {
64 return false;
65 }
66 switch (search[searchPos]) {
67 default:
68 case '?': // wildcard
69 break;
70 case 'w': // whitespace
71 if (!std::isspace(in[inPos])) return false;
72 break;
73 case 'a': // letter
74 if (!(in[inPos] >= 'a' && in[inPos] <= 'z' || in[inPos] >= 'A' && in[inPos] <= 'Z')) return false;
75 break;
76 case 'u': // uppercase letter
77 if (!(in[inPos] >= 'A' && in[inPos] <= 'Z')) return false;
78 break;
79 case 'l': // lowercase letter
80 if (!(in[inPos] >= 'a' && in[inPos] <= 'z')) return false;
81 break;
82 case 'd': // digit
83 if (!std::isdigit(in[inPos])) return false;
84 break;
85 case '%': // escaped percent
86 if (in[inPos] != '%') return false;
87 break;
88 }
89 } else if ((!ignoreCase && in[inPos] != search[searchPos]) || (ignoreCase && std::tolower(in[inPos]) != std::tolower(search[searchPos]))) {
90 return false;
91 }
92 }
93 return inPos == in.length() && searchPos == search.length();
94}
95
96bool string::iequals(std::string_view s1, std::string_view s2) {
97 return std::ranges::equal(s1, s2, [](char a, char b) { return std::tolower(a) == std::tolower(b); });
98}
99
100// https://stackoverflow.com/a/217605
101
102void string::ltrim(std::string& s) {
103 s.erase(s.begin(), std::ranges::find_if(s, [](char c) { return !std::isspace(c); }));
104}
105
106std::string_view string::ltrim(std::string_view s) {
107 while (!s.empty() && std::isspace(s[0])) {
108 s.remove_prefix(1);
109 }
110 return s;
111}
112
113void string::rtrim(std::string& s) {
114 s.erase(std::find_if(s.rbegin(), s.rend(), [](char c) { return !std::isspace(c); }).base(), s.end());
115}
116
117std::string_view string::rtrim(std::string_view s) {
118 while (!s.empty() && std::isspace(s[s.size() - 1])) {
119 s.remove_suffix(1);
120 }
121 return s;
122}
123
124void string::trim(std::string& s) {
125 rtrim(s);
126 ltrim(s);
127}
128
129std::string_view string::trim(std::string_view s) {
130 return ltrim(rtrim(s));
131}
132
133void string::trimInternal(std::string& s) {
134 s.erase(std::ranges::unique(s, [](char lhs, char rhs) { return lhs == rhs && std::isspace(lhs); }).begin(), s.end());
135}
136
137std::string string::trimInternal(std::string_view s) {
138 std::string out{s};
139 trimInternal(out);
140 return out;
141}
142
143void string::ltrim(std::string& s, std::string_view chars) {
144 s.erase(s.begin(), std::ranges::find_if(s, [chars](char c) {
145 return !contains(chars, c);
146 }));
147}
148
149std::string_view string::ltrim(std::string_view s, std::string_view chars) {
150 while (!s.empty() && contains(chars, s[0])) {
151 s.remove_prefix(1);
152 }
153 return s;
154}
155
156void string::rtrim(std::string& s, std::string_view chars) {
157 s.erase(std::find_if(s.rbegin(), s.rend(), [chars](char c) {
158 return !contains(chars, c);
159 }).base(), s.end());
160}
161
162std::string_view string::rtrim(std::string_view s, std::string_view chars) {
163 while (!s.empty() && contains(chars, s[s.size() - 1])) {
164 s.remove_suffix(1);
165 }
166 return s;
167}
168
169void string::trim(std::string& s, std::string_view chars) {
170 rtrim(s, chars);
171 ltrim(s, chars);
172}
173
174std::string_view string::trim(std::string_view s, std::string_view chars) {
175 return ltrim(rtrim(s, chars), chars);
176}
177
178void string::trimInternal(std::string& s, std::string_view chars) {
179 s.erase(std::ranges::unique(s, [chars](char lhs, char rhs) { return lhs == rhs && std::ranges::find(chars, lhs) != chars.end(); }).begin(), s.end());
180}
181
182std::string string::trimInternal(std::string_view s, std::string_view chars) {
183 std::string out{s};
184 trimInternal(out, chars);
185 return out;
186}
187
188// https://stackoverflow.com/a/46931770
189
190std::vector<std::string> string::split(std::string_view s, char delim) {
191 std::vector<std::string> result;
192 std::stringstream ss(std::string{s});
193 std::string item;
194 while (std::getline(ss, item, delim)) {
195 result.push_back(item);
196 }
197 return result;
198}
199
200void string::toLower(std::string& input) {
201 std::ranges::transform(input, input.begin(), [](unsigned char c){ return std::tolower(c); });
202}
203
204std::string string::toLower(std::string_view input) {
205 std::string out{input};
206 toLower(out);
207 return out;
208}
209
210void string::toUpper(std::string& input) {
211 std::ranges::transform(input, input.begin(), [](unsigned char c){ return std::toupper(c); });
212}
213
214std::string string::toUpper(std::string_view input) {
215 std::string out{input};
216 toUpper(out);
217 return out;
218}
219
220std::string string::createRandom(uint16_t length, std::string_view chars) {
221 auto& generator = ::getRandomGenerator();
222 std::uniform_int_distribution distribution{0, static_cast<int>(chars.length() - 1)};
223
224 std::string out;
225 for (uint16_t i = 0; i < length; i++) {
226 out += chars[distribution(generator)];
227 }
228
229 return out;
230}
231
233 static constexpr std::string_view chars = "0123456789abcdef";
234
235 auto& generator = ::getRandomGenerator();
236 std::uniform_int_distribution distribution{0, static_cast<int>(chars.length() - 1)};
237
238 std::string out;
239 for (uint16_t i = 0; i < 8; i++) {
240 out += chars[distribution(generator)];
241 }
242 out += '-';
243 for (uint16_t i = 0; i < 3; i++) {
244 for (uint16_t j = 0; j < 4; j++) {
245 out += chars[distribution(generator)];
246 }
247 out += '-';
248 }
249 for (uint16_t i = 0; i < 12; i++) {
250 out += chars[distribution(generator)];
251 }
252
253 return out;
254}
255
256std::string string::padNumber(int64_t number, int width) {
257 return std::format("{:0>{}}", number, width);
258}
259
260void string::normalizeSlashes(std::string& path, bool stripSlashPrefix, bool stripSlashSuffix) {
261 std::ranges::replace(path, '\\', '/');
262 if (stripSlashPrefix && path.starts_with('/')) {
263 path = path.substr(1);
264 }
265 if (stripSlashSuffix && path.ends_with('/')) {
266 path.pop_back();
267 }
268}
269
270void string::denormalizeSlashes(std::string& path, bool stripSlashPrefix, bool stripSlashSuffix) {
271 std::ranges::replace(path, '/', '\\');
272 if (stripSlashPrefix && path.starts_with('\\')) {
273 path = path.substr(1);
274 }
275 if (stripSlashSuffix && path.ends_with('\\')) {
276 path.pop_back();
277 }
278}
279
280bool string::toBool(std::string_view number, bool& out, int base) {
281 uint8_t tmp;
282 const auto [ptr, err] = std::from_chars(number.data(), number.data() + number.size(), tmp, base);
283 out = tmp;
284 return ptr == number.data() + number.size() && err == std::errc{};
285}
286
287bool string::isBool(std::string_view number, int base) {
288 bool tmp;
289 return toBool(number, tmp, base);
290}
291
292std::vector<std::byte> string::decodeHex(std::string_view hex) {
293 static constexpr auto hexChar2Int = [](char c) -> uint8_t {
294 if (c >= '0' && c <= '9') {
295 return c - '0';
296 }
297 if (c >= 'a' && c <= 'f') {
298 return c - 'a' + 10;
299 }
300 if (c >= 'A' && c <= 'F') {
301 return c - 'A' + 10;
302 }
303 return 0;
304 };
305
306 const bool remainder = hex.size() % 2;
307
308 std::vector<std::byte> hexData;
309 hexData.reserve(hex.size() / 2 + remainder);
310 if (remainder) {
311 hexData.push_back(static_cast<std::byte>(hexChar2Int(hex.front())));
312 }
313 for (int i = remainder; i < hex.length(); i += 2) {
314 hexData.push_back(static_cast<std::byte>(hexChar2Int(hex[i]) << 4 | hexChar2Int(hex[i + 1])));
315 }
316 return hexData;
317}
318
319std::string string::encodeHex(std::span<const std::byte> hex) {
320 std::string hexStr;
321 hexStr.reserve(hex.size() * 2);
322 for (const auto byte : hex) {
323 hexStr.append(std::format("{:02x}", static_cast<int>(byte)));
324 }
325 return hexStr;
326}
std::string createRandom(uint16_t length=32, std::string_view chars="0123456789_abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ")
Definition String.cpp:220
std::string padNumber(int64_t number, int width)
Definition String.cpp:256
std::vector< std::byte > decodeHex(std::string_view hex)
Definition String.cpp:292
bool contains(std::string_view s, char c)
Definition String.cpp:55
void normalizeSlashes(std::string &path, bool stripSlashPrefix=false, bool stripSlashSuffix=true)
Definition String.cpp:260
bool isWhitespace(char c)
If a char is a whitespace character.
Definition String.cpp:31
bool matches(std::string_view in, std::string_view search, bool ignoreCase=false)
A very basic regex-like pattern checker for ASCII strings.
Definition String.cpp:59
void ltrim(std::string &s)
Definition String.cpp:102
bool isNewLine(char c)
If a char is a newline character.
Definition String.cpp:23
bool toBool(std::string_view number, bool &out, int base=10)
Definition String.cpp:280
std::vector< std::string > split(std::string_view s, char delim)
Definition String.cpp:190
void denormalizeSlashes(std::string &path, bool stripSlashPrefix=false, bool stripSlashSuffix=true)
Definition String.cpp:270
void trimInternal(std::string &s)
Definition String.cpp:133
bool isBool(std::string_view number, int base=10)
Definition String.cpp:287
bool isNumber(char c)
If a char is a numerical character (0-9).
Definition String.cpp:39
void rtrim(std::string &s)
Definition String.cpp:113
void toUpper(std::string &input)
Definition String.cpp:210
void trim(std::string &s)
Definition String.cpp:124
bool iequals(std::string_view s1, std::string_view s2)
Definition String.cpp:96
void toLower(std::string &input)
Definition String.cpp:200
std::string generateUUIDv4()
Definition String.cpp:232
std::string encodeHex(std::span< const std::byte > hex)
Definition String.cpp:319
bool isAlphaNumeric(char c)
If a char is a letter (a-z, A-Z) or a numerical character (0-9).
Definition String.cpp:47