CBMC
Loading...
Searching...
No Matches
string_utils.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module:
4
5Author: Daniel Poetzl
6
7\*******************************************************************/
8
9#include "string_utils.h"
10#include "exception_utils.h"
11#include "invariant.h"
12
13#include <algorithm>
14#include <cctype>
15#include <iomanip>
16
21std::string strip_string(std::string_view s)
22{
23 auto pred=[](char c){ return std::isspace(c); };
24
25 std::string_view::const_iterator left =
26 std::find_if_not(s.begin(), s.end(), pred);
27 if(left==s.end())
28 return "";
29
30 std::size_t i = std::distance(s.begin(), left);
31
32 std::string_view::const_reverse_iterator right =
33 std::find_if_not(s.rbegin(), s.rend(), pred);
34 std::size_t j = std::distance(right, s.rend()) - 1;
35
36 // copy happens here; this could return a view in the future
37 return std::string{s.substr(i, (j - i + 1))};
38}
39
41 std::string_view s,
42 char delim,
43 std::vector<std::string> &result,
44 bool strip,
45 bool remove_empty)
46{
47 PRECONDITION(result.empty());
48 // delim can't be a space character if using strip
49 PRECONDITION(!std::isspace(delim) || !strip);
50
51 if(s.empty())
52 {
53 if(!remove_empty)
54 result.push_back("");
55 return;
56 }
57
58 std::size_t n = s.length();
59 INVARIANT(n > 0, "Empty string case should already be handled");
60
61 std::size_t start = 0;
62 std::size_t i;
63
64 for(i=0; i<n; i++)
65 {
66 if(s[i]==delim)
67 {
68 // result owns std::strings rather than string_views: callers
69 // routinely pass a temporary as `s` (e.g. some_function()
70 // returning std::string), so the input may not outlive the
71 // result.
72 std::string new_s = std::string{s.substr(start, i - start)};
73
74 if(strip)
76
77 if(!remove_empty || !new_s.empty())
78 result.push_back(std::move(new_s));
79
80 start=i+1;
81 }
82 }
83
84 // result owns std::strings rather than string_views: see the
85 // comment above the first push_back in the loop.
86 std::string new_s = std::string{s.substr(start, n - start)};
87
88 if(strip)
90
91 if(!remove_empty || !new_s.empty())
92 result.push_back(std::move(new_s));
93
94 if(!remove_empty && result.empty())
95 result.push_back("");
96}
97
99 std::string_view s,
100 char delim,
101 std::string &left,
102 std::string &right,
103 bool strip)
104{
105 // delim can't be a space character if using strip
106 PRECONDITION(!std::isspace(delim) || !strip);
107
108 std::vector<std::string> result = split_string(s, delim, strip);
109
110 if(result.size() != 2)
111 {
113 "expected string '" + std::string{s} +
114 "' to contain two substrings "
115 "delimited by " +
116 delim + " but has " + std::to_string(result.size())};
117 }
118
119 left=result[0];
120 right=result[1];
121}
122
123std::vector<std::string>
124split_string(std::string_view s, char delim, bool strip, bool remove_empty)
125{
126 std::vector<std::string> result;
127 split_string(s, delim, result, strip, remove_empty);
128 return result;
129}
130
131std::string trim_from_last_delimiter(std::string_view s, const char delim)
132{
133 std::string result;
134 const size_t index=s.find_last_of(delim);
135 if(index!=std::string::npos)
136 result=s.substr(0, index);
137 return result;
138}
139
140std::string escape(std::string_view s)
141{
142 std::string result;
143
144 for(std::size_t i=0; i<s.size(); i++)
145 {
146 if(s[i]=='\\' || s[i]=='"')
147 result+='\\';
148
149 result+=s[i];
150 }
151
152 return result;
153}
154
155std::string escape_non_alnum(std::string_view to_escape)
156{
157 std::ostringstream escaped;
158 for(auto &ch : to_escape)
159 {
160 // `ch` may have a negative value in the case of utf-8 encodings of
161 // characters above unicode code point 127. The following line maps these
162 // negative values to positive values in the 128-255 range, using a
163 // `static_cast`. This is neccessary in order to avoid undefined behaviour
164 // in `isalnum`. The positive values are then stored in an integer using a
165 // widening initialisation so that the stream insertion operator prints them
166 // as numbers rather than characters.
167 const int uch{static_cast<unsigned char>(ch)};
168 if(ch == '_')
169 escaped << "__";
170 else if(isalnum(uch))
171 escaped << ch;
172 else
173 escaped << '_' << std::hex << std::setfill('0') << std::setw(2) << uch;
174 }
175 return escaped.str();
176}
177
178std::string capitalize(std::string_view s)
179{
180 if(s.empty())
181 return std::string{};
182 std::string capitalized = std::string{s}; // copy
184 return capitalized;
185}
186
187std::string wrap_line(
188 const std::string &line,
189 const std::size_t left_margin,
190 const std::size_t width)
191{
192 return wrap_line(line.cbegin(), line.cend(), left_margin, width);
193}
194
195std::string wrap_line(
196 std::string::const_iterator left,
197 std::string::const_iterator right,
198 const std::size_t left_margin,
199 const std::size_t width)
200{
201 PRECONDITION(left_margin < width);
202
203 const std::size_t column_width = width - left_margin;
204 const std::string margin(left_margin, ' ');
205
206 auto distance = std::distance(left, right);
207 CHECK_RETURN(distance > 0);
208
209 std::string result;
210
211 if(static_cast<std::size_t>(distance) <= column_width)
212 {
213 result.append(margin);
214 result.append(left, right);
215
216 return result;
217 }
218
219 auto it_line_begin = left;
220
221 do
222 {
223 // points to the first character past the current column
224 auto it = it_line_begin + column_width;
225
226 auto rit_r = std::reverse_iterator<decltype(it)>(it) - 1;
227 auto rit_l = rit_r + column_width;
228
229 auto rit_space = std::find(rit_r, rit_l, ' ');
230
231 if(rit_space != rit_l)
232 {
233 auto it_space = rit_space.base() - 1;
234 CHECK_RETURN(*it_space == ' ');
235
236 result.append(margin);
237 result.append(it_line_begin, it_space);
238 result.append("\n");
239
241 }
242 else
243 {
244 // we have not found a space, thus cannot wrap this line
245 result.clear();
246 result.append(left, right);
247
248 return result;
249 }
250 } while(static_cast<std::size_t>(std::distance(it_line_begin, right)) >
252
253 result.append(margin);
254 result.append(it_line_begin, right);
255
256 return result;
257}
virtual void clear()
Reset the abstract state.
Definition ai.h:269
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:566
Thrown when failing to deserialize a value from some low level format, like JSON or raw bytes.
int toupper(int c)
Definition ctype.c:134
int isalnum(int c)
Definition ctype.c:4
#define CHECK_RETURN(CONDITION)
Definition invariant.h:495
#define PRECONDITION(CONDITION)
Definition invariant.h:463
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
Definition invariant.h:423
void split_string(std::string_view s, char delim, std::vector< std::string > &result, bool strip, bool remove_empty)
std::string capitalize(std::string_view s)
std::string escape_non_alnum(std::string_view to_escape)
Replace non-alphanumeric characters with _xx escapes, where xx are hex digits.
std::string trim_from_last_delimiter(std::string_view s, const char delim)
std::string escape(std::string_view s)
Generic escaping of strings; this is not meant to be a particular programming language.
std::string wrap_line(const std::string &line, const std::size_t left_margin, const std::size_t width)
Wrap line at spaces to not extend past the right margin, and include given padding with spaces to the...
std::string strip_string(std::string_view s)
Remove all whitespace characters from either end of a string.