1 | #include "duckdb/common/string_util.hpp" |
2 | |
3 | #include <algorithm> |
4 | #include <cctype> |
5 | #include <iomanip> |
6 | #include <memory> |
7 | #include <sstream> |
8 | #include <stdarg.h> |
9 | #include <string.h> |
10 | #include <string> |
11 | |
12 | using namespace duckdb; |
13 | using namespace std; |
14 | |
15 | bool StringUtil::Contains(const string &haystack, const string &needle) { |
16 | return (haystack.find(needle) != string::npos); |
17 | } |
18 | |
19 | void StringUtil::LTrim(string &str) { |
20 | auto it = str.begin(); |
21 | while (isspace(*it)) { |
22 | it++; |
23 | } |
24 | str.erase(str.begin(), it); |
25 | } |
26 | |
27 | // Remove trailing ' ', '\f', '\n', '\r', '\t', '\v' |
28 | void StringUtil::RTrim(string &str) { |
29 | str.erase(find_if(str.rbegin(), str.rend(), [](int ch) { return ch > 0 && !isspace(ch); }).base(), str.end()); |
30 | } |
31 | |
32 | void StringUtil::Trim(string &str) { |
33 | StringUtil::LTrim(str); |
34 | StringUtil::RTrim(str); |
35 | } |
36 | |
37 | bool StringUtil::StartsWith(const string &str, const string &prefix) { |
38 | if (prefix.size() > str.size()) { |
39 | return false; |
40 | } |
41 | return equal(prefix.begin(), prefix.end(), str.begin()); |
42 | } |
43 | |
44 | bool StringUtil::EndsWith(const string &str, const string &suffix) { |
45 | if (suffix.size() > str.size()) |
46 | return false; |
47 | return equal(suffix.rbegin(), suffix.rend(), str.rbegin()); |
48 | } |
49 | |
50 | string StringUtil::Repeat(const string &str, idx_t n) { |
51 | ostringstream os; |
52 | if (n == 0 || str.empty()) { |
53 | return (os.str()); |
54 | } |
55 | for (int i = 0; i < static_cast<int>(n); i++) { |
56 | os << str; |
57 | } |
58 | return (os.str()); |
59 | } |
60 | |
61 | vector<string> StringUtil::Split(const string &str, char delimiter) { |
62 | stringstream ss(str); |
63 | vector<string> lines; |
64 | string temp; |
65 | while (getline(ss, temp, delimiter)) { |
66 | lines.push_back(temp); |
67 | } // WHILE |
68 | return (lines); |
69 | } |
70 | |
71 | string StringUtil::Join(const vector<string> &input, const string &separator) { |
72 | return StringUtil::Join(input, input.size(), separator, [](const string &s) { return s; }); |
73 | } |
74 | |
75 | string StringUtil::Prefix(const string &str, const string &prefix) { |
76 | vector<string> lines = StringUtil::Split(str, '\n'); |
77 | if (lines.empty()) |
78 | return ("" ); |
79 | |
80 | ostringstream os; |
81 | for (idx_t i = 0, cnt = lines.size(); i < cnt; i++) { |
82 | if (i > 0) |
83 | os << endl; |
84 | os << prefix << lines[i]; |
85 | } // FOR |
86 | return (os.str()); |
87 | } |
88 | |
89 | // http://ubuntuforums.org/showpost.php?p=10215516&postcount=5 |
90 | string StringUtil::FormatSize(idx_t bytes) { |
91 | double BASE = 1024; |
92 | double KB = BASE; |
93 | double MB = KB * BASE; |
94 | double GB = MB * BASE; |
95 | |
96 | ostringstream os; |
97 | |
98 | if (bytes >= GB) { |
99 | os << fixed << setprecision(2) << (bytes / GB) << " GB" ; |
100 | } else if (bytes >= MB) { |
101 | os << fixed << setprecision(2) << (bytes / MB) << " MB" ; |
102 | } else if (bytes >= KB) { |
103 | os << fixed << setprecision(2) << (bytes / KB) << " KB" ; |
104 | } else { |
105 | os << to_string(bytes) + " bytes" ; |
106 | } |
107 | return (os.str()); |
108 | } |
109 | |
110 | string StringUtil::Upper(const string &str) { |
111 | string copy(str); |
112 | transform(copy.begin(), copy.end(), copy.begin(), [](unsigned char c) { return toupper(c); }); |
113 | return (copy); |
114 | } |
115 | |
116 | string StringUtil::Lower(const string &str) { |
117 | string copy(str); |
118 | transform(copy.begin(), copy.end(), copy.begin(), [](unsigned char c) { return tolower(c); }); |
119 | return (copy); |
120 | } |
121 | |
122 | // http://stackoverflow.com/a/8098080 |
123 | string StringUtil::Format(const string fmt_str, ...) { |
124 | // Reserve two times as much as the length of the fmt_str |
125 | int final_n, n = ((int)fmt_str.size()) * 2; |
126 | string str; |
127 | unique_ptr<char[]> formatted; |
128 | va_list ap; |
129 | |
130 | while (1) { |
131 | // Wrap the plain char array into the unique_ptr |
132 | formatted.reset(new char[n + 1]); |
133 | strcpy(&formatted[0], fmt_str.c_str()); |
134 | va_start(ap, fmt_str); |
135 | final_n = vsnprintf(&formatted[0], n, fmt_str.c_str(), ap); |
136 | va_end(ap); |
137 | if (final_n < 0 || final_n >= n) |
138 | n += abs(final_n - n + 1); |
139 | else |
140 | break; |
141 | } |
142 | return string(formatted.get()); |
143 | } |
144 | |
145 | string StringUtil::VFormat(const string fmt_str, va_list args) { |
146 | va_list args_copy; |
147 | |
148 | unique_ptr<char[]> formatted; |
149 | // make a copy of the args as we can only use it once |
150 | va_copy(args_copy, args); |
151 | |
152 | // first get the amount of characters we need |
153 | const auto n = vsnprintf(nullptr, 0, fmt_str.c_str(), args) + 1; |
154 | |
155 | // now allocate the string and do the actual printing |
156 | formatted.reset(new char[n]); |
157 | (void)vsnprintf(&formatted[0], n, fmt_str.c_str(), args_copy); |
158 | return string(formatted.get()); |
159 | } |
160 | |
161 | vector<string> StringUtil::Split(const string &input, const string &split) { |
162 | vector<string> splits; |
163 | |
164 | idx_t last = 0; |
165 | idx_t input_len = input.size(); |
166 | idx_t split_len = split.size(); |
167 | while (last <= input_len) { |
168 | idx_t next = input.find(split, last); |
169 | if (next == string::npos) { |
170 | next = input_len; |
171 | } |
172 | |
173 | // Push the substring [last, next) on to splits |
174 | string substr = input.substr(last, next - last); |
175 | if (substr.empty() == false) { |
176 | splits.push_back(substr); |
177 | } |
178 | last = next + split_len; |
179 | } |
180 | return splits; |
181 | } |
182 | |
183 | string StringUtil::Replace(string source, const string &from, const string &to) { |
184 | if (from.empty()) |
185 | return source; |
186 | ; |
187 | idx_t start_pos = 0; |
188 | while ((start_pos = source.find(from, start_pos)) != string::npos) { |
189 | source.replace(start_pos, from.length(), to); |
190 | start_pos += to.length(); // In case 'to' contains 'from', like |
191 | // replacing 'x' with 'yx' |
192 | } |
193 | return source; |
194 | } |
195 | |