40 static constexpr string_view chars_meta(
"~<>[]*$|?\\(){}\"`'&;=" ,
nullptr ) ;
42 static constexpr string_view chars_alnum(
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
43 "0123456789" "abcdefghijklmnopqrstuvwxyz" ,
nullptr ) ;
44 static_assert( chars_alnum.size() == 26U+10U+26U ,
"" ) ;
46 static constexpr string_view chars_hexmap(
"0123456789abcdef" ,
nullptr ) ;
47 static_assert( chars_hexmap.size() == 16U ,
"" ) ;
49 static constexpr string_view chars_ws(
" \t\n\r" ,
nullptr ) ;
50 static_assert( chars_ws.size() == 4U ,
"" ) ;
52 bool isDigit(
char c ) ;
53 bool isHex(
char c ) ;
54 bool isPrintableAscii(
char c ) ;
55 char toLower(
char c ) ;
56 char toUpper(
char c ) ;
57 unsigned short toUShort(
const std::string & s ,
bool & overflow ,
bool & invalid ) ;
58 unsigned long toULong(
const std::string & s ,
bool & overflow ,
bool & invalid ) ;
59 unsigned long toULongHex(
const std::string & s ,
bool limited ) ;
60 unsigned int toUInt(
const std::string & s ,
bool & overflow ,
bool & invalid ) ;
61 short toShort(
const std::string & s ,
bool & overflow ,
bool & invalid ) ;
62 long toLong(
const std::string & s ,
bool & overflow ,
bool & invalid ) ;
63 int toInt(
const std::string & s ,
bool & overflow ,
bool & invalid ) ;
64 void strncpy(
char * ,
const char * , std::size_t ) noexcept ;
65 void escape( std::string & s ,
char c_escape ,
const char * specials_in ,
const char * specials_out ,
67 void readLineFrom( std::istream & stream ,
const std::string & eol , std::string & line ) ;
68 template <
typename S,
typename T,
typename SV>
void splitIntoTokens(
const S & in , T & out ,
const SV & ws ) ;
69 template <
typename S,
typename T>
void splitIntoTokens(
const S & in , T & out ,
const S & ws ,
typename S::value_type esc ) ;
70 template <
typename T>
void splitIntoFields(
const std::string & in , T & out ,
string_view ws ) ;
71 template <
typename T>
void splitIntoFields(
const std::string & in_in , T & out ,
string_view ws ,
72 char escape ,
bool remove_escapes ) ;
73 template <
typename T1,
typename T2,
typename P>
bool equal4( T1 p1 , T1 end1 , T2 p2 , T2 end2 , P p ) ;
74 bool ilessc(
char c1 ,
char c2 ) ;
75 bool iless(
const std::string & a ,
const std::string & b ) ;
76 bool imatchc(
char c1 ,
char c2 ) ;
77 bool imatch(
const std::string & a ,
const std::string & b ) ;
78 bool match(
const std::string & a ,
const std::string & b ,
bool ignore_case ) ;
79 template <
typename T,
typename V> T unique( T in , T end , V repeat , V replacement ) ;
80 bool inList( StringArray::const_iterator begin , StringArray::const_iterator end ,
81 const std::string & s ,
bool i ) ;
82 bool notInList( StringArray::const_iterator begin , StringArray::const_iterator end ,
83 const std::string & s ,
bool i ) ;
84 template <
typename T>
struct Joiner ;
85 void join(
const std::string & , std::string & ,
const std::string & ) ;
86 template <
typename Tout> std::size_t outputHex( Tout out ,
char c ) ;
87 template <
typename Tout> std::size_t outputHex( Tout out ,
wchar_t c ) ;
88 template <
typename Tout,
typename T
char> std::size_t outputPrintable( Tout , Tchar , Tchar ,
char ,
bool ) ;
96 std::string s( s_in ) ;
101std::string
G::Str::escaped(
const std::string & s_in ,
char c_escape ,
const std::string & specials_in ,
102 const std::string & specials_out )
104 std::string s( s_in ) ;
105 escape( s , c_escape , specials_in , specials_out ) ;
109std::string
G::Str::escaped(
const std::string & s_in ,
char c_escape ,
const char * specials_in ,
110 const char * specials_out )
112 std::string s( s_in ) ;
113 escape( s , c_escape , specials_in , specials_out ) ;
117void G::StrImp::escape( std::string & s ,
char c_escape ,
const char * specials_in ,
118 const char * specials_out ,
bool with_nul )
120 G_ASSERT( specials_in !=
nullptr ) ;
121 G_ASSERT( specials_out !=
nullptr && (std::strlen(specials_out)-std::strlen(specials_in)) <= 1U ) ;
122 std::size_t pos = 0U ;
126 pos = s.find_first_of( specials_in , pos ) ;
127 if( pos != std::string::npos )
130 pos = s.find(
'\0' , pos ) ;
131 if( pos == std::string::npos )
134 G_ASSERT( std::strchr(specials_in,c_in) !=
nullptr ) ;
135 const std::size_t special_index = std::strchr(specials_in,c_in) - specials_in ;
136 G_ASSERT( special_index < std::strlen(specials_out) ) ;
138 s.insert( pos , 1U , c_escape ) ;
140 s.at(pos) = specials_out[special_index] ;
148 imp::escape( s ,
'\\' ,
"\\\r\n\t" ,
"\\rnt0" ,
true ) ;
151void G::Str::escape( std::string & s ,
char c_escape ,
const char * specials_in ,
const char * specials_out )
154 bool with_nul = std::strlen(specials_in) != std::strlen(specials_out) ;
155 imp::escape( s , c_escape , specials_in , specials_out , with_nul ) ;
158void G::Str::escape( std::string & s ,
char c_escape ,
const std::string & specials_in ,
159 const std::string & specials_out )
162 G_ASSERT( specials_in.length() == specials_out.length() ) ;
163 bool with_nul = !specials_in.empty() && specials_in.at(specials_in.length()-1U) ==
'\0' ;
164 imp::escape( s , c_escape , specials_in.c_str() , specials_out.c_str() , with_nul ) ;
170 result.reserve( s.size() ) ;
171 bool in_quote = false ;
172 bool escaped = false ;
175 if( c == esc && !escaped )
178 result.append( 1U , esc ) ;
182 std::size_t wspos = 0 ;
183 if( c == qq && !escaped && !in_quote )
187 else if( c == qq && !escaped )
191 else if( in_quote && (wspos=ws.find(c)) != std::string::npos )
195 result.append( 1U , nbws.at(wspos) ) ;
199 result.append( 1U , esc ) ;
200 result.append( 1U , c ) ;
205 result.append( 1U , c ) ;
215 unescape( s ,
'\\' ,
"rnt0" ,
"\r\n\t" ) ;
218void G::Str::unescape( std::string & s ,
char c_escape ,
const char * specials_in ,
const char * specials_out )
220 G_ASSERT( specials_in !=
nullptr ) ;
221 G_ASSERT( specials_out !=
nullptr && (std::strlen(specials_in)-std::strlen(specials_out)) <= 1U ) ;
222 bool escaped = false ;
223 auto out = s.begin() ;
224 for(
char & c_in : s )
226 const char * specials_in_p = std::strchr( specials_in , c_in ) ;
227 const char * specials_out_p = specials_in_p ? (specials_out+(specials_in_p-specials_in)) : nullptr ;
228 if( escaped && specials_out_p )
229 *out++ = *specials_out_p , escaped = false ;
230 else if( escaped && c_in == c_escape )
231 *out++ = c_escape , escaped = false ;
233 *out++ = c_in , escaped = false ;
234 else if( c_in == c_escape )
237 *out++ = c_in , escaped = false ;
239 if( out != s.end() ) s.erase( out , s.end() ) ;
244 std::string s( s_in ) ;
260 for( std::string & s : a )
261 replace( s , from , to ) ;
264bool G::Str::replace( std::string & s ,
const std::string & from ,
const std::string & to , std::size_t * pos_p )
266 if( from.length() == 0 )
269 std::size_t pos = pos_p ==
nullptr ? 0 : *pos_p ;
270 if( pos >= s.length() )
273 pos = s.find( from , pos ) ;
274 if( pos == std::string::npos )
280 s.replace( pos , from.length() , to ) ;
281 if( pos_p !=
nullptr )
282 *pos_p = pos + to.length() ;
287unsigned int G::Str::replaceAll( std::string & s ,
const std::string & from ,
const std::string & to )
289 unsigned int count = 0U ;
290 for( std::size_t pos = 0U ; replace(s,from,to,&pos) ; count++ )
297 if( s.find(from) != std::string::npos )
299 unsigned int count = 0U ;
300 std::string f( from ) ;
301 for( std::size_t pos = 0U ; replace(s,f,to,&pos) ; count++ )
313 std::string result( s ) ;
314 replaceAll( result , std::string(1U,from) , std::string(1U,to) ) ;
320 s.erase( std::remove_if( s.begin() , s.end() , [c](
char x){return x==c;} ) , s.end() ) ;
323std::string
G::Str::only(
const std::string & chars ,
const std::string & s )
326 result.reserve( s.size() ) ;
329 if( chars.find(c) != std::string::npos )
330 result.append( 1U , c ) ;
337 std::size_t n = s.find_first_not_of( ws.data() , 0U , ws.size() ) ;
338 if( limit != 0U && ( n == std::string::npos || n > limit ) )
339 n = limit >= s.length() ? std::string::npos : limit ;
340 if( n == std::string::npos )
349 std::size_t n = s.find_last_not_of( ws.data() , std::string::npos , ws.size() ) ;
350 if( limit != 0U && ( n == std::string::npos || s.length() > (limit+n+1U) ) )
351 n = limit >= s.length() ? std::string::npos : (s.length()-limit-1U) ;
352 if( n == std::string::npos )
354 else if( (n+1U) != s.length() )
361 return trimLeft( trimRight(s,ws) , ws ) ;
366 std::string s( s_in ) ;
367 return trim( s , ws ) ;
372 return std::move( trimLeft(trimRight(s,ws),ws) ) ;
375bool G::StrImp::isDigit(
char c )
377 auto uc =
static_cast<unsigned char>(c) ;
378 return uc >= 48U && uc <= 57U ;
381bool G::StrImp::isHex(
char c )
383 auto uc =
static_cast<unsigned char>(c) ;
384 return ( uc >= 48U && uc <= 57U ) || ( uc >= 65U && uc <= 70U ) || ( uc >= 97U && uc <= 102U ) ;
387bool G::StrImp::isPrintableAscii(
char c )
389 auto uc =
static_cast<unsigned char>(c) ;
390 return uc >= 32U && uc < 127U ;
393char G::StrImp::toLower(
char c )
395 const auto uc =
static_cast<unsigned char>(c) ;
396 return ( uc >= 65U && uc <= 90U ) ?
static_cast<char>(c+
'\x20') : c ;
399char G::StrImp::toUpper(
char c )
401 const auto uc =
static_cast<unsigned char>(c) ;
402 return ( uc >= 97U && uc <= 122U ) ?
static_cast<char>(c-
'\x20') : c ;
408 const auto end = s.end() ;
410 if( allow_minus_sign && p != end && *p ==
'-' ) ++p ;
411 return std::all_of( p , end , imp::isDigit ) ;
417 return std::all_of( s.begin() , s.end() , imp::isHex ) ;
423 return std::all_of( s.begin() , s.end() , imp::isPrintableAscii ) ;
429 bool overflow = false ;
430 bool invalid = false ;
431 imp::toInt( s , overflow , invalid ) ;
432 return !overflow && !invalid ;
438 bool overflow = false ;
439 bool invalid = false ;
440 imp::toUShort( s , overflow , invalid ) ;
441 return !overflow && !invalid ;
447 bool overflow = false ;
448 bool invalid = false ;
449 imp::toUInt( s , overflow , invalid ) ;
450 return !overflow && !invalid ;
456 bool overflow = false ;
457 bool invalid = false ;
458 imp::toULong( s , overflow , invalid ) ;
459 return !overflow && !invalid ;
464 return b ?
"true" :
"false" ;
469 std::ostringstream ss ;
470 ss << std::setprecision(16) << d ;
476 std::string str = lower( s ) ;
480 else if( str ==
"false" )
483 throw InvalidFormat(
"expected true/false" , s ) ;
491 std::size_t end = 0U ;
492 double result = std::stod( s , &end ) ;
493 if( end == 0U || end == s.size() )
494 throw InvalidFormat(
"expected floating point number" , s ) ;
497 catch( std::invalid_argument & )
499 throw InvalidFormat(
"expected floating point number" , s ) ;
501 catch( std::out_of_range & )
503 throw Overflow( s ) ;
510 bool overflow = false ;
511 bool invalid = false ;
512 int result = imp::toInt( s , overflow , invalid ) ;
514 throw InvalidFormat(
"expected integer" , s ) ;
516 throw Overflow( s ) ;
520int G::StrImp::toInt(
const std::string & s ,
bool & overflow ,
bool & invalid )
522 long long_val = toLong( s , overflow , invalid ) ;
523 int result =
static_cast<int>( long_val ) ;
524 if( result != long_val )
532 bool overflow = false ;
533 bool invalid = false ;
534 long result = imp::toLong( s , overflow , invalid ) ;
536 throw InvalidFormat(
"expected long integer" , s ) ;
538 throw Overflow( s ) ;
542long G::StrImp::toLong(
const std::string & s ,
bool & overflow ,
bool & invalid )
546 std::size_t end = 0U ;
547 long result = std::stol( s , &end , 10 ) ;
548 if( end == 0U || end != s.size() )
552 catch( std::invalid_argument & )
557 catch( std::out_of_range & )
567 bool overflow = false ;
568 bool invalid = false ;
569 short result = imp::toShort( s , overflow , invalid ) ;
571 throw InvalidFormat(
"expected short integer" , s ) ;
573 throw Overflow( s ) ;
577short G::StrImp::toShort(
const std::string & s ,
bool & overflow ,
bool & invalid )
579 long long_val = toLong( s , overflow , invalid ) ;
580 auto result =
static_cast<short>( long_val ) ;
581 if( result != long_val )
588 return !s1.empty() && isUInt(s1) ? toUInt(s1) : toUInt(s2) ;
594 bool overflow = false ;
595 bool invalid = false ;
596 unsigned int result = imp::toUInt( s , overflow , invalid ) ;
598 throw InvalidFormat(
"expected unsigned integer" , s ) ;
600 result = std::numeric_limits<unsigned int>::max() ;
607 bool overflow = false ;
608 bool invalid = false ;
609 unsigned int result = imp::toUInt( s , overflow , invalid ) ;
611 throw InvalidFormat(
"expected unsigned integer" , s ) ;
613 throw Overflow( s ) ;
617unsigned int G::StrImp::toUInt(
const std::string & s ,
bool & overflow ,
bool & invalid )
619 unsigned long ulong_val = toULong( s , overflow , invalid ) ;
620 auto result =
static_cast<unsigned int>( ulong_val ) ;
621 if( result != ulong_val )
629 bool overflow = false ;
630 bool invalid = false ;
631 unsigned long result = imp::toULong( s , overflow , invalid ) ;
633 throw InvalidFormat(
"expected unsigned long integer" , s ) ;
635 result = std::numeric_limits<unsigned long>::max() ;
642 return imp::toULongHex( s ,
true ) ;
648 return imp::toULongHex( s ,
false ) ;
651unsigned long G::StrImp::toULongHex(
const std::string & s ,
bool limited )
653 unsigned long n = 0U ;
654 if( s.empty() )
return 0U ;
655 std::size_t i0 = s.find_first_not_of(
'0') ;
656 if( i0 == std::string::npos ) i0 = 0U ;
657 if( (s.size()-i0) > (
sizeof(
unsigned long)*2U) )
659 if( limited )
return ~0UL ;
660 throw Str::Overflow( s ) ;
662 for( std::size_t i = i0 ; i < s.size() ; i++ )
664 unsigned int c =
static_cast<unsigned char>(s.at(i)) ;
665 if( c >= 97U && c <= 102U ) c -= 87U ;
666 else if( c >= 65U && c <= 70U ) c -= 55U ;
667 else if( c >= 48U && c <= 57U ) c -= 48U ;
668 else throw Str::InvalidFormat(
"invalid hexadecimal" , s ) ;
678 bool overflow = false ;
679 bool invalid = false ;
680 unsigned long result = imp::toULong( s , overflow , invalid ) ;
682 throw InvalidFormat(
"expected unsigned long integer" , s ) ;
684 throw Overflow( s ) ;
690 return !s1.empty() && isULong(s1) ? toULong(s1) : toULong(s2) ;
693unsigned long G::StrImp::toULong(
const std::string & s ,
bool & overflow ,
bool & invalid )
697 return Str::toUnsigned<unsigned long>( s.data() , s.data()+s.size() , overflow , invalid ) ;
703 bool overflow = false ;
704 bool invalid = false ;
705 unsigned short result = imp::toUShort( s , overflow , invalid ) ;
707 throw InvalidFormat(
"expected unsigned short integer" , s ) ;
709 result = std::numeric_limits<unsigned short>::max() ;
716 bool overflow = false ;
717 bool invalid = false ;
718 unsigned short result = imp::toUShort( s , overflow , invalid ) ;
720 throw InvalidFormat(
"expected unsigned short integer" , s ) ;
722 throw Overflow( s ) ;
726unsigned short G::StrImp::toUShort(
const std::string & s ,
bool & overflow ,
bool & invalid )
728 unsigned long ulong_val = toULong( s , overflow , invalid ) ;
729 auto result =
static_cast<unsigned short>( ulong_val ) ;
730 if( result != ulong_val )
738 std::transform( s.begin() , s.end() , s.begin() , imp::toLower ) ;
743 std::string out = in ;
751 std::transform( s.begin() , s.end() , s.begin() , imp::toUpper ) ;
756 std::string out = in ;
761template <
typename Tout>
762std::size_t G::StrImp::outputHex( Tout out ,
char c )
765 std::size_t n =
static_cast<unsigned char>( c ) ;
767 *out++ = imp::chars_hexmap[(n>>4U)%16U] ;
768 *out++ = imp::chars_hexmap[(n>>0U)%16U] ;
772template <
typename Tout>
773std::size_t G::StrImp::outputHex( Tout out ,
wchar_t c )
776 using uwchar_t =
typename std::make_unsigned<wchar_t>::type ;
777 std::size_t n =
static_cast<uwchar_t
>( c ) ;
779 *out++ = imp::chars_hexmap[(n>>12U)%16U] ;
780 *out++ = imp::chars_hexmap[(n>>8U)%16U] ;
781 *out++ = imp::chars_hexmap[(n>>4U)%16U] ;
782 *out++ = imp::chars_hexmap[(n>>0U)%16U] ;
786template <
typename Tout,
typename T
char>
787std::size_t G::StrImp::outputPrintable( Tout out , Tchar c , Tchar escape_in ,
char escape_out ,
bool eight_bit )
789 using Tuchar =
typename std::make_unsigned<Tchar>::type ;
790 const auto uc =
static_cast<Tuchar
>( c ) ;
794 *out++ = escape_out , n++ ;
795 *out++ = escape_out ;
797 else if( !eight_bit && uc >= 0x20U && uc < 0x7FU && uc != 0xFFU )
799 *out++ =
static_cast<char>(c) ;
801 else if( eight_bit && ( ( uc >= 0x20U && uc < 0x7FU ) || uc >= 0xA0 ) && uc != 0xFFU )
803 *out++ =
static_cast<char>(c) ;
807 *out++ = escape_out , n++ ;
827 n += outputHex( out , c ) ;
833template <
typename T
char>
837 const Tchar escape_in ;
838 const char escape_out ;
839 const bool eight_bit ;
840 PrintableAppender( std::string & s_ , Tchar escape_in_ ,
char escape_out_ ,
bool eight_bit_ ) :
842 escape_in(escape_in_) ,
843 escape_out(escape_out_) ,
844 eight_bit(eight_bit_)
847 void operator()( Tchar c )
849 outputPrintable( std::back_inserter(s) , c , escape_in , escape_out , eight_bit ) ;
857 std::size_t m_i{0U} ;
868 m_s.insert( m_pos , 1U , c ) ;
882 void operator++() = delete ;
889 result.reserve( in.length() + (in.length()/8U) + 1U ) ;
890 std::for_each( in.begin() , in.end() , imp::PrintableAppender<>(result,escape,escape,
true) ) ;
897 for( std::size_t pos = 0U ; pos < s.size() ; )
899 imp::InPlaceBackInserter out( s , pos ) ;
900 pos += imp::outputPrintable( out , s.at(pos) , escape , escape ,
true ) ;
902 return std::move( s ) ;
909 result.reserve( in.length() + (in.length()/8U) + 1U ) ;
910 std::for_each( in.begin() , in.end() , imp::PrintableAppender<>(result,escape,escape,
false) ) ;
918 result.reserve( in.length() + (in.length()/8U) + 1U ) ;
919 std::for_each( in.begin() , in.end() ,
920 imp::PrintableAppender<wchar_t>(result,escape,
static_cast<char>(escape),
false) ) ;
927 readLineFrom( stream , eol.empty() ? std::string(1U,
'\n') : eol , result ,
true ) ;
931void G::StrImp::readLineFrom( std::istream & stream ,
const std::string & eol , std::string & line )
933 const std::size_t limit = line.max_size() ;
934 const std::size_t eol_length = eol.length() ;
935 const char eol_final = eol.at( eol_length - 1U ) ;
936 std::size_t line_length = line.length() ;
938 bool changed = false ;
948 stream.clear( ( stream.rdstate() & ~std::ios_base::failbit ) | std::ios_base::eofbit ) ;
952 if( line_length == limit )
954 stream.setstate( std::ios_base::failbit ) ;
958 line.append( 1U , c ) ;
962 if( line_length >= eol_length && c == eol_final )
964 const std::size_t offset = line_length - eol_length ;
965 if( line.find(eol,offset) == offset )
973 stream.setstate( std::ios_base::failbit ) ;
976void G::Str::readLineFrom( std::istream & stream ,
const std::string & eol , std::string & line ,
bool pre_erase )
979 G_ASSERT( eol.length() != 0U ) ;
985 if( eol.length() == 2U && eol[0] != eol[1] && line.length() == 1U )
993 const char c = line[0] ;
995 std::getline( stream , line , eol[1] ) ;
996 const std::size_t line_length = line.length() ;
997 bool complete = line_length > 0U && line[line_length-1U] == eol[0] ;
1000 line.resize( line_length - 1U ) ;
1001 line.insert( 0U , &c , 1U ) ;
1005 line.insert( 0U , &c , 1U ) ;
1008 line.append( 1U , eol[1] ) ;
1009 imp::readLineFrom( stream , eol , line ) ;
1015 imp::readLineFrom( stream , eol , line ) ;
1019template <
typename S,
typename T,
typename SV>
1020void G::StrImp::splitIntoTokens(
const S & in , T & out ,
const SV & ws )
1022 for( std::size_t p = 0U ; p != S::npos ; )
1024 p = in.find_first_not_of( ws.data() , p , ws.size() ) ;
1027 std::size_t end = in.find_first_of( ws.data() , p , ws.size() ) ;
1028 std::size_t len = end == S::npos ? end : (end-p) ;
1029 out.push_back( in.substr(p,len) ) ;
1035template <
typename S,
typename T>
1036void G::StrImp::splitIntoTokens(
const S & in , T & out ,
const S & ws ,
typename S::value_type esc )
1038 using string_type = S ;
1039 string_type ews = ws + string_type(1U,esc) ;
1040 for( std::size_t p = 0U ; p != S::npos ; )
1043 p = in.find_first_not_of( ws.data() , p , ws.size() ) ;
1044 if( p == S::npos || ( in.at(p) == esc && (p+1) == in.size() ) )
1048 std::size_t end = in.find_first_of( ews.data() , p , ews.size() ) ;
1049 while( end != S::npos && end < in.size() && in.at(end) == esc )
1050 end = (end+2) < in.size() ? in.find_first_of( ews.data() , end+2 , ews.size() ) : S::npos ;
1053 std::size_t len = end == std::string::npos ? end : (end-p) ;
1054 string_type w( in.substr(p,len) ) ;
1057 for( std::size_t i = 0 ; esc && i < w.size() ; i++ )
1061 if( (i+1) < w.size() && ws.find(w[i+1]) != S::npos )
1068 out.push_back( w ) ;
1076 if( esc && in.find(esc) != std::string::npos )
1077 imp::splitIntoTokens( in , out , sv_to_string(ws) , esc ) ;
1079 imp::splitIntoTokens( in , out , ws ) ;
1085 splitIntoTokens( in , out , ws , esc ) ;
1089template <
typename T>
1090void G::StrImp::splitIntoFields(
const std::string & in , T & out ,
string_view ws )
1094 std::size_t start = 0U ;
1095 std::size_t pos = 0U ;
1098 if( pos >= in.length() ) break ;
1099 pos = in.find_first_of( ws.data() , pos , ws.size() ) ;
1100 if( pos == std::string::npos ) break ;
1101 out.push_back( in.substr(start,pos-start) ) ;
1105 out.push_back( in.substr(start,pos-start) ) ;
1109template <
typename T>
1110void G::StrImp::splitIntoFields(
const std::string & in_in , T & out , string_view ws ,
1111 char escape ,
bool remove_escapes )
1114 ews.reserve( ws.size() + 1U ) ;
1115 ews.assign( ws.data() , ws.size() ) ;
1116 if( escape !=
'\0' ) ews.append( 1U , escape ) ;
1117 if( in_in.length() )
1119 std::string in = in_in ;
1120 std::size_t start = 0U ;
1121 std::size_t pos = 0U ;
1124 if( pos >= in.length() ) break ;
1125 pos = in.find_first_of( ews , pos ) ;
1126 if( pos == std::string::npos ) break ;
1127 if( in.at(pos) == escape )
1129 if( remove_escapes )
1130 in.erase( pos , 1U ) ;
1137 out.push_back( in.substr(start,pos-start) ) ;
1142 out.push_back( in.substr(start,pos-start) ) ;
1147 char escape ,
bool remove_escapes )
1150 imp::splitIntoFields( in , out , ws , escape , remove_escapes ) ;
1157 imp::splitIntoFields( in , out , ws ) ;
1161template <
typename T>
1167 Joiner( T & result_ ,
const T & sep_ ,
bool & first_ ) :
1174 void operator()(
const T & s )
1176 if( !first ) result.append( sep ) ;
1177 result.append( s ) ;
1183 const std::string & pre_in ,
const std::string & post )
1186 std::string pre = pre_in.empty() ? std::string(
"=") : pre_in ;
1187 std::string result ;
1189 imp::Joiner<std::string> joiner( result , sep , first ) ;
1190 for(
const auto & map_item : map )
1191 joiner( std::string(map_item.first).append(pre).append(map_item.second).append(post) ) ;
1198 std::string result ;
1200 std::for_each( strings.begin() , strings.end() , imp::Joiner<std::string>(result,sep,first) ) ;
1204std::string
G::Str::join(
const std::string & sep ,
const std::set<std::string> & strings )
1207 std::string result ;
1209 std::for_each( strings.begin() , strings.end() , imp::Joiner<std::string>(result,sep,first) ) ;
1213std::string
G::Str::join(
const std::string & sep ,
const std::string & s1 ,
const std::string & s2 ,
1214 const std::string & s3 ,
const std::string & s4 ,
const std::string & s5 ,
const std::string & s6 ,
1215 const std::string & s7 ,
const std::string & s8 ,
const std::string & s9 )
1218 std::string result ;
1219 imp::join( sep , result , s1 ) ;
1220 imp::join( sep , result , s2 ) ;
1221 imp::join( sep , result , s3 ) ;
1222 imp::join( sep , result , s4 ) ;
1223 imp::join( sep , result , s5 ) ;
1224 imp::join( sep , result , s6 ) ;
1225 imp::join( sep , result , s7 ) ;
1226 imp::join( sep , result , s8 ) ;
1227 imp::join( sep , result , s9 ) ;
1231void G::StrImp::join(
const std::string & sep , std::string & result ,
const std::string & s )
1233 if( !result.empty() && !s.empty() )
1234 result.append( sep ) ;
1235 result.append( s ) ;
1240 std::set<std::string> result ;
1241 std::transform( map.begin() , map.end() , std::inserter(result,result.end()) ,
1242 [](
const StringMap::value_type & pair){return pair.first;} ) ;
1249 result.reserve( map.size() ) ;
1250 std::transform( map.begin() , map.end() , std::back_inserter(result) ,
1251 [](
const StringMap::value_type & pair){return pair.first;} ) ;
1258 return imp::chars_ws ;
1264 return imp::chars_alnum ;
1270 return imp::chars_meta ;
1273std::string
G::Str::head(
const std::string & in , std::size_t pos ,
const std::string & default_ )
1276 pos == std::string::npos ?
1278 ( pos == 0U ? std::string() : ( pos >= in.length() ? in : in.substr(0U,pos) ) ) ;
1281std::string
G::Str::head(
const std::string & in ,
const std::string & sep ,
bool default_empty )
1283 std::size_t pos = sep.empty() ? std::string::npos : in.find( sep ) ;
1284 return head( in , pos , default_empty ? std::string() : in ) ;
1287std::string
G::Str::tail(
const std::string & in , std::size_t pos ,
const std::string & default_ )
1290 pos == std::string::npos ?
1292 ( (pos+1U) >= in.length() ? std::string() : in.substr(pos+1U) ) ;
1295std::string
G::Str::tail(
const std::string & in ,
const std::string & sep ,
bool default_empty )
1297 std::size_t pos = sep.empty() ? std::string::npos : in.find(sep) ;
1298 if( pos != std::string::npos ) pos += (sep.length()-1U) ;
1299 return tail( in , pos , default_empty ? std::string() : in ) ;
1306 ( in.length() >= tail.length() && 0 == in.compare(in.length()-tail.length(),tail.length(),tail) ) ;
1311 return std::any_of( in.begin() , in.end() ,
1312 [&tail](
const std::string &x){return tailMatch(x,tail);} ) ;
1317 if( head ==
nullptr || *head ==
'\0' )
return true ;
1318 std::size_t head_length = std::strlen( head ) ;
1319 return ( in.length() >= head_length && 0 == in.compare(0U,head_length,head) ) ;
1326 ( in.length() >= head.length() && 0 == in.compare(0U,head.length(),head) ) ;
1331 return std::any_of( in.begin() , in.end() ,
1332 [&head](
const std::string &x){return headMatch(x,head);} ) ;
1337 const auto end = in.end() ;
1338 for(
auto p = in.begin() ; p != end ; ++p )
1340 if( headMatch( *p , head ) )
1341 return (*p).substr( head.length() ) ;
1343 return std::string() ;
1358 std::string s = trimmed( lower(s_in) , ws() ) ;
1359 return !s.empty() && ( s ==
"y" || s ==
"yes" || s ==
"t" || s ==
"true" || s ==
"1" || s ==
"on" ) ;
1364 std::string s = trimmed( lower(s_in) , ws() ) ;
1365 return !s.empty() && ( s ==
"n" || s ==
"no" || s ==
"f" || s ==
"false" || s ==
"0" || s ==
"off" ) ;
1375 return std::find( a.begin() , a.end() , b ) != a.end() ;
1378template <
typename T1,
typename T2,
typename P>
1379bool G::StrImp::equal4( T1 p1 , T1 end1 , T2 p2 , T2 end2 , P p )
1382 for( ; p1 != end1 && p2 != end2 ; ++p1 , ++p2 )
1387 return p1 == end1 && p2 == end2 ;
1390bool G::StrImp::ilessc(
char c1 ,
char c2 )
1392 if( c1 >=
'a' && c1 <=
'z' ) c1 -=
'\x20' ;
1393 if( c2 >=
'a' && c2 <=
'z' ) c2 -=
'\x20' ;
1397bool G::StrImp::iless(
const std::string & a ,
const std::string & b )
1399 return std::lexicographical_compare( a.begin() , a.end() , b.begin() , b.end() , StrImp::ilessc ) ;
1404 return StrImp::iless( a , b ) ;
1407bool G::StrImp::imatchc(
char c1 ,
char c2 )
1409 if( c1 >=
'A' && c1 <=
'Z' ) c1 +=
'\x20' ;
1410 if( c2 >=
'A' && c2 <=
'Z' ) c2 +=
'\x20' ;
1416 return StrImp::imatchc( c1 , c2 ) ;
1419bool G::StrImp::imatch(
const std::string & a ,
const std::string & b )
1421 return a.size() == b.size() && equal4( a.begin() , a.end() , b.begin() , b.end() , imatchc ) ;
1424bool G::StrImp::match(
const std::string & a ,
const std::string & b ,
bool ignore_case )
1426 return ignore_case ? imatch(a,b) : (a==b) ;
1432 return imp::imatch( a , b ) ;
1438 using namespace std::placeholders ;
1439 return std::any_of( a.begin() , a.end() , std::bind(imp::imatch,_1,std::cref(b)) ) ;
1442std::size_t
G::Str::ifind(
const std::string & s ,
const std::string & key , std::size_t pos )
1445 if( s.empty() || key.empty() || pos > s.length() )
return std::string::npos ;
1446 auto p = std::search( s.begin()+pos , s.end() , key.begin() , key.end() , imp::imatchc ) ;
1447 return p == s.end() ? std::string::npos : std::distance(s.begin(),p) ;
1450template <
typename T,
typename V>
1451T G::StrImp::unique( T in , T end , V repeat , V replacement )
1457 T in_next = in ; ++in_next ;
1458 if( *in == repeat && *in_next == repeat )
1460 while( *in == repeat )
1462 *out++ = replacement ;
1475 std::string s( s_in ) ;
1476 s.erase( imp::unique( s.begin() , s.end() , c , r ) , s.end() ) ;
1482 return s.find(c) == std::string::npos ? s : unique( s , c , c ) ;
1485bool G::StrImp::inList( StringArray::const_iterator begin , StringArray::const_iterator end ,
1486 const std::string & s ,
bool i )
1488 using namespace std::placeholders ;
1489 return std::any_of( begin , end , std::bind(match,_1,std::cref(s),i) ) ;
1492bool G::StrImp::notInList( StringArray::const_iterator begin , StringArray::const_iterator end ,
1493 const std::string & s ,
bool i )
1495 return !inList( begin , end , s , i ) ;
1498G::StringArray::iterator
G::Str::keepMatch( StringArray::iterator begin , StringArray::iterator end ,
1499 const StringArray & match_list ,
bool ignore_case )
1502 using namespace std::placeholders ;
1503 if( match_list.empty() )
return end ;
1504 return std::remove_if( begin , end ,
1505 std::bind(imp::notInList,match_list.begin(),match_list.end(),_1,ignore_case) ) ;
1509 const StringArray & match_list ,
bool ignore_case )
1512 using namespace std::placeholders ;
1513 return std::remove_if( begin , end ,
1514 std::bind(imp::inList,match_list.begin(),match_list.end(),_1,ignore_case) ) ;
1517void G::StrImp::strncpy(
char * dst ,
const char * src , std::size_t n )
noexcept
1520 for( ; n ; n-- , dst++ , src++ )
1528errno_t
G::Str::strncpy_s(
char * dst , std::size_t n_dst ,
const char * src , std::size_t count )
noexcept
1531 if( dst ==
nullptr || n_dst == 0U )
1534 if( src ==
nullptr )
1540 std::size_t n = std::strlen( src ) ;
1541 if( count != truncate && count < n )
1544 if( count == truncate && n >= n_dst )
1546 imp::strncpy( dst , src , n_dst ) ;
1547 dst[n_dst-1U] =
'\0' ;
1550 else if( n >= n_dst )
1557 imp::strncpy( dst , src , n ) ;
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.
static string_view alnum()
Returns a string of seven-bit alphanumeric characters, ie A-Z, a-z and 0-9.
static bool isNegative(const std::string &)
Returns true if the string has a negative meaning, such as "0", "false", "no".
static bool isPositive(const std::string &)
Returns true if the string has a positive meaning, such as "1", "true", "yes".
static std::string join(const std::string &sep, const StringArray &strings)
Concatenates an array of strings with separators.
static bool imatch(char, char)
Returns true if the two characters are the same, ignoring Latin-1 case.
static bool isNumeric(const std::string &s, bool allow_minus_sign=false)
Returns true if every character is a decimal digit.
static bool toBool(const std::string &s)
Converts string 's' to a bool.
static void toLower(std::string &s)
Replaces all Latin-1 upper-case characters in string 's' by lower-case characters.
static string_view ws()
Returns a string of standard whitespace characters.
static std::set< std::string > keySet(const StringMap &string_map)
Extracts the keys from a map of strings.
static StringArray keys(const StringMap &string_map)
Extracts the keys from a map of strings.
static std::string positive()
Returns a default positive string. See isPositive().
static std::string tail(const std::string &in, std::size_t pos, const std::string &default_=std::string())
Returns the last part of the string after the given position.
static void splitIntoFields(const std::string &in, StringArray &out, string_view ws, char escape='\0', bool remove_escapes=true)
Splits the string into fields.
static std::string unescaped(const std::string &s)
Returns the unescape()d version of s.
static bool isPrintableAscii(const std::string &s)
Returns true if every character is a 7-bit, non-control character (ie.
static double toDouble(const std::string &s)
Converts string 's' to a double.
static std::string replaced(const std::string &s, char from, char to)
Returns the string 's' with all occurrences of 'from' replaced by 'to'.
static std::string fromBool(bool b)
Converts boolean 'b' to a string.
static std::string & trimLeft(std::string &s, string_view ws, std::size_t limit=0U)
Trims the lhs of s, taking off up to 'limit' of the 'ws' characters.
static void splitIntoTokens(const std::string &in, StringArray &out, string_view ws, char esc='\0')
Splits the string into 'ws'-delimited tokens.
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.
static std::string toPrintableAscii(const std::string &in, char escape='\\')
Returns a 7-bit printable representation of the given input string.
static unsigned short toUShort(const std::string &s, Limited)
Converts string 's' to an unsigned short.
static std::string unique(const std::string &s, char c, char r)
Returns a string with repeated 'c' characters replaced by one 'r' character.
static bool tailMatch(const std::string &in, const std::string &ending)
Returns true if the string has the given ending (or the given ending is empty).
static bool isULong(const std::string &s)
Returns true if the string can be converted into an unsigned long without throwing an exception.
static long toLong(const std::string &s)
Converts string 's' to a long.
static void removeAll(std::string &, char)
Removes all occurrences of the character from the string. See also only().
static StringArray::iterator removeMatch(StringArray::iterator begin, StringArray::iterator end, const StringArray &match_list, bool ignore_case=false)
Removes items in the begin/end list that match one of the elements in the match-list (blocklist).
static std::string upper(const std::string &s)
Returns a copy of 's' in which all Latin-1 lower-case characters have been replaced by upper-case cha...
static std::string negative()
Returns a default negative string. See isNegative().
static bool match(const std::string &, const std::string &)
Returns true if the two strings are the same.
static string_view meta()
Returns a list of shell meta-characters with a tilde as the first character.
static std::string & trimRight(std::string &s, string_view ws, std::size_t limit=0U)
Trims the rhs of s, taking off up to 'limit' of the 'ws' characters.
static std::string dequote(const std::string &, char qq='\"' , char esc = '\\' , string_view ws = Str::ws() , string_view nbws = Str::ws() )
Dequotes a string by removing unescaped quotes and escaping quoted whitespace, so "qq-aaa-esc-qq-bbb-...
static bool isHex(const std::string &s)
Returns true if every character is a hexadecimal digit.
static std::string printable(const std::string &in, char escape='\\')
Returns a printable representation of the given input string, using chacter code ranges 0x20 to 0x7e ...
static std::string only(const std::string &allow_chars, const std::string &s)
Returns the 's' with all occurrences of the characters not appearing in the fist string deleted.
static bool headMatch(const std::string &in, const std::string &head)
Returns true if the string has the given start (or head is empty).
static std::string headMatchResidue(const StringArray &in, const std::string &head)
Returns the unmatched part of the first string in the array that has the given start.
static void toUpper(std::string &s)
Replaces all Latin-1 lower-case characters in string 's' by upper-case characters.
static unsigned int replaceAll(std::string &s, const std::string &from, const std::string &to)
Does a global replace on string 's', replacing all occurrences of sub-string 'from' with 'to'.
static int toInt(const std::string &s)
Converts string 's' to an int.
static unsigned int toUInt(const std::string &s)
Converts string 's' to an unsigned int.
static unsigned long toULong(const std::string &s, Limited)
Converts string 's' to an unsigned long.
static std::string fromDouble(double d)
Converts double 'd' to a string.
static bool isUInt(const std::string &s)
Returns true if the string can be converted into an unsigned integer without throwing an exception.
static errno_t strncpy_s(char *dst, std::size_t n_dst, const char *src, std::size_t count) noexcept
Does the same as windows strncpy_s().
static std::string head(const std::string &in, std::size_t pos, const std::string &default_=std::string())
Returns the first part of the string up to just before the given position.
static std::string trimmed(const std::string &s, string_view ws)
Returns a trim()med version of s.
static bool isInt(const std::string &s)
Returns true if the string can be converted into an integer without throwing an exception.
static StringArray::iterator keepMatch(StringArray::iterator begin, StringArray::iterator end, const StringArray &match_list, bool ignore_case=false)
Removes items in the begin/end list that do not match any of the elements in the match-list (whitelis...
static bool replace(std::string &s, const std::string &from, const std::string &to, std::size_t *pos_p=nullptr)
Replaces 'from' with 'to', starting at offset '*pos_p'.
static std::string lower(const std::string &s)
Returns a copy of 's' in which all Latin-1 upper-case characters have been replaced by lower-case cha...
static bool iless(const std::string &, const std::string &)
Returns true if the first string is lexicographically less than the first, after Latin-1 lower-case l...
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...
static std::string readLineFrom(std::istream &stream, const std::string &eol=std::string())
Reads a line from the stream using the given line terminator.
static bool isUShort(const std::string &s)
Returns true if the string can be converted into an unsigned short without throwing an exception.
static std::string & trim(std::string &s, string_view ws)
Trims both ends of s, taking off any of the 'ws' characters.
static short toShort(const std::string &s)
Converts string 's' to a short.
static std::size_t ifind(const std::string &s, const std::string &key, std::size_t pos=0U)
Returns the position of the key in 's' using a Latin-1 case-insensitive search.
A class template like c++17's std::basic_string_view.
An implementation namespace for G::Str.
std::vector< std::string > StringArray
A std::vector of std::strings.
std::map< std::string, std::string > StringMap
A std::map of std::strings.
A character appender used by G::Str.
A sub-string joiner used by G::Str.
A character appender used by G::Str.
Overload discrimiator for G::Str::toUWhatever() indicating hexadecimal strings.
Overload discrimiator for G::Str::toUWhatever() requesting a range-limited result.