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 <random>
6#include <sstream>
7
8namespace {
9
10std::mt19937& getRandomGenerator() {
11 static std::random_device random_device{};
12 static std::mt19937 generator{random_device()};
13 return generator;
14}
15
16} // namespace
17
18using namespace sourcepp;
19
20bool string::contains(std::string_view s, char c) {
21 return std::find(s.begin(), s.end(), c) != s.end();
22}
23
24bool string::matches(std::string_view in, std::string_view search) {
25 int inPos = 0, searchPos = 0;
26 for ( ; inPos < in.length() && searchPos < search.length(); inPos++, searchPos++) {
27 if (search[searchPos] == '%') {
28 if (++searchPos == search.length()) {
29 return false;
30 }
31 switch (search[searchPos]) {
32 default:
33 case '?': // wildcard
34 break;
35 case 'w': // whitespace
36 if (!std::isspace(in[inPos])) return false;
37 break;
38 case 'a': // letter
39 if (!(in[inPos] >= 'a' && in[inPos] <= 'z' || in[inPos] >= 'A' && in[inPos] <= 'Z')) return false;
40 break;
41 case 'u': // uppercase letter
42 if (!(in[inPos] >= 'A' && in[inPos] <= 'Z')) return false;
43 break;
44 case 'l': // lowercase letter
45 if (!(in[inPos] >= 'a' && in[inPos] <= 'z')) return false;
46 break;
47 case 'd': // digit
48 if (!std::isdigit(in[inPos])) return false;
49 break;
50 case '%': // escaped percent
51 if (in[inPos] != '%') return false;
52 break;
53 }
54 } else if (in[inPos] != search[searchPos]) {
55 return false;
56 }
57 }
58 return inPos == in.length() && searchPos == search.length();
59}
60
61bool string::iequals(std::string_view s1, std::string_view s2) {
62 return std::ranges::equal(s1, s2, [](char a, char b) { return std::tolower(a) == std::tolower(b); });
63}
64
65// https://stackoverflow.com/a/217605
66
67void string::ltrim(std::string& s) {
68 s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](char c) { return !std::isspace(c); }));
69}
70
71std::string_view string::ltrim(std::string_view s) {
72 while (!s.empty() && std::isspace(s[0])) {
73 s.remove_prefix(1);
74 }
75 return s;
76}
77
78void string::rtrim(std::string& s) {
79 s.erase(std::find_if(s.rbegin(), s.rend(), [](char c) { return !std::isspace(c); }).base(), s.end());
80}
81
82std::string_view string::rtrim(std::string_view s) {
83 while (!s.empty() && std::isspace(s[s.size() - 1])) {
84 s.remove_suffix(1);
85 }
86 return s;
87}
88
89void string::trim(std::string& s) {
90 rtrim(s);
91 ltrim(s);
92}
93
94std::string_view string::trim(std::string_view s) {
95 return ltrim(rtrim(s));
96}
97
98void string::ltrim(std::string& s, std::string_view chars) {
99 s.erase(s.begin(), std::find_if(s.begin(), s.end(), [chars](char c) {
100 return !contains(chars, c);
101 }));
102}
103
104std::string_view string::ltrim(std::string_view s, std::string_view chars) {
105 while (!s.empty() && contains(chars, s[0])) {
106 s.remove_prefix(1);
107 }
108 return s;
109}
110
111void string::rtrim(std::string& s, std::string_view chars) {
112 s.erase(std::find_if(s.rbegin(), s.rend(), [chars](char c) {
113 return !contains(chars, c);
114 }).base(), s.end());
115}
116
117std::string_view string::rtrim(std::string_view s, std::string_view chars) {
118 while (!s.empty() && contains(chars, s[s.size() - 1])) {
119 s.remove_suffix(1);
120 }
121 return s;
122}
123
124void string::trim(std::string& s, std::string_view chars) {
125 rtrim(s, chars);
126 ltrim(s, chars);
127}
128
129std::string_view string::trim(std::string_view s, std::string_view chars) {
130 return ltrim(rtrim(s, chars), chars);
131}
132
133// https://stackoverflow.com/a/46931770
134
135std::vector<std::string> string::split(std::string_view s, char delim) {
136 std::vector<std::string> result;
137 std::stringstream ss(std::string{s});
138 std::string item;
139 while (std::getline(ss, item, delim)) {
140 result.push_back(item);
141 }
142 return result;
143}
144
145void string::toLower(std::string& input) {
146 std::transform(input.begin(), input.end(), input.begin(), [](unsigned char c){ return std::tolower(c); });
147}
148
149std::string string::toLower(std::string_view input) {
150 std::string out{input};
151 toLower(out);
152 return out;
153}
154
155void string::toUpper(std::string& input) {
156 std::transform(input.begin(), input.end(), input.begin(), [](unsigned char c){ return std::toupper(c); });
157}
158
159std::string string::toUpper(std::string_view input) {
160 std::string out{input};
161 toUpper(out);
162 return out;
163}
164
165std::string string::createRandom(uint16_t length, std::string_view chars) {
166 auto& generator = ::getRandomGenerator();
167 std::uniform_int_distribution distribution{0, static_cast<int>(chars.length() - 1)};
168
169 std::string out;
170 for (uint16_t i = 0; i < length; i++) {
171 out += chars[distribution(generator)];
172 }
173
174 return out;
175}
176
178 static constexpr std::string_view chars = "0123456789abcdef";
179
180 auto& generator = ::getRandomGenerator();
181 std::uniform_int_distribution distribution{0, static_cast<int>(chars.length() - 1)};
182
183 std::string out;
184 for (uint16_t i = 0; i < 8; i++) {
185 out += chars[distribution(generator)];
186 }
187 out += '-';
188 for (uint16_t i = 0; i < 3; i++) {
189 for (uint16_t j = 0; j < 4; j++) {
190 out += chars[distribution(generator)];
191 }
192 out += '-';
193 }
194 for (uint16_t i = 0; i < 12; i++) {
195 out += chars[distribution(generator)];
196 }
197
198 return out;
199}
200
201std::string string::padNumber(int64_t number, int width, char pad) {
202 const auto numStr = std::to_string(number);
203 return std::string(width - std::min<std::string::size_type>(width, numStr.length()), pad) + numStr;
204}
205
206void string::normalizeSlashes(std::string& path, bool stripSlashPrefix, bool stripSlashSuffix) {
207 std::replace(path.begin(), path.end(), '\\', '/');
208 if (stripSlashPrefix && path.starts_with('/')) {
209 path = path.substr(1);
210 }
211 if (stripSlashSuffix && path.ends_with('/')) {
212 path.pop_back();
213 }
214}
215
216void string::denormalizeSlashes(std::string& path, bool stripSlashPrefix, bool stripSlashSuffix) {
217 std::replace(path.begin(), path.end(), '/', '\\');
218 if (stripSlashPrefix && path.starts_with('\\')) {
219 path = path.substr(1);
220 }
221 if (stripSlashSuffix && path.ends_with('\\')) {
222 path.pop_back();
223 }
224}
225
226std::from_chars_result string::toBool(std::string_view number, bool& out, int base) {
227 uint8_t tmp;
228 const auto result = std::from_chars(number.data(), number.data() + number.size(), tmp, base);
229 out = tmp;
230 return result;
231}
std::string createRandom(uint16_t length=32, std::string_view chars="0123456789_abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ")
Definition: String.cpp:165
bool contains(std::string_view s, char c)
Definition: String.cpp:20
void normalizeSlashes(std::string &path, bool stripSlashPrefix=false, bool stripSlashSuffix=true)
Definition: String.cpp:206
std::from_chars_result toBool(std::string_view number, bool &out, int base=10)
Definition: String.cpp:226
void ltrim(std::string &s)
Definition: String.cpp:67
std::vector< std::string > split(std::string_view s, char delim)
Definition: String.cpp:135
void denormalizeSlashes(std::string &path, bool stripSlashPrefix=false, bool stripSlashSuffix=true)
Definition: String.cpp:216
bool matches(std::string_view in, std::string_view search)
A very basic regex-like pattern checker for ASCII strings.
Definition: String.cpp:24
std::string padNumber(int64_t number, int width, char pad='0')
Definition: String.cpp:201
void rtrim(std::string &s)
Definition: String.cpp:78
void toUpper(std::string &input)
Definition: String.cpp:155
void trim(std::string &s)
Definition: String.cpp:89
bool iequals(std::string_view s1, std::string_view s2)
Definition: String.cpp:61
void toLower(std::string &input)
Definition: String.cpp:145
std::string generateUUIDv4()
Definition: String.cpp:177
Definition: LZMA.h:11