Doxygen
Loading...
Searching...
No Matches
dstring.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2015 by Dimitri van Heesch.
4 *
5 * Permission to use, copy, modify, and distribute this software and its
6 * documentation under the terms of the GNU General Public License is hereby
7 * granted. No representations are made about the suitability of this software
8 * for any purpose. It is provided "as is" without express or implied warranty.
9 * See the GNU General Public License for more details.
10 *
11 * Documents produced by Doxygen are derivative works derived from the
12 * input used in their production; they are not affected by this license.
13 *
14 */
15
16// own header
17#include "dstring.h"
18
19// standard includes
20#include <cctype>
21#include <climits>
22#include <cstdarg>
23#include <cstdio>
24#include <cstdlib>
25
26// other includes
27#include "regex.h"
28
29inline char toLowerChar(char c)
30{
31 return c>='A' && c<='Z' ? c|0x20 : c;
32}
33
34DString &DString::sprintf( const char *format, ... )
35{
36 va_list ap;
37 va_start( ap, format );
38 const size_t minlen=256;
39 size_t l = length();
40 if (l<minlen) { resize(minlen+1); l=minlen; }
41 int n=vsnprintf( rawData(), l+1, format, ap);
42 if (n<0) n=static_cast<int>(l);
43 resize(n);
44 va_end( ap );
45 return *this;
46}
47
48size_t DString::rfind_insensitive( char c, size_t index) const
49{
50 const char *b = data();
51 const char *pos = nullptr;
52 size_t len = length();
53 if (len==0) return -1; // empty string
54 if (index==npos) // start from end
55 {
56 index=len;
57 }
58 else if (index>len) // bad index
59 {
60 return DString::npos;
61 }
62 pos = b+index;
63 c = toLowerChar(c);
64 while ( pos>=b && toLowerChar(*pos)!=c) pos--;
65 return pos>=b ? static_cast<size_t>(pos-b) : DString::npos;
66}
67
68size_t DString::rfind_insensitive( const char *str, size_t index) const
69{
70 size_t slen = dstrlen(str);
71 size_t len = length();
72 if (slen>len) return DString::npos; // length of search string is longer than this string
73 if (index==DString::npos) index = len-slen; // start from end
74 else if (index>len) return DString::npos; // bad index
75 else if (index+slen>len) index = len-slen; // str would be too long
76 const char *pos = data()+index;
77 for (size_t i=index+1; i>0; )
78 {
79 --i;
80 if (dstrnicmp(pos--,str,slen)==0) return i;
81 }
82 return DString::npos;
83}
84
85int DString::contains( char c, bool cs ) const
86{
87 if (length()==0) return 0;
88 int count=0;
89 const char *pos = data();
90 if (cs)
91 {
92 while (*pos) if (*pos++ == c) count++;
93 }
94 else
95 {
96 c = toLowerChar(c);
97 while (*pos)
98 {
99 if (toLowerChar(*pos)==c) count++;
100 pos++;
101 }
102 }
103 return count;
104}
105
106int DString::contains( const char *str, bool cs ) const
107{
108 if (str==nullptr || length()==0) return 0;
109 int count=0;
110 const char *pos = data();
111 int len = dstrlen(str);
112 while (*pos)
113 {
114 if (cs)
115 {
116 if (dstrncmp(pos,str,len)==0) count++;
117 }
118 else
119 {
120 if (dstrnicmp(pos,str,len)==0) count++;
121 }
122 pos++;
123 }
124 return count;
125}
126
128{
129 if ( empty() ) // nothing to do
130 return *this;
131
132 DString result( length(), ExplicitSize );
133 const char *from = data();
134 char *to = result.rawData();
135 char *first = to;
136 while ( true )
137 {
138 while ( *from && disspace(*from) )
139 from++;
140 while ( *from && !disspace(*from) )
141 *to++ = *from++;
142 if ( *from )
143 *to++ = 0x20; // ' '
144 else
145 break;
146 }
147 if ( to > first && *(to-1) == 0x20 )
148 to--;
149 *to = '\0';
150 result.resize( static_cast<int>(to - result.data()) );
151 return result;
152}
153
154DString &DString::replace( size_t index, size_t len, const char *s)
155{
156 remove( index, len );
157 insert( index, s );
158 return *this;
159}
160
161static bool ok_in_base( char c, int base )
162{
163 if ( base <= 10 )
164 return c>='0' && c<='9' && (c-'0') < base;
165 else
166 return (c>='0' && c<='9') ||
167 (c >= 'a' && c < char('a'+base-10)) ||
168 (c >= 'A' && c < char('A'+base-10));
169}
170
171short DString::toShort(bool *ok, int base) const
172{
173 long v = toLong( ok, base );
174 if ( ok && *ok && (v < -32768 || v > 32767) ) {
175 *ok = false;
176 v = 0;
177 }
178 return static_cast<short>(v);
179}
180
181uint16_t DString::toUShort(bool *ok,int base) const
182{
183 unsigned long v = toULong( ok, base );
184 if ( ok && *ok && (v > 65535) ) {
185 *ok = false;
186 v = 0;
187 }
188 return static_cast<uint16_t>(v);
189}
190
191int DString::toInt(bool *ok, int base) const
192{
193 return static_cast<int>(toLong( ok, base ));
194}
195
196uint32_t DString::toUInt(bool *ok,int base) const
197{
198 return static_cast<uint32_t>(toULong( ok, base ));
199}
200
201
202long DString::toLong(bool *ok,int base) const
203{
204 const char *p = data();
205 long val=0;
206 int l = static_cast<int>(length());
207 const long max_mult = INT_MAX / base;
208 bool is_ok = false;
209 int neg = 0;
210 if ( !p )
211 goto bye;
212 while ( l && disspace(*p) ) // skip leading space
213 {
214 l--;
215 p++;
216 }
217 if ( l && *p == '-' )
218 {
219 l--;
220 p++;
221 neg = 1;
222 } else if ( *p == '+' )
223 {
224 l--;
225 p++;
226 }
227
228 // NOTE: toULong() code is similar
229 if ( !l || !ok_in_base(*p,base) )
230 {
231 goto bye;
232 }
233 while ( l && ok_in_base(*p,base) )
234 {
235 l--;
236 int dv = 0;
237 if ( *p>='0' && *p<='9' )
238 {
239 dv = *p-'0';
240 }
241 else
242 {
243 if ( *p >= 'a' && *p <= 'z' )
244 {
245 dv = *p - 'a' + 10;
246 }
247 else
248 {
249 dv = *p - 'A' + 10;
250 }
251 }
252 if ( val > max_mult || (val == max_mult && dv > (INT_MAX%base)+neg) )
253 {
254 goto bye;
255 }
256 val = base*val + dv;
257 p++;
258 }
259 if ( neg )
260 {
261 val = -val;
262 }
263 while ( l && disspace(*p) ) // skip trailing space
264 {
265 l--;
266 p++;
267 }
268 if ( !l )
269 {
270 is_ok = true;
271 }
272bye:
273 if ( ok )
274 {
275 *ok = is_ok;
276 }
277 return is_ok ? val : 0;
278}
279
280unsigned long DString::toULong(bool *ok,int base) const
281{
282 const char *p = data();
283 unsigned long val=0;
284 int l = static_cast<int>(length());
285 const unsigned long max_mult = 429496729; // UINT_MAX/10, rounded down
286 bool is_ok = false;
287 if ( !p )
288 {
289 goto bye;
290 }
291 while ( l && disspace(*p) ) // skip leading space
292 {
293 l--;
294 p++;
295 }
296 if ( *p == '+' )
297 {
298 l--;
299 p++;
300 }
301
302 // NOTE: toLong() code is similar
303 if ( !l || !ok_in_base(*p,base) )
304 {
305 goto bye;
306 }
307 while ( l && ok_in_base(*p,base) )
308 {
309 l--;
310 uint32_t dv = 0;
311 if ( *p>='0' && *p<='9' )
312 {
313 dv = *p-'0';
314 }
315 else
316 {
317 if ( *p >= 'a' && *p <= 'z' )
318 {
319 dv = *p - 'a' + 10;
320 }
321 else
322 {
323 dv = *p - 'A' + 10;
324 }
325 }
326 if ( val > max_mult || (val == max_mult && dv > (UINT_MAX%base)) )
327 {
328 goto bye;
329 }
330 val = base*val + dv;
331 p++;
332 }
333
334 while ( l && disspace(*p) ) // skip trailing space
335 {
336 l--;
337 p++;
338 }
339 if ( !l )
340 {
341 is_ok = true;
342 }
343bye:
344 if ( ok )
345 {
346 *ok = is_ok;
347 }
348 return is_ok ? val : 0;
349}
350
351uint64_t DString::toUInt64(bool *ok,int base) const
352{
353 const char *p = data();
354 uint64_t val=0;
355 int l = static_cast<int>(length());
356 const uint64_t max_mult = 1844674407370955161ULL; // ULLONG_MAX/10, rounded down
357 bool is_ok = false;
358 if ( !p )
359 {
360 goto bye;
361 }
362 while ( l && disspace(*p) ) // skip leading space
363 {
364 l--;
365 p++;
366 }
367 if ( *p == '+' )
368 {
369 l--;
370 p++;
371 }
372
373 // NOTE: toULong() code is similar
374 if ( !l || !ok_in_base(*p,base) )
375 {
376 goto bye;
377 }
378 while ( l && ok_in_base(*p,base) )
379 {
380 l--;
381 uint32_t dv = 0;
382 if ( *p>='0' && *p<='9' )
383 {
384 dv = *p-'0';
385 }
386 else
387 {
388 if ( *p >= 'a' && *p <= 'z' )
389 {
390 dv = *p - 'a' + 10;
391 }
392 else
393 {
394 dv = *p - 'A' + 10;
395 }
396 }
397 if ( val > max_mult || (val == max_mult && dv > (ULLONG_MAX%base)) )
398 {
399 goto bye;
400 }
401 val = base*val + dv;
402 p++;
403 }
404
405 while ( l && disspace(*p) ) // skip trailing space
406 {
407 l--;
408 p++;
409 }
410 if ( !l )
411 {
412 is_ok = true;
413 }
414bye:
415 if ( ok )
416 {
417 *ok = is_ok;
418 }
419 return is_ok ? val : 0;
420}
421
422//-------------------------------------------------
423
424char *dstrdup( const char *str )
425{
426 if ( !str ) return nullptr;
427 char *dst = new char[dstrlen(str)+1];
428 return strcpy( dst, str );
429}
430
431void dstrfree( const char *str )
432{
433 delete [](str);
434}
435
436char *dstrncpy( char *dst, const char *src, size_t len )
437{
438 if ( !src ) return nullptr;
439 strncpy( dst, src, len );
440 if ( len > 0 ) dst[len-1] = '\0';
441 return dst;
442}
443
444int dstricmp( const char *s1, const char *s2 )
445{
446 if ( !s1 || !s2 )
447 {
448 return s1 == s2 ? 0 : static_cast<int>(s2 - s1);
449 }
450 int res = 0;
451 char c = 0;
452 for ( ; !(res = ((c=toLowerChar(*s1)) - toLowerChar(*s2))); s1++, s2++ )
453 {
454 if ( !c ) // strings are equal
455 {
456 break;
457 }
458 }
459 return res;
460}
461
462int dstrnicmp( const char *s1, const char *s2, size_t len )
463{
464 if ( !s1 || !s2 )
465 {
466 return static_cast<int>(s2 - s1);
467 }
468 for ( ; len--; s1++, s2++ )
469 {
470 char c = toLowerChar(*s1);
471 int res = c-toLowerChar(*s2);
472 if ( res!=0 ) // strings are not equal
473 {
474 return res;
475 }
476 if ( c==0 ) // strings are equal
477 {
478 break;
479 }
480 }
481 return 0;
482}
483
484/// substitute all occurrences of \a src in \a s by \a dst
485DString substitute(const DString &s,const DString &src,const DString &dst)
486{
487 if (s.empty() || src.empty()) return s;
488 const char *q = nullptr, *p = nullptr;
489 size_t srcLen = src.length();
490 size_t dstLen = dst.length();
491 size_t resLen = 0;
492 if (srcLen!=dstLen)
493 {
494 int count = 0;
495 for (p = s.data(); (q=strstr(p,src.data()))!=nullptr; p=q+srcLen) count++;
496 resLen = s.length()+count*(dstLen-srcLen);
497 }
498 else // result has same size as s
499 {
500 resLen = s.length();
501 }
502 DString result(resLen, DString::ExplicitSize);
503 char *r = result.rawData();
504 for (p = s.data(); (q=strstr(p,src.data()))!=nullptr; p=q+srcLen)
505 {
506 int l = static_cast<int>(q-p);
507 memcpy(r,p,l);
508 r+=l;
509
510 if (dstLen>0) memcpy(r,dst.data(),dstLen);
511 r+=dstLen;
512 }
513 if (r)
514 {
515 dstrcpy(r,p);
516 }
517 //printf("substitute(%s,%s,%s)->%s\n",s,src,dst,result.data());
518 return result;
519}
520
521
522/// substitute all occurrences of \a src in \a s by \a dst, but skip
523/// each consecutive sequence of \a src where the number consecutive
524/// \a src matches \a skip_seq; if \a skip_seq is negative, skip any
525/// number of consecutive \a src
526DString substitute(const DString &s,const DString &src,const DString &dst,int skip_seq)
527{
528 if (s.empty() || src.empty()) return s;
529 const char *p = nullptr, *q = nullptr;
530 size_t srcLen = src.length();
531 size_t dstLen = dst.length();
532 size_t resLen = 0;
533 if (srcLen!=dstLen)
534 {
535 int count = 0;
536 for (p=s.data(); (q=strstr(p,src.data()))!=nullptr; p=q+srcLen) count++;
537 resLen = s.length()+count*(dstLen-srcLen);
538 }
539 else // result has same size as s
540 {
541 resLen = s.length();
542 }
543 DString result(resLen, DString::ExplicitSize);
544 char *r = result.rawData();
545 for (p = s.data(); (q=strstr(p,src.data()))!=nullptr; p=q+srcLen)
546 {
547 // search a consecutive sequence of src
548 int seq = 0, skip = 0;
549 if (skip_seq)
550 {
551 for (const char *n=q+srcLen; dstrncmp(n,src.data(),srcLen)==0; seq=1+skip, n+=srcLen)
552 ++skip; // number of consecutive src after the current one
553
554 // verify the allowed number of consecutive src to skip
555 if (skip_seq > 0 && skip_seq != seq)
556 seq = skip = 0;
557 }
558
559 // skip a consecutive sequence of src when necessary
560 int l = static_cast<int>((q + seq * srcLen)-p);
561 memcpy(r,p,l);
562 r+=l;
563
564 if (skip)
565 {
566 // skip only the consecutive src found after the current one
567 q += skip * srcLen;
568 // the next loop will skip the current src, aka (p=q+srcLen)
569 continue;
570 }
571
572 if (dstLen>0) memcpy(r,dst.data(),dstLen);
573 r+=dstLen;
574 }
575 dstrcpy(r,p);
576 result.resize(strlen(result.data()));
577 //printf("substitute(%s,%s,%s)->%s\n",s,src,dst,result.data());
578 return result;
579}
580
582{
583 if (empty()) return DString();
584 const std::string &s = m_rep;
585 int end=static_cast<int>(s.length());
586 int start=0,p=0;
587 // skip leading empty lines
588 for (;;)
589 {
590 int c;
591 while ((c=s[p]) && (c==' ' || c=='\t')) p++;
592 if (s[p]=='\n')
593 {
594 start=++p;
595 }
596 else
597 {
598 break;
599 }
600 }
601 // skip trailing empty lines
602 p=end-1;
603 if (p>=start && s.at(p)=='\n') p--;
604 while (p>=start)
605 {
606 int c;
607 while ((c=s[p]) && (c==' ' || c=='\t')) p--;
608 if (s[p]=='\n')
609 {
610 end=p+1;
611 }
612 else
613 {
614 break;
615 }
616 p--;
617 }
618 //printf("stripLeadingAndTrailingEmptyLines(%d-%d)\n",start,end);
619 return s.substr(start,end-start);
620}
621
623{
624 DString result;
625 int residual = n;
626
627 char modVal[2];
628 modVal[1] = 0;
629 while (residual > 0)
630 {
631 modVal[0] = (upper ? 'A': 'a') + (residual-1)%26;
632 result = modVal + result;
633 residual = (residual-1) / 26;
634 }
635 return result;
636}
637
639{
640 static const char *str_romans_upper[] = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" };
641 static const char *str_romans_lower[] = { "m", "cm", "d", "cd", "c", "xc", "l", "xl", "x", "ix", "v", "iv", "i" };
642 static const int values[] = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
643 static const char **str_romans = upper ? str_romans_upper : str_romans_lower;
644
645 DString result;
646 int residual = n;
647
648 for (int i = 0; i < 13; ++i)
649 {
650 while (residual - values[i] >= 0)
651 {
652 result += str_romans[i];
653 residual -= values[i];
654 }
655 }
656
657 return result;
658}
659
660bool DString::findAndRemoveWord(const char *word)
661{
662 static reg::Ex re(R"(\s*(<\a+>)\s*)");
663 std::string s = m_rep;
664 reg::Iterator it(s,re);
666 std::string result;
667 bool found=false;
668 size_t p=0;
669 for ( ; it!=end ; ++it)
670 {
671 const auto &match = *it;
672 std::string part = match[1].str();
673 if (part!=word)
674 {
675 size_t i = match.position();
676 size_t l = match.length();
677 result+=s.substr(p,i-p);
678 result+=match.str();
679 p=i+l;
680 }
681 else
682 {
683 found=true;
684 size_t i = match[1].position();
685 size_t l = match[1].length();
686 result+=s.substr(p,i-p);
687 p=i+l;
688 }
689 }
690 result+=s.substr(p);
691 m_rep = DString(result).simplifyWhiteSpace().str();
692 return found;
693}
694
695bool DString::containsWord(const char *word) const
696{
697 if (m_rep.empty() || word==nullptr) return false;
698 static const reg::Ex re(R"(\a+)");
699 for (reg::Iterator it(m_rep,re) ; it!=reg::Iterator() ; ++it)
700 {
701 if (it->str()==word) return true;
702 }
703 return false;
704}
705
706
A String class for use with Doxygen wrapping std::string and adding some additional functionality off...
Definition dstring.h:84
static DString integerToAlpha(int n, bool upper=true)
Definition dstring.cpp:622
void resize(size_t newlen)
Definition dstring.h:209
DString stripLeadingAndTrailingEmptyLines() const
Definition dstring.cpp:581
DString()=default
unsigned long toULong(bool *ok=nullptr, int base=10) const
Definition dstring.cpp:280
DString upper() const
Definition dstring.h:331
uint16_t toUShort(bool *ok=nullptr, int base=10) const
Definition dstring.cpp:181
DString simplifyWhiteSpace() const
return a copy of this string with leading and trailing whitespace removed and multiple internal white...
Definition dstring.cpp:127
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:148
iterator end()
Definition dstring.h:183
DString & replace(size_t index, size_t len, const char *s)
Definition dstring.cpp:154
uint32_t toUInt(bool *ok=nullptr, int base=10) const
Definition dstring.cpp:196
bool containsWord(const char *word) const
returns true if this string contains word as a whole word, false otherwise.
Definition dstring.cpp:695
static DString integerToRoman(int n, bool upper=true)
Definition dstring.cpp:638
uint64_t toUInt64(bool *ok=nullptr, int base=10) const
Definition dstring.cpp:351
char * rawData()
Returns a writable pointer to the data.
Definition dstring.h:166
short toShort(bool *ok=nullptr, int base=10) const
Definition dstring.cpp:171
DString & remove(size_t index, size_t len)
Definition dstring.h:535
static constexpr size_t npos
value used to indicate 'not found' or 'to the end of the string', matching std::string::npos
Definition dstring.h:178
bool findAndRemoveWord(const char *word)
removes occurrences of whole word from this string, while keeping internal spaces and reducing multip...
Definition dstring.cpp:660
size_t rfind_insensitive(char c, size_t pos=npos) const
Definition dstring.cpp:48
std::string m_rep
Definition dstring.h:711
int contains(char c, bool cs=true) const
Definition dstring.cpp:85
DString & sprintf(const char *format,...)
Definition dstring.cpp:34
int toInt(bool *ok=nullptr, int base=10) const
Definition dstring.cpp:191
@ ExplicitSize
Definition dstring.h:131
long toLong(bool *ok=nullptr, int base=10) const
Definition dstring.cpp:202
const std::string & str() const
Definition dstring.h:645
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition dstring.h:157
DString & insert(size_t index, const DString &s)
Definition dstring.h:425
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:151
Class representing a regular expression.
Definition regex.h:39
Class to iterate through matches.
Definition regex.h:239
void dstrfree(const char *str)
Frees the memory allocated using dstrdup().
Definition dstring.cpp:431
char * dstrdup(const char *str)
Definition dstring.cpp:424
static bool ok_in_base(char c, int base)
Definition dstring.cpp:161
char * dstrncpy(char *dst, const char *src, size_t len)
Definition dstring.cpp:436
char toLowerChar(char c)
Definition dstring.cpp:29
int dstrnicmp(const char *s1, const char *s2, size_t len)
Definition dstring.cpp:462
DString substitute(const DString &s, const DString &src, const DString &dst)
substitute all occurrences of src in s by dst
Definition dstring.cpp:485
int dstricmp(const char *s1, const char *s2)
Definition dstring.cpp:444
int dstrnicmp(const char *str1, const char *str2, size_t len)
Definition dstring.cpp:462
char * dstrcpy(char *dst, const char *src)
Definition dstring.h:42
int dstrncmp(const char *str1, const char *str2, size_t len)
Definition dstring.h:56
uint32_t dstrlen(const char *str)
Returns the length of string str, or 0 if a null pointer is passed.
Definition dstring.h:39
bool disspace(char c)
Definition dstring.h:62