VideoTools
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Pages
gstr.cpp
Go to the documentation of this file.
1 //
2 // Copyright (C) 2017 Graeme Walker
3 //
4 // This program is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16 // ===
17 //
18 // gstr.cpp
19 //
20 
21 #include "gdef.h"
22 #include "gstr.h"
23 #include "gdebug.h"
24 #include <cmath>
25 #include <algorithm>
26 #include <iterator>
27 #include <functional>
28 #include <ctype.h>
29 #include <iomanip>
30 #include <climits>
31 #include <string>
32 #include <sstream>
33 
34 std::string G::Str::escaped( const std::string & s_in )
35 {
36  std::string s( s_in ) ;
37  escape( s ) ;
38  return s ;
39 }
40 
41 std::string G::Str::escaped( const std::string & s_in , char c_escape , const std::string & specials_in , const std::string & specials_out )
42 {
43  std::string s( s_in ) ;
44  escape( s , c_escape , specials_in , specials_out ) ;
45  return s ;
46 }
47 
48 std::string G::Str::escaped( const std::string & s_in , char c_escape , const char * specials_in , const char * specials_out )
49 {
50  std::string s( s_in ) ;
51  escape( s , c_escape , specials_in , specials_out ) ;
52  return s ;
53 }
54 
55 void G::Str::escape( std::string & s )
56 {
57  escapeImp( s , '\\' , "\\\r\n\t" , "\\rnt0" , true ) ;
58 }
59 
60 void G::Str::escape( std::string & s , char c_escape , const char * specials_in , const char * specials_out )
61 {
62  bool with_nul = std::strlen(specials_in) != std::strlen(specials_out) ;
63  escapeImp( s , c_escape , specials_in , specials_out , with_nul ) ;
64 }
65 
66 void G::Str::escape( std::string & s , char c_escape , const std::string & specials_in , const std::string & specials_out )
67 {
68  G_ASSERT( specials_in.length() == specials_out.length() ) ;
69  bool with_nul = !specials_in.empty() && specials_in.at(specials_in.length()-1U) == '\0' ;
70  escapeImp( s , c_escape , specials_in.c_str() , specials_out.c_str() , with_nul ) ;
71 }
72 
73 void G::Str::escapeImp( std::string & s , char c_escape , const char * specials_in , const char * specials_out , bool with_nul )
74 {
75  G_ASSERT( specials_in != nullptr && specials_out != nullptr && (std::strlen(specials_out)-std::strlen(specials_in)) <= 1U ) ;
76  size_type pos = 0U ;
77  for(;;)
78  {
79  char c_in = '\0' ;
80  pos = s.find_first_of( specials_in , pos ) ;
81  if( pos != std::string::npos )
82  c_in = s.at( pos ) ;
83  else if( with_nul )
84  pos = s.find( '\0' , pos ) ;
85  if( pos == std::string::npos )
86  break ;
87 
88  G_ASSERT( std::strchr(specials_in,c_in) != nullptr ) ;
89  const size_t special_index = std::strchr(specials_in,c_in) - specials_in ;
90  G_ASSERT( special_index < std::strlen(specials_out) ) ;
91 
92  s.insert( pos , 1U , c_escape ) ;
93  pos++ ;
94  s.at(pos) = specials_out[special_index] ;
95  pos++ ;
96  }
97 }
98 
99 void G::Str::unescape( std::string & s )
100 {
101  unescape( s , '\\' , "rnt0" , "\r\n\t" ) ;
102 }
103 
104 void G::Str::unescape( std::string & s , char c_escape , const char * specials_in , const char * specials_out )
105 {
106  G_ASSERT( specials_in != nullptr && specials_out != nullptr && (std::strlen(specials_in)-std::strlen(specials_out)) <= 1U ) ;
107  bool escaped = false ;
108  std::string::iterator out = s.begin() ; // output in-place
109  for( std::string::iterator in = s.begin() ; in != s.end() ; ++in )
110  {
111  const char * specials_in_p = std::strchr( specials_in , *in ) ;
112  const char * specials_out_p = specials_in_p ? (specials_out+(specials_in_p-specials_in)) : nullptr ;
113  if( escaped && specials_out_p )
114  *out++ = *specials_out_p , escaped = false ;
115  else if( escaped && *in == c_escape )
116  *out++ = c_escape , escaped = false ;
117  else if( escaped )
118  *out++ = *in , escaped = false ;
119  else if( *in == c_escape )
120  escaped = true ;
121  else
122  *out++ = *in , escaped = false ;
123  }
124  if( out != s.end() ) s.erase( out , s.end() ) ;
125 }
126 
127 std::string G::Str::unescaped( const std::string & s_in )
128 {
129  std::string s( s_in ) ;
130  unescape( s ) ;
131  return s ;
132 }
133 
134 bool G::Str::replace( std::string & s , const std::string & from , const std::string & to , size_type * pos_p )
135 {
136  if( from.length() == 0 )
137  return false ;
138 
139  size_type pos = pos_p == nullptr ? 0 : *pos_p ;
140  if( pos >= s.length() )
141  return false ;
142 
143  pos = s.find( from , pos ) ;
144  if( pos == std::string::npos )
145  {
146  return false ;
147  }
148  else
149  {
150  s.replace( pos , from.length() , to ) ;
151  if( pos_p != nullptr )
152  *pos_p = pos + to.length() ;
153  return true ;
154  }
155 }
156 
157 unsigned int G::Str::replaceAll( std::string & s , const std::string & from , const std::string & to )
158 {
159  unsigned int count = 0U ;
160  for( size_type pos = 0U ; replace(s,from,to,&pos) ; count++ )
161  ; // no-op
162  return count ;
163 }
164 
165 unsigned int G::Str::replaceAll( std::string & s , const char * from , const char * to )
166 {
167  if( s.find(from) != std::string::npos )
168  {
169  unsigned int count = 0U ;
170  for( size_type pos = 0U ; replace(s,from,to,&pos) ; count++ )
171  ; // no-op
172  return count ;
173  }
174  else
175  {
176  return 0U ;
177  }
178 }
179 
180 void G::Str::removeAll( std::string & s , char c )
181 {
182  const std::string::iterator end = s.end() ;
183  s.erase( std::remove_if( s.begin() , end , std::bind1st(std::equal_to<char>(),c) ) , end ) ;
184 }
185 
186 void G::Str::trimLeft( std::string & s , const std::string & ws , size_type limit )
187 {
188  size_type n = s.find_first_not_of( ws ) ;
189  if( limit != 0U && ( n == std::string::npos || n > limit ) )
190  n = limit >= s.length() ? std::string::npos : limit ;
191  if( n == std::string::npos )
192  s = std::string() ;
193  else if( n != 0U )
194  s.erase( 0U , n ) ;
195 }
196 
197 void G::Str::trimRight( std::string & s , const std::string & ws , size_type limit )
198 {
199  size_type n = s.find_last_not_of( ws ) ;
200  if( limit != 0U && ( n == std::string::npos || s.length() > (limit+n+1U) ) )
201  n = limit >= s.length() ? std::string::npos : (s.length()-limit-1U) ;
202  if( n == std::string::npos )
203  s = std::string() ;
204  else if( (n+1U) != s.length() )
205  s.resize( n + 1U ) ;
206 }
207 
208 void G::Str::trim( std::string & s , const std::string & ws )
209 {
210  trimLeft(s,ws) ; trimRight(s,ws) ;
211 }
212 
213 std::string G::Str::trimmed( const std::string & s_in , const std::string & ws )
214 {
215  std::string s( s_in ) ;
216  trim( s , ws ) ;
217  return s ;
218 }
219 
220 namespace
221 {
222  struct IsDigit : std::unary_function<char,bool>
223  {
224  bool operator()( char c ) const { return !! isdigit( c ) ; }
225  } ;
226 }
227 
228 bool G::Str::isNumeric( const std::string & s , bool allow_minus_sign )
229 {
230  const std::string::const_iterator end = s.end() ;
231  std::string::const_iterator p = s.begin() ;
232  if( allow_minus_sign && p != end && *p == '-' ) ++p ;
233  return std::find_if( p , end , std::not1(IsDigit()) ) == end ;
234 }
235 
236 namespace
237 {
238  struct IsPrintableAscii : std::unary_function<char,bool>
239  {
240  bool operator()( char c ) const { return c >= 0x20 && c < 0x7f ; }
241  } ;
242 }
243 
244 bool G::Str::isPrintableAscii( const std::string & s )
245 {
246  const std::string::const_iterator end = s.end() ;
247  return std::find_if( s.begin() , end , std::not1(IsPrintableAscii()) ) == end ;
248 }
249 
250 bool G::Str::isInt( const std::string & s )
251 {
252  bool overflow = false ;
253  bool invalid = false ;
254  toIntImp( s , overflow , invalid ) ;
255  return !overflow && !invalid ;
256 }
257 
258 bool G::Str::isUShort( const std::string & s )
259 {
260  bool overflow = false ;
261  bool invalid = false ;
262  toUShortImp( s , overflow , invalid ) ;
263  return !overflow && !invalid ;
264 }
265 
266 bool G::Str::isUInt( const std::string & s )
267 {
268  bool overflow = false ;
269  bool invalid = false ;
270  toUIntImp( s , overflow , invalid ) ;
271  return !overflow && !invalid ;
272 }
273 
274 bool G::Str::isULong( const std::string & s )
275 {
276  bool overflow = false ;
277  bool invalid = false ;
278  toULongImp( s , overflow , invalid ) ;
279  return !overflow && !invalid ;
280 }
281 
282 std::string G::Str::fromBool( bool b )
283 {
284  return b ? "true" : "false" ;
285 }
286 
287 std::string G::Str::fromDouble( double d )
288 {
289  std::ostringstream ss ;
290  ss << std::setprecision(16) << d ; // was "setprecision(DBL_DIG+1)"
291  return ss.str() ;
292 }
293 
294 std::string G::Str::fromInt( int i )
295 {
296  std::ostringstream ss ;
297  ss << i ;
298  return ss.str() ;
299 }
300 
301 std::string G::Str::fromLong( long l )
302 {
303  std::ostringstream ss ;
304  ss << l ;
305  return ss.str() ;
306 }
307 
308 std::string G::Str::fromShort( short s )
309 {
310  std::ostringstream ss ;
311  ss << s ;
312  return ss.str() ;
313 }
314 
315 std::string G::Str::fromUInt( unsigned int ui )
316 {
317  std::ostringstream ss ;
318  ss << ui ;
319  return ss.str() ;
320 }
321 
322 std::string G::Str::fromULong( unsigned long ul )
323 {
324  std::ostringstream ss ;
325  ss << ul ;
326  return ss.str() ;
327 }
328 
329 std::string G::Str::fromUShort( unsigned short us )
330 {
331  std::ostringstream ss ;
332  ss << us ;
333  return ss.str() ;
334 }
335 
336 bool G::Str::toBool( const std::string & s )
337 {
338  std::string str = lower( s ) ;
339  if( str == "true" )
340  {
341  return true ;
342  }
343  else if( str == "false" )
344  {
345  return false ;
346  }
347  else
348  {
349  throw InvalidFormat( "expected true/false" , s ) ;
350  return false ; // never gets here -- the return pacifies some compilers
351  }
352 }
353 
354 double G::Str::toDouble( const std::string & s )
355 {
356  char * end = nullptr ;
357  double result = ::strtod( s.c_str(), &end ) ;
358 
359  if( end == 0 || end[0] != '\0' )
360  throw InvalidFormat( "expected floating point number" , s ) ;
361 
362  if( result == HUGE_VAL || result == -(HUGE_VAL) )
363  throw Overflow( s ) ;
364 
365  return result ;
366 }
367 
368 int G::Str::toInt( const std::string & s )
369 {
370  bool overflow = false ;
371  bool invalid = false ;
372  int result = toIntImp( s , overflow , invalid ) ;
373  if( invalid )
374  throw InvalidFormat( "expected integer" , s ) ;
375  if( overflow )
376  throw Overflow( s ) ;
377  return result ;
378 }
379 
380 int G::Str::toIntImp( const std::string & s , bool & overflow , bool & invalid )
381 {
382  long long_val = toLongImp( s , overflow , invalid ) ;
383  int result = static_cast<int>( long_val ) ;
384  if( result != long_val )
385  overflow = true ;
386  return result ;
387 }
388 
389 long G::Str::toLong( const std::string & s )
390 {
391  bool overflow = false ;
392  bool invalid = false ;
393  long result = toLongImp( s , overflow , invalid ) ;
394  if( invalid )
395  throw InvalidFormat( "expected long integer" , s ) ;
396  if( overflow )
397  throw Overflow( s ) ;
398  return result ;
399 }
400 
401 long G::Str::toLongImp( const std::string & s , bool & overflow , bool & invalid )
402 {
403  char * end = nullptr ;
404  long result = ::strtol( s.c_str(), &end, 10 ) ; // was radix 0
405  if( end == 0 || end[0] != '\0' )
406  invalid = true ;
407  if( result == LONG_MAX || result == LONG_MIN )
408  overflow = true ;
409  return result ;
410 }
411 
412 short G::Str::toShort( const std::string & s )
413 {
414  bool overflow = false ;
415  bool invalid = false ;
416  short result = toShortImp( s , overflow , invalid ) ;
417  if( invalid )
418  throw InvalidFormat( "expected short integer" , s ) ;
419  if( overflow )
420  throw Overflow( s ) ;
421  return result ;
422 }
423 
424 short G::Str::toShortImp( const std::string & s , bool & overflow , bool & invalid )
425 {
426  long long_val = toLongImp( s , overflow , invalid ) ;
427  short result = static_cast<short>( long_val ) ;
428  if( result != long_val )
429  overflow = true ;
430  return result ;
431 }
432 
433 unsigned int G::Str::toUInt( const std::string & s1 , const std::string & s2 )
434 {
435  return !s1.empty() && isUInt(s1) ? toUInt(s1) : toUInt(s2) ;
436 }
437 
438 unsigned int G::Str::toUInt( const std::string & s , Limited )
439 {
440  bool overflow = false ;
441  bool invalid = false ;
442  unsigned int result = toUIntImp( s , overflow , invalid ) ;
443  if( invalid )
444  throw InvalidFormat( "expected unsigned integer" , s ) ;
445  if( overflow )
446  result = UINT_MAX ;
447  return result ;
448 }
449 
450 unsigned int G::Str::toUInt( const std::string & s )
451 {
452  bool overflow = false ;
453  bool invalid = false ;
454  unsigned int result = toUIntImp( s , overflow , invalid ) ;
455  if( invalid )
456  throw InvalidFormat( "expected unsigned integer" , s ) ;
457  if( overflow )
458  throw Overflow( s ) ;
459  return result ;
460 }
461 
462 unsigned int G::Str::toUIntImp( const std::string & s , bool & overflow , bool & invalid )
463 {
464  unsigned long ulong_val = toULongImp( s , overflow , invalid ) ;
465  unsigned int result = static_cast<unsigned int>( ulong_val ) ;
466  if( result != ulong_val )
467  overflow = true ;
468  return result ;
469 }
470 
471 unsigned long G::Str::toULong( const std::string & s , Limited )
472 {
473  bool overflow = false ;
474  bool invalid = false ;
475  unsigned long result = toULongImp( s , overflow , invalid ) ;
476  if( invalid )
477  throw InvalidFormat( "expected unsigned long integer" , s ) ;
478  if( overflow )
479  result = ULONG_MAX ;
480  return result ;
481 }
482 
483 unsigned long G::Str::toULong( const std::string & s )
484 {
485  bool overflow = false ;
486  bool invalid = false ;
487  unsigned long result = toULongImp( s , overflow , invalid ) ;
488  if( invalid )
489  throw InvalidFormat( "expected unsigned long integer" , s ) ;
490  else if( overflow )
491  throw Overflow( s ) ;
492  return result ;
493 }
494 
495 unsigned long G::Str::toULongImp( const std::string & s , bool & overflow , bool & invalid )
496 {
497  if( s.empty() ) // new
498  {
499  invalid = true ;
500  overflow = false ;
501  return 0UL ;
502  }
503  else
504  {
505  char * end = nullptr ;
506  unsigned long result = ::strtoul( s.c_str() , &end , 10 ) ;
507  if( end == 0 || end[0] != '\0' )
508  invalid = true ;
509  if( result == ULONG_MAX )
510  overflow = true ;
511  return result ;
512  }
513 }
514 
515 unsigned short G::Str::toUShort( const std::string & s , Limited )
516 {
517  bool overflow = false ;
518  bool invalid = false ;
519  unsigned short result = toUShortImp( s , overflow , invalid ) ;
520  if( invalid )
521  throw InvalidFormat( "expected unsigned short integer" , s ) ;
522  if( overflow )
523  result = USHRT_MAX ;
524  return result ;
525 }
526 
527 unsigned short G::Str::toUShort( const std::string & s )
528 {
529  bool overflow = false ;
530  bool invalid = false ;
531  unsigned short result = toUShortImp( s , overflow , invalid ) ;
532  if( invalid )
533  throw InvalidFormat( "expected unsigned short integer" , s ) ;
534  else if( overflow )
535  throw Overflow( s ) ;
536  return result ;
537 }
538 
539 unsigned short G::Str::toUShortImp( const std::string & s , bool & overflow , bool & invalid )
540 {
541  unsigned long ulong_val = toULongImp( s , overflow , invalid ) ;
542  unsigned short result = static_cast<unsigned short>( ulong_val ) ;
543  if( result != ulong_val )
544  overflow = true ;
545  return result ;
546 }
547 
548 namespace
549 {
550  struct ToLower : std::unary_function<char,char>
551  {
552  char operator()( char c ) { return static_cast<char>( tolower(c) ) ; }
553  } ;
554 }
555 
556 void G::Str::toLower( std::string & s )
557 {
558  std::transform( s.begin() , s.end() , s.begin() , ToLower() ) ;
559 }
560 
561 std::string G::Str::lower( const std::string & in )
562 {
563  std::string out = in ;
564  toLower( out ) ;
565  return out ;
566 }
567 
568 namespace
569 {
570  struct ToUpper : std::unary_function<char,char>
571  {
572  char operator()( char c ) { return static_cast<char>( toupper(c) ) ; }
573  } ;
574 }
575 
576 void G::Str::toUpper( std::string & s )
577 {
578  std::transform( s.begin() , s.end() , s.begin() , ToUpper() ) ;
579 }
580 
581 std::string G::Str::upper( const std::string & in )
582 {
583  std::string out = in ;
584  toUpper( out ) ;
585  return out ;
586 }
587 
588 namespace
589 {
590  template <typename Tchar = char , typename Tuchar = unsigned char>
591  struct PrintableAppender : std::unary_function<Tchar,void>
592  {
593  std::string & s ;
594  Tchar escape_in ;
595  char escape_out ;
596  bool eight_bit ;
597  PrintableAppender( std::string & s_ , Tchar escape_ , bool eight_bit_ ) :
598  s(s_) ,
599  escape_in(escape_) ,
600  escape_out(static_cast<char>(escape_)) ,
601  eight_bit(eight_bit_)
602  {
603  }
604  void operator()( Tchar c )
605  {
606  const Tuchar uc = static_cast<Tuchar>(c) ;
607  if( c == escape_in )
608  {
609  s.append( 2U , escape_out ) ;
610  }
611  else if( !eight_bit && uc >= 0x20U && uc < 0x7FU && uc != 0xFFU )
612  {
613  s.append( 1U , static_cast<char>(c) ) ;
614  }
615  else if( eight_bit && ( ( uc >= 0x20U && uc < 0x7FU ) || uc >= 0xA0 ) && uc != 0xFFU )
616  {
617  s.append( 1U , static_cast<char>(c) ) ;
618  }
619  else
620  {
621  s.append( 1U , escape_out ) ;
622  if( static_cast<char>(c) == '\n' )
623  {
624  s.append( 1U , 'n' ) ;
625  }
626  else if( static_cast<char>(c) == '\r' )
627  {
628  s.append( 1U , 'r' ) ;
629  }
630  else if( static_cast<char>(c) == '\t' )
631  {
632  s.append( 1U , 't' ) ;
633  }
634  else if( c == 0 )
635  {
636  s.append( 1U , '0' ) ;
637  }
638  else
639  {
640  s.append( 1U , 'x' ) ;
641  const char * const map = "0123456789abcdef" ;
642  unsigned long n = uc ;
643  if( sizeof(Tchar) == 1 )
644  {
645  n &= 0xFFUL ;
646  s.append( 1U , map[(n/16UL)%16UL] ) ;
647  s.append( 1U , map[n%16UL] ) ;
648  }
649  else
650  {
651  n &= 0xFFFFUL ;
652  s.append( 1U , map[(n/4096UL)%16UL] ) ;
653  s.append( 1U , map[(n/256UL)%16UL] ) ;
654  s.append( 1U , map[(n/16UL)%16UL] ) ;
655  s.append( 1U , map[n%16UL] ) ;
656  }
657  }
658  }
659  }
660  } ;
661 }
662 
663 std::string G::Str::printable( const std::string & in , char escape )
664 {
665  std::string result ;
666  result.reserve( in.length() + 1U ) ;
667  std::for_each( in.begin() , in.end() , PrintableAppender<char,unsigned char>(result,escape,true) ) ;
668  return result ;
669 }
670 
671 std::string G::Str::toPrintableAscii( const std::string & in , char escape )
672 {
673  std::string result ;
674  result.reserve( in.length() + 1U ) ;
675  std::for_each( in.begin() , in.end() , PrintableAppender<char,unsigned char>(result,escape,false) ) ;
676  return result ;
677 }
678 
679 std::string G::Str::toPrintableAscii( char c , char escape )
680 {
681  std::string result ;
682  PrintableAppender<char,unsigned char> append_printable( result , escape , false ) ;
683  append_printable( c ) ;
684  return result ;
685 }
686 
687 std::string G::Str::toPrintableAscii( const std::wstring & in , wchar_t escape )
688 {
689  std::string result ;
690  result.reserve( in.length() * 3U ) ;
691  std::for_each( in.begin() , in.end() , PrintableAppender<wchar_t,unsigned long>(result,escape,false) ) ;
692  return result ;
693 }
694 
695 std::string G::Str::readLineFrom( std::istream & stream , const std::string & eol )
696 {
697  std::string result ;
698  readLineFrom( stream , eol.empty() ? std::string(1U,'\n') : eol , result , true ) ;
699  return result ;
700 }
701 
702 void G::Str::readLineFrom( std::istream & stream , const std::string & eol , std::string & line , bool pre_erase )
703 {
704  G_ASSERT( eol.length() != 0U ) ;
705 
706  if( pre_erase )
707  line.erase() ;
708 
709  // this is a special speed optimisation for a two-character terminator with a one-character initial string ;-)
710  if( eol.length() == 2U && eol[0] != eol[1] && line.length() == 1U )
711  {
712  // save the initial character, use std::getline() for speed (terminating
713  // on the second character of the two-character terminator), check that the
714  // one-character terminator was actually part of the required two-character
715  // terminator, remove the first character of the two-character terminator,
716  // and finally re-insert the initial character
717  //
718  const char c = line[0] ;
719  line.erase() ; // since getline() doesnt erase it if already at eof
720  std::getline( stream , line , eol[1] ) ; // fast
721  const std::string::size_type line_length = line.length() ;
722  bool complete = line_length > 0U && line[line_length-1U] == eol[0] ;
723  if( complete )
724  {
725  line.resize( line_length - 1U ) ;
726  line.insert( 0U , &c , 1U ) ;
727  }
728  else
729  {
730  line.insert( 0U , &c , 1U ) ;
731  if( stream.good() )
732  {
733  line.append( 1U , eol[1] ) ;
734  readLineFromImp( stream , eol , line ) ;
735  }
736  }
737  }
738  else
739  {
740  readLineFromImp( stream , eol , line ) ;
741  }
742 }
743 
744 void G::Str::readLineFromImp( std::istream & stream , const std::string & eol , std::string & line )
745 {
746  const size_type limit = line.max_size() ;
747  const size_type eol_length = eol.length() ;
748  const char eol_final = eol.at( eol_length - 1U ) ;
749  size_type line_length = line.length() ;
750 
751  bool changed = false ;
752  char c ;
753  for(;;)
754  {
755  stream.get( c ) ;
756  if( stream.fail() ) // get(char) always sets the failbit at eof, not necessarily eofbit
757  {
758  // set eofbit, reset failbit -- cf. std::getline() in <string>
759  stream.clear( ( stream.rdstate() & ~std::ios_base::failbit ) | std::ios_base::eofbit ) ;
760  break ;
761  }
762 
763  if( line_length == limit ) // pathological case -- see also std::getline()
764  {
765  stream.setstate( std::ios_base::failbit ) ;
766  break ;
767  }
768 
769  line.append( 1U , c ) ; // fast enough if 'line' has sufficient capacity
770  changed = true ;
771  ++line_length ;
772 
773  if( line_length >= eol_length && c == eol_final ) // optimisation
774  {
775  const size_type offset = line_length - eol_length ;
776  if( line.find(eol,offset) == offset )
777  {
778  line.erase(offset) ;
779  break ;
780  }
781  }
782  }
783  if( !changed )
784  stream.setstate( std::ios_base::failbit ) ;
785 }
786 
787 std::string G::Str::wrap( std::string text , const std::string & prefix_1 ,
788  const std::string & prefix_2 , size_type width )
789 {
790  std::string ws( " \t\n" ) ;
791  std::ostringstream ss ;
792  for( bool first_line = true ; text.length() ; first_line = false )
793  {
794  const size_type prefix_length =
795  first_line ? prefix_1.length() : prefix_2.length() ;
796  size_type w = (width > prefix_length) ? (width-prefix_length) : width ;
797 
798  const size_type pos_nl = text.find_first_of("\n") ;
799  if( pos_nl != std::string::npos && pos_nl != 0U && pos_nl < w )
800  {
801  w = pos_nl ;
802  }
803 
804  std::string line = text ;
805  if( text.length() > w ) // (should use wcwidth() for utf-8 compatibility)
806  {
807  line = text.substr( 0U , w ) ;
808  if( text.find_first_of(ws,w) != w )
809  {
810  const size_type white_space = line.find_last_of( ws ) ;
811  const size_type black_space = line.find_first_not_of( ws ) ;
812  if( white_space != std::string::npos &&
813  black_space != std::string::npos &&
814  (white_space+1U) != black_space )
815  {
816  line = line.substr( 0U , white_space ) ;
817  }
818  }
819  }
820 
821  if( line.length() != 0U )
822  {
823  ss << ( first_line ? prefix_1 : prefix_2 ) << line << std::endl ;
824  }
825 
826  text = text.length() == line.length() ?
827  std::string() : text.substr(line.length()) ;
828 
829  const size_type black_space = text.find_first_not_of( ws ) ;
830  if( black_space != 0U && black_space != std::string::npos )
831  {
832  unsigned int newlines = 0U ;
833  for( size_type pos = 0U ; pos < black_space ; ++pos )
834  {
835  if( text.at(pos) == '\n' )
836  {
837  newlines++ ;
838  if( newlines > 1U )
839  ss << prefix_2 << std::endl ;
840  }
841  }
842 
843  text = text.substr( black_space ) ;
844  }
845  }
846  return ss.str() ;
847 }
848 
849 namespace
850 {
851  template <typename T>
852  void splitIntoTokens_( const std::string & in , T & out , const std::string & ws )
853  {
854  typedef G::Str::size_type size_type ;
855  for( size_type p = 0U ; p != std::string::npos ; )
856  {
857  p = in.find_first_not_of( ws , p ) ;
858  if( p != std::string::npos )
859  {
860  size_type end = in.find_first_of( ws , p ) ;
861  size_type len = end == std::string::npos ? end : (end-p) ;
862  out.push_back( in.substr(p,len) ) ;
863  p = end ;
864  }
865  }
866  }
867 }
868 void G::Str::splitIntoTokens( const std::string & in , StringArray & out , const std::string & ws )
869 {
870  splitIntoTokens_( in , out , ws ) ;
871 }
872 G::StringArray G::Str::splitIntoTokens( const std::string & in , const std::string & ws )
873 {
874  StringArray out ;
875  splitIntoTokens_( in , out , ws ) ;
876  return out ;
877 }
878 
879 namespace
880 {
881  template <typename T>
882  void splitIntoFields_( const std::string & in_in , T & out , const std::string & ws ,
883  char escape , bool discard_bogus )
884  {
885  typedef G::Str::size_type size_type ;
886  std::string ews( ws ) ; // escape+whitespace
887  if( escape != '\0' )
888  ews.append( 1U , escape ) ;
889 
890  if( in_in.length() )
891  {
892  std::string in = in_in ;
893  size_type start = 0U ;
894  size_type last_pos = in.length() - 1U ;
895  size_type pos = 0U ;
896  for(;;)
897  {
898  if( pos >= in.length() ) break ;
899  pos = in.find_first_of( ews , pos ) ;
900  if( pos == std::string::npos ) break ;
901  if( in.at(pos) == escape )
902  {
903  const bool valid = pos != last_pos && in.find(ews,pos+1U) == (pos+1U) ;
904  if( valid || discard_bogus )
905  in.erase( pos , 1U ) ;
906  else
907  pos++ ;
908  pos++ ;
909  }
910  else
911  {
912  out.push_back( in.substr(start,pos-start) ) ;
913  pos++ ;
914  start = pos ;
915  }
916  }
917  out.push_back( in.substr(start,pos-start) ) ;
918  }
919  }
920 }
921 void G::Str::splitIntoFields( const std::string & in , StringArray & out , const std::string & ws ,
922  char escape , bool discard_bogus )
923 {
924  splitIntoFields_( in , out , ws , escape , discard_bogus ) ;
925 }
926 
927 bool G::Str::splitMatch( const std::string & in , const std::string & s , const std::string & ws )
928 {
929  StringArray a ;
930  splitIntoTokens( in , a , ws ) ;
931  return std::find( a.begin() , a.end() , s ) != a.end() ;
932 }
933 
934 std::string G::Str::splitMatchTail( const std::string & in , const std::string & s , const std::string & ws )
935 {
936  StringArray a ;
937  splitIntoTokens( in , a , ws ) ;
938  for( StringArray::iterator p = a.begin() ; p != a.end() ; ++p )
939  {
940  if( *p == s )
941  return std::string() ;
942  if( (*p).find(s) == 0U )
943  return (*p).substr( s.size() ) ;
944  }
945  return std::string() ;
946 }
947 
948 namespace
949 {
950  template <typename T>
951  struct Joiner : std::unary_function<const T&,void>
952  {
953  T & result ;
954  const T & sep ;
955  bool & first ;
956  Joiner( T & result_ , const T & sep_ , bool & first_ ) :
957  result(result_) ,
958  sep(sep_) ,
959  first(first_)
960  {
961  first = true ;
962  }
963  void operator()( const T & s )
964  {
965  if( !first ) result.append( sep ) ;
966  result.append( s ) ;
967  first = false ;
968  }
969  } ;
970 }
971 
972 std::string G::Str::join( const std::string & sep , const StringArray & strings )
973 {
974  std::string result ;
975  bool first = true ;
976  std::for_each( strings.begin() , strings.end() , Joiner<std::string>(result,sep,first) ) ;
977  return result ;
978 }
979 
980 std::string G::Str::join( const std::string & sep , const std::set<std::string> & strings )
981 {
982  std::string result ;
983  bool first = true ;
984  std::for_each( strings.begin() , strings.end() , Joiner<std::string>(result,sep,first) ) ;
985  return result ;
986 }
987 
988 std::string G::Str::join( const std::string & sep , const std::string & s1 , const std::string & s2 ,
989  const std::string & s3 , const std::string & s4 , const std::string & s5 , const std::string & s6 ,
990  const std::string & s7 , const std::string & s8 , const std::string & s9 )
991 {
992  std::string result ;
993  joinImp( sep , result , s1 ) ;
994  joinImp( sep , result , s2 ) ;
995  joinImp( sep , result , s3 ) ;
996  joinImp( sep , result , s4 ) ;
997  joinImp( sep , result , s5 ) ;
998  joinImp( sep , result , s6 ) ;
999  joinImp( sep , result , s7 ) ;
1000  joinImp( sep , result , s8 ) ;
1001  joinImp( sep , result , s9 ) ;
1002  return result ;
1003 }
1004 
1005 void G::Str::joinImp( const std::string & sep , std::string & result , const std::string & s )
1006 {
1007  if( !result.empty() && !s.empty() )
1008  result.append( sep ) ;
1009  result.append( s ) ;
1010 }
1011 
1012 namespace
1013 {
1014  template <typename T>
1015  struct Firster : std::unary_function<const T&,const typename T::first_type&>
1016  {
1017  const typename T::first_type & operator()( const T & pair ) { return pair.first ; }
1018  } ;
1019 }
1020 std::set<std::string> G::Str::keySet( const StringMap & map )
1021 {
1022  std::set<std::string> result ;
1023  std::transform( map.begin() , map.end() , std::inserter(result,result.end()) , Firster<StringMap::value_type>() ) ;
1024  return result ;
1025 }
1026 
1027 std::string G::Str::ws()
1028 {
1029  return std::string(" \t\n\r") ;
1030 }
1031 
1032 std::string G::Str::meta()
1033 {
1034  return std::string("~<>[]*$|?\\(){}\"`'&;=") ; // bash meta-characters plus "~"
1035 }
1036 
1037 std::string G::Str::head( const std::string & in , std::string::size_type pos , const std::string & default_ )
1038 {
1039  return
1040  pos == std::string::npos ?
1041  default_ :
1042  ( pos == 0U ? std::string() : ( (pos+1U) > in.length() ? in : in.substr(0U,pos) ) ) ;
1043 }
1044 
1045 std::string G::Str::head( const std::string & in , const std::string & sep , bool default_empty )
1046 {
1047  size_t pos = sep.empty() ? std::string::npos : in.find( sep ) ;
1048  return head( in , pos , default_empty ? std::string() : in ) ;
1049 }
1050 
1051 std::string G::Str::tail( const std::string & in , std::string::size_type pos , const std::string & default_ )
1052 {
1053  return
1054  pos == std::string::npos ?
1055  default_ :
1056  ( (pos+1U) >= in.length() ? std::string() : in.substr(pos+1U) ) ;
1057 }
1058 
1059 std::string G::Str::tail( const std::string & in , const std::string & sep , bool default_empty )
1060 {
1061  size_t pos = sep.empty() ? std::string::npos : in.find(sep) ;
1062  if( pos != std::string::npos ) pos += (sep.length()-1U) ;
1063  return tail( in , pos , default_empty ? std::string() : in ) ;
1064 }
1065 
1066 bool G::Str::tailMatch( const std::string & in , const std::string & ending )
1067 {
1068  return
1069  ending.empty() ||
1070  ( in.length() >= ending.length() &&
1071  in.substr( in.length() - ending.length() ) == ending ) ;
1072  // 0 == in.compare( in.length() - ending.length() , ending.length() , ending ) // faster, but not gcc2.95
1073 }
1074 
1075 std::string G::Str::positive()
1076 {
1077  return "yes" ;
1078 }
1079 
1080 std::string G::Str::negative()
1081 {
1082  return "no" ;
1083 }
1084 
1085 bool G::Str::isPositive( const std::string & s_in )
1086 {
1087  std::string s = trimmed( lower(s_in) , ws() ) ;
1088  return !s.empty() && ( s == "y" || s == "yes" || s == "t" || s == "true" || s == "1" || s == "on" ) ;
1089 }
1090 
1091 bool G::Str::isNegative( const std::string & s_in )
1092 {
1093  std::string s = trimmed( lower(s_in) , ws() ) ;
1094  return !s.empty() && ( s == "n" || s == "no" || s == "f" || s == "false" || s == "0" || s == "off" ) ;
1095 }
1096 
1097 namespace
1098 {
1099  bool icompare( char c1 , char c2 )
1100  {
1101  if( c1 >= 'A' && c1 <= 'Z' ) c1 += '\x20' ;
1102  if( c2 >= 'A' && c2 <= 'Z' ) c2 += '\x20' ;
1103  return c1 == c2 ;
1104  }
1105 }
1106 
1107 bool G::Str::imatch( const std::string & a , const std::string & b )
1108 {
1109  return a.size() == b.size() && std::equal( a.begin() , a.end() , b.begin() , icompare ) ;
1110 }
1111 
1112 std::string::size_type G::Str::ifind( const std::string & s , const std::string & key , std::string::size_type pos )
1113 {
1114  if( s.empty() || key.empty() || pos > s.length() ) return std::string::npos ;
1115  std::string::const_iterator p = std::search( s.begin()+pos , s.end() , key.begin() , key.end() , icompare ) ;
1116  return p == s.end() ? std::string::npos : std::distance(s.begin(),p) ;
1117 }
1118 
1119 namespace
1120 {
1121  template <typename T, typename V>
1122  T unique_imp( T in , T end , V repeat , V replacement )
1123  {
1124  // replace repeated repeat-s with a single replacement
1125  T out = in ;
1126  while( in != end )
1127  {
1128  T in_next = in ; ++in_next ;
1129  if( *in == repeat && *in_next == repeat )
1130  {
1131  while( *in == repeat )
1132  ++in ;
1133  *out++ = replacement ;
1134  }
1135  else
1136  {
1137  *out++ = *in++ ;
1138  }
1139  }
1140  return out ; // new end
1141  }
1142 }
1143 
1144 std::string G::Str::unique( std::string s , char c , char r )
1145 {
1146  s.erase( unique_imp( s.begin() , s.end() , c , r ) , s.end() ) ;
1147  return s ;
1148 }
1149 
1150 /// \file gstr.cpp
static unsigned long toULong(const std::string &s, Limited)
Converts string 's' to an unsigned long.
Definition: gstr.cpp:471
static std::string positive()
Returns a default positive string. See isPositive().
Definition: gstr.cpp:1075
static std::string fromBool(bool b)
Converts boolean 'b' to a string.
Definition: gstr.cpp:282
static std::string printable(const std::string &in, char escape= '\\')
Returns a printable represention of the given input string.
Definition: gstr.cpp:663
static bool toBool(const std::string &s)
Converts string 's' to a bool.
Definition: gstr.cpp:336
static int toInt(const std::string &s)
Converts string 's' to an int.
Definition: gstr.cpp:368
static std::string wrap(std::string text, const std::string &prefix_first_line, const std::string &prefix_subsequent_lines, size_type width=70U)
Does word-wrapping.
Definition: gstr.cpp:787
static bool tailMatch(const std::string &in, const std::string &ending)
Returns true if the given string has the given ending.
Definition: gstr.cpp:1066
static bool isPositive(const std::string &)
Returns true if the string has a positive meaning, such as "1", "true", "yes".
Definition: gstr.cpp:1085
static std::string fromDouble(double d)
Converts double 'd' to a string.
Definition: gstr.cpp:287
static bool imatch(const std::string &, const std::string &)
Returns true if the two strings are the same, ignoring case.
Definition: gstr.cpp:1107
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstrings.h:33
static bool replace(std::string &s, const std::string &from, const std::string &to, size_type *pos_p=nullptr)
Replaces 'from' with 'to', starting at offset '*pos_p'.
Definition: gstr.cpp:134
std::string::size_type size_type
A std::size_t type.
Definition: md5.h:43
static void splitIntoTokens(const std::string &in, StringArray &out, const std::string &ws)
Splits the string into 'ws'-delimited tokens.
Definition: gstr.cpp:868
static short toShort(const std::string &s)
Converts string 's' to a short.
Definition: gstr.cpp:412
static bool isNumeric(const std::string &s, bool allow_minus_sign=false)
Returns true if every character is a decimal digit.
Definition: gstr.cpp:228
static void escape(std::string &s, char c_escape, const std::string &specials_in, const std::string &specials_out)
Prefixes each occurrence of one of the special-in characters with the escape character and its corres...
Definition: gstr.cpp:66
static std::string fromInt(int i)
Converts int 'i' to a string.
Definition: gstr.cpp:294
static std::string tail(const std::string &in, std::string::size_type pos, const std::string &default_=std::string())
Returns the last part of the string after the given position.
Definition: gstr.cpp:1051
static void trim(std::string &s, const std::string &ws)
Trims both ends of s, taking off any of the 'ws' characters.
Definition: gstr.cpp:208
static void trimLeft(std::string &s, const std::string &ws, size_type limit=0U)
Trims the lhs of s, taking off up to 'limit' of the 'ws' characters.
Definition: gstr.cpp:186
static std::string fromLong(long l)
Converts long 'l' to a string.
Definition: gstr.cpp:301
static std::string lower(const std::string &s)
Returns a copy of 's' in which all Latin-1 uppercase characters have been replaced by lowercase chara...
Definition: gstr.cpp:561
static unsigned int toUInt(const std::string &s)
Converts string 's' to an unsigned int.
Definition: gstr.cpp:450
static std::string escaped(const std::string &, char c_escape, const std::string &specials_in, const std::string &specials_out)
Returns the escape()d string.
Definition: gstr.cpp:41
static void toLower(std::string &s)
Replaces all Latin-1 uppercase characters in string 's' by lowercase characters.
Definition: gstr.cpp:556
static std::string fromUInt(unsigned int ui)
Converts unsigned int 'ui' to a string.
Definition: gstr.cpp:315
static unsigned int replaceAll(std::string &s, const std::string &from, const std::string &to)
Does a global replace on string 's', replacing all occurances of sub-string 'from' with 'to'...
Definition: gstr.cpp:157
static std::string head(const std::string &in, std::string::size_type pos, const std::string &default_=std::string())
Returns the first part of the string up to just before the given position.
Definition: gstr.cpp:1037
static void trimRight(std::string &s, const std::string &ws, size_type limit=0U)
Trims the rhs of s, taking off up to 'limit' of the 'ws' characters.
Definition: gstr.cpp:197
static bool isInt(const std::string &s)
Returns true if the string can be converted into an integer without throwing an exception.
Definition: gstr.cpp:250
static std::string unique(std::string s, char c= ' ', char r= ' ')
Returns a string with repeated 'c' charaters replaced by one 'r' character.
Definition: gstr.cpp:1144
static double toDouble(const std::string &s)
Converts string 's' to a double.
Definition: gstr.cpp:354
static bool splitMatch(const std::string &in, const std::string &s, const std::string &ws=Str::ws())
Returns true if any of the split parts of 'in' are equal to 's'.
Definition: gstr.cpp:927
static std::string fromShort(short s)
Converts short 's' to a string.
Definition: gstr.cpp:308
static bool isUShort(const std::string &s)
Returns true if the string can be converted into an unsigned short without throwing an exception...
Definition: gstr.cpp:258
static std::string upper(const std::string &s)
Returns a copy of 's' in which all Latin-1 lowercase characters have been replaced by uppercase chara...
Definition: gstr.cpp:581
static std::string fromUShort(unsigned short us)
Converts unsigned short 'us' to a string.
Definition: gstr.cpp:329
static std::string trimmed(const std::string &s, const std::string &ws)
Returns a trim()med version of s.
Definition: gstr.cpp:213
static std::string readLineFrom(std::istream &stream, const std::string &eol=std::string())
Reads a line from the stream using the given line terminator.
Definition: gstr.cpp:695
Overload discrimiator for G::Str::toUWhatever()
Definition: gstr.h:50
static bool isNegative(const std::string &)
Returns true if the string has a negative meaning, such as "0", "false", "no".
Definition: gstr.cpp:1091
static void unescape(std::string &s, char c_escape, const char *specials_in, const char *specials_out)
Unescapes the string by replacing e-e with e, e-special-in with special-out, and e-other with other...
Definition: gstr.cpp:104
static std::string splitMatchTail(const std::string &in, const std::string &s, const std::string &ws=Str::ws())
Splits the input string into parts and looks for a part that starts with 's' and returns the trailing...
Definition: gstr.cpp:934
static bool isPrintableAscii(const std::string &s)
Returns true if every character is a 7-bit, non-control character (ie.
Definition: gstr.cpp:244
std::map< std::string, std::string > StringMap
A std::map of std::strings.
Definition: gstrings.h:34
static std::string join(const std::string &sep, const StringArray &strings)
Concatenates an array of strings.
Definition: gstr.cpp:972
static long toLong(const std::string &s)
Converts string 's' to a long.
Definition: gstr.cpp:389
static unsigned short toUShort(const std::string &s, Limited)
Converts string 's' to an unsigned short.
Definition: gstr.cpp:515
static std::string negative()
Returns a default negative string. See isNegative().
Definition: gstr.cpp:1080
static bool isUInt(const std::string &s)
Returns true if the string can be converted into an unsigned integer without throwing an exception...
Definition: gstr.cpp:266
static std::string::size_type ifind(const std::string &s, const std::string &key, std::string::size_type pos=0U)
Does a case-insensitive std::string::find().
Definition: gstr.cpp:1112
static void removeAll(std::string &, char)
Removes all occurrences of the character from the string.
Definition: gstr.cpp:180
static std::set< std::string > keySet(const StringMap &string_map)
Extracts the keys from a map of strings.
Definition: gstr.cpp:1020
static bool isULong(const std::string &s)
Returns true if the string can be converted into an unsigned long without throwing an exception...
Definition: gstr.cpp:274
static std::string fromULong(unsigned long ul)
Converts unsigned long 'ul' to a string.
Definition: gstr.cpp:322
static std::string unescaped(const std::string &s)
Returns the unescape()d version of s.
Definition: gstr.cpp:127
static std::string meta()
Returns a list of shell meta-characters, typically used with escape().
Definition: gstr.cpp:1032
static std::string ws()
A convenience function returning standard whitespace characters.
Definition: gstr.cpp:1027
static std::string toPrintableAscii(char c, char escape= '\\')
Returns a 7-bit printable representation of the given input character.
Definition: gstr.cpp:679
static void splitIntoFields(const std::string &in, StringArray &out, const std::string &seperators, char escape= '\0', bool remove_escapes=true)
Splits the string into fields.
Definition: gstr.cpp:921
static void toUpper(std::string &s)
Replaces all Latin-1 lowercase characters in string 's' by uppercase characters.
Definition: gstr.cpp:576