src/String.cc

changeset 88
5def6ff8b466
child 89
029a330a9bef
equal deleted inserted replaced
87:8f65914e7046 88:5def6ff8b466
1 /*
2 Copyright 2012-2014 Santeri Piippo
3 All rights reserved.
4
5 Redistribution and use in source and binary forms, with or without
6 modification, are permitted provided that the following conditions
7 are met:
8
9 1. Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11 2. Redistributions in binary form must reproduce the above copyright
12 notice, this list of conditions and the following disclaimer in the
13 documentation and/or other materials provided with the distribution.
14 3. The name of the author may not be used to endorse or promote products
15 derived from this software without specific prior written permission.
16
17 THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <cstring>
30 #include "Main.h"
31 #include "String.h"
32
33 // =============================================================================
34 //
35 int String::Compare (const String& other) const
36 {
37 return mString.compare (other.STDString());
38 }
39
40 // =============================================================================
41 //
42 void String::Trim (int n)
43 {
44 if (n > 0)
45 mString = Mid (0, Length() - n).STDString();
46 else
47 mString = Mid (n, -1).STDString();
48 }
49
50 // =============================================================================
51 //
52 String String::Strip (const List< char >& unwanted)
53 {
54 String copy (mString);
55
56 for (char c : unwanted)
57 for (int i = 0; i < copy.Length(); ++i)
58 if (copy[i] == c)
59 copy.RemoveAt (i);
60
61 /*
62 while(( pos = copy.first( c )) != -1 )
63 copy.erase( pos );
64 */
65
66 return copy;
67 }
68
69 // =============================================================================
70 //
71 String String::ToUppercase() const
72 {
73 String newstr = mString;
74
75 for (char & c : newstr)
76 if (c >= 'a' && c <= 'z')
77 c -= 'a' - 'A';
78
79 return newstr;
80 }
81
82 // =============================================================================
83 //
84 String String::ToLowercase() const
85 {
86 String newstr = mString;
87
88 for (char & c : newstr)
89 if (c >= 'A' && c <= 'Z')
90 c += 'a' - 'A';
91
92 return newstr;
93 }
94
95 // =============================================================================
96 //
97 StringList String::Split (char del) const
98 {
99 String delimstr;
100 delimstr += del;
101 return Split (delimstr);
102 }
103
104 // =============================================================================
105 //
106 StringList String::Split (String del) const
107 {
108 StringList res;
109 long a = 0;
110
111 // Find all separators and store the text left to them.
112 for (;;)
113 {
114 long b = FirstIndexOf (del, a);
115
116 if (b == -1)
117 break;
118
119 String sub = Mid (a, b);
120
121 if (sub.Length() > 0)
122 res.Append (Mid (a, b));
123
124 a = b + del.Length();
125 }
126
127 // Add the string at the right of the last separator
128 if (a < (int) Length())
129 res.Append (Mid (a, Length()));
130
131 return res;
132 }
133
134 // =============================================================================
135 //
136 void String::Replace (const char* a, const char* b)
137 {
138 long pos;
139
140 while ( (pos = FirstIndexOf (a)) != -1)
141 mString = mString.replace (pos, strlen (a), b);
142 }
143
144 // =============================================================================
145 //
146 int String::Count (char needle) const
147 {
148 int needles = 0;
149
150 for (const char & c : mString)
151 if (c == needle)
152 needles++;
153
154 return needles;
155 }
156
157 // =============================================================================
158 //
159 String String::Mid (long a, long b) const
160 {
161 if (b == -1)
162 b = Length();
163
164 if (b == a)
165 return "";
166
167 if (b < a)
168 {
169 // Swap the variables
170 int c = a;
171 a = b;
172 b = c;
173 }
174
175 char* newstr = new char[b - a + 1];
176 strncpy (newstr, mString.c_str() + a, b - a);
177 newstr[b - a] = '\0';
178
179 String other (newstr);
180 delete[] newstr;
181 return other;
182 }
183
184 // =============================================================================
185 //
186 int String::WordPosition (int n) const
187 {
188 int count = 0;
189
190 for (int i = 0; i < Length(); ++i)
191 {
192 if (mString[i] != ' ')
193 continue;
194
195 if (++count < n)
196 continue;
197
198 return i;
199 }
200
201 return -1;
202 }
203
204 // =============================================================================
205 //
206 int String::FirstIndexOf (const char* c, int a) const
207 {
208 for (; a < Length(); a++)
209 if (mString[a] == c[0] && strncmp (mString.c_str() + a, c, strlen (c)) == 0)
210 return a;
211
212 return -1;
213 }
214
215 // =============================================================================
216 //
217 int String::LastIndexOf (const char* c, int a) const
218 {
219 if (a == -1 || a >= Length())
220 a = Length() - 1;
221
222 for (; a > 0; a--)
223 if (mString[a] == c[0] && strncmp (mString.c_str() + a, c, strlen (c)) == 0)
224 return a;
225
226 return -1;
227 }
228
229 // =============================================================================
230 //
231 void String::Dump() const
232 {
233 Print ("`%1`:\n", CString());
234 int i = 0;
235
236 for (char u : mString)
237 Print ("\t%1. [%d2] `%3`\n", i++, u, String (u));
238 }
239
240 // =============================================================================
241 //
242 long String::ToLong (bool* ok, int base) const
243 {
244 errno = 0;
245 char* endptr;
246 long i = strtol (mString.c_str(), &endptr, base);
247
248 if (ok)
249 *ok = (errno == 0 && *endptr == '\0');
250
251 return i;
252 }
253
254 // =============================================================================
255 //
256 float String::ToFloat (bool* ok) const
257 {
258 errno = 0;
259 char* endptr;
260 float i = strtof (mString.c_str(), &endptr);
261
262 if (ok)
263 *ok = (errno == 0 && *endptr == '\0');
264
265 return i;
266 }
267
268 // =============================================================================
269 //
270 double String::ToDouble (bool* ok) const
271 {
272 errno = 0;
273 char* endptr;
274 double i = strtod (mString.c_str(), &endptr);
275
276 if (ok)
277 *ok = (errno == 0 && *endptr == '\0');
278
279 return i;
280 }
281
282 // =============================================================================
283 //
284 String String::operator+ (const String& data) const
285 {
286 String newString = *this;
287 newString.Append (data);
288 return newString;
289 }
290
291 // =============================================================================
292 //
293 String String::operator+ (const char* data) const
294 {
295 String newstr = *this;
296 newstr.Append (data);
297 return newstr;
298 }
299
300 // =============================================================================
301 //
302 bool String::IsNumeric() const
303 {
304 bool gotDot = false;
305
306 for (const char & c : mString)
307 {
308 // Allow leading hyphen for negatives
309 if (&c == &mString[0] && c == '-')
310 continue;
311
312 // Check for decimal point
313 if (!gotDot && c == '.')
314 {
315 gotDot = true;
316 continue;
317 }
318
319 if (c >= '0' && c <= '9')
320 continue; // Digit
321
322 // If the above cases didn't catch this character, it was
323 // illegal and this is therefore not a number.
324 return false;
325 }
326
327 return true;
328 }
329
330 // =============================================================================
331 //
332 bool String::EndsWith (const String& other)
333 {
334 if (Length() < other.Length())
335 return false;
336
337 const int ofs = Length() - other.Length();
338 return strncmp (CString() + ofs, other.CString(), other.Length()) == 0;
339 }
340
341 // =============================================================================
342 //
343 bool String::StartsWith (const String& other)
344 {
345 if (Length() < other.Length())
346 return false;
347
348 return strncmp (CString(), other.CString(), other.Length()) == 0;
349 }
350
351 // =============================================================================
352 //
353 void String::SPrintf (const char* fmtstr, ...)
354 {
355 char* buf;
356 int bufsize = 256;
357 va_list va;
358 va_start (va, fmtstr);
359
360 do
361 buf = new char[bufsize];
362 while (vsnprintf (buf, bufsize, fmtstr, va) >= bufsize);
363
364 va_end (va);
365 mString = buf;
366 delete[] buf;
367 }
368
369 // =============================================================================
370 //
371 String StringList::Join (const String& delim)
372 {
373 String result;
374
375 for (const String& it : GetDeque())
376 {
377 if (result.IsEmpty() == false)
378 result += delim;
379
380 result += it;
381 }
382
383 return result;
384 }
385
386 // =============================================================================
387 //
388 bool String::MaskAgainst (const String& pattern) const
389 {
390 // Elevate to uppercase for case-insensitive matching
391 String pattern_upper = pattern.ToUppercase();
392 String this_upper = ToUppercase();
393 const char* maskstring = pattern_upper.CString();
394 const char* mptr = &maskstring[0];
395
396 for (const char* sptr = this_upper.CString(); *sptr != '\0'; sptr++)
397 {
398 if (*mptr == '?')
399 {
400 if (*(sptr + 1) == '\0')
401 {
402 // ? demands that there's a character here and there wasn't.
403 // Therefore, mask matching fails
404 return false;
405 }
406 }
407 elif (*mptr == '*')
408 {
409 char end = *(++mptr);
410
411 // If '*' is the final character of the message, all of the remaining
412 // string matches against the '*'. We don't need to bother checking
413 // the string any further.
414 if (end == '\0')
415 return true;
416
417 // Skip to the end character
418 while (*sptr != end && *sptr != '\0')
419 sptr++;
420
421 // String ended while the mask still had stuff
422 if (*sptr == '\0')
423 return false;
424 }
425 elif (*sptr != *mptr)
426 return false;
427
428 mptr++;
429 }
430
431 return true;
432 }
433
434 // =============================================================================
435 //
436 String String::FromNumber (int a)
437 {
438 char buf[32];
439 sprintf (buf, "%d", a);
440 return String (buf);
441 }
442
443 // =============================================================================
444 //
445 String String::FromNumber (long a)
446 {
447 char buf[32];
448 sprintf (buf, "%ld", a);
449 return String (buf);
450 }

mercurial