E-MailRelay

gstr.cpp
Go to the documentation of this file.
1//
2// Copyright (C) 2001-2024 Graeme Walker <graeme_walker@users.sourceforge.net>
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/// \file gstr.cpp
19///
20
21#include "gdef.h"
22#include "gstr.h"
23#include "gassert.h"
24#include <algorithm>
25#include <type_traits> // std::make_unsigned
26#include <stdexcept>
27#include <iterator>
28#include <limits>
29#include <functional>
30#include <iomanip>
31#include <string>
32#include <sstream>
33#include <cstring>
34#include <cerrno>
35#include <cmath>
36
37 namespace G
38{
39 namespace StrImp
40 {
41 std::string_view hexmap() noexcept ;
42 bool isDigit( char c ) noexcept ;
43 bool isHex( char c ) noexcept ;
44 bool isPrintableAscii( char c ) noexcept ;
45 bool isPrintable( char c ) noexcept ;
46 bool isSimple( char c ) noexcept ;
47 char toLower( char c ) noexcept ;
48 char toUpper( char c ) noexcept ;
49 unsigned short toUShort( std::string_view , bool & overflow , bool & invalid ) noexcept ;
50 unsigned long toULong( std::string_view , bool & overflow , bool & invalid ) noexcept ;
51 unsigned long toULongHex( std::string_view , bool limited ) ;
52 unsigned int toUInt( std::string_view , bool & overflow , bool & invalid ) noexcept ;
53 short toShort( std::string_view , bool & overflow , bool & invalid ) noexcept ;
54 long toLong( std::string_view , bool & overflow , bool & invalid ) noexcept ;
55 int toInt( std::string_view , bool & overflow , bool & invalid ) noexcept ;
56 template <typename U> std::string_view fromUnsignedToHex( U u , char * out_p ) noexcept ;
57 void strncpy( char * , const char * , std::size_t ) noexcept ;
58 template <typename Tstr, typename Fn> bool readLine( std::istream & , Tstr & , char * , std::size_t , Fn ) ;
59 template <typename S, typename T, typename SV> void splitIntoTokens( const S & in , T & out , const SV & ws ) ;
60 template <typename S, typename T> void splitIntoTokens( const S & in , T & out , const S & ws , typename S::value_type esc ) ;
61 template <typename T> void splitIntoFields( std::string_view in , T & out , std::string_view ws ) ;
62 template <typename T> void splitIntoFields( std::string_view in_in , T & out , std::string_view ws , char escape , bool remove_escapes ) ;
63 bool ilessc( char c1 , char c2 ) noexcept ;
64 bool imatchc( char c1 , char c2 ) noexcept ;
65 bool imatch( const std::string & a , const std::string & b ) ;
66 template <typename T, typename V> T unique( T in , T end , V repeat , V replacement ) ;
67 bool inList( StringArray::const_iterator begin , StringArray::const_iterator end , const std::string & s , bool i ) ;
68 bool notInList( StringArray::const_iterator begin , StringArray::const_iterator end , const std::string & s , bool i ) ;
69 void join( std::string_view , std::string & , std::string_view ) ;
70 template <typename Tout> std::size_t outputHex( Tout out , char c ) ;
71 template <typename Tout> std::size_t outputHex( Tout out , wchar_t c ) ;
72 template <typename Tout, typename Tchar> std::size_t outputPrintable( Tout , Tchar , Tchar , char , bool ) ;
73 bool allOf( std::string_view s , bool (*fn)(char) ) noexcept ;
74 }
75}
76
77 bool G::StrImp::allOf( std::string_view s , bool (*fn)(char) ) noexcept
78{
79 return std::all_of( s.begin() , s.end() , fn ) ; // (true if empty)
80}
81
82#ifndef G_LIB_SMALL
83std::string G::Str::escaped( std::string_view s_in )
84{
85 if( s_in.empty() ) return {} ;
86 std::string s( s_in.data() , s_in.size() ) ;
87 escape( s ) ;
88 return s ;
89}
90#endif
91
92std::string G::Str::escaped( std::string_view s_in , char c_escape , std::string_view specials_in , std::string_view specials_out )
93{
94 if( s_in.empty() ) return {} ;
95 std::string s( s_in.data() , s_in.size() ) ;
96 escape( s , c_escape , specials_in , specials_out ) ;
97 return s ;
98}
99
100 void G::Str::escape( std::string & s , char c_escape , std::string_view specials_in , std::string_view specials_out )
101{
102 G_ASSERT( specials_in.size() == specials_out.size() ) ;
103 std::size_t pos = 0U ;
104 for(;;)
105 {
106 char c_in = '\0' ;
107 pos = s.find_first_of( specials_in.data() , pos , specials_in.size() ) ;
108 if( pos == std::string::npos )
109 break ;
110 else
111 c_in = s.at( pos ) ;
112
113 const std::size_t special_index = specials_in.find( c_in ) ;
114 char c_out = specials_out.at( special_index ) ;
115
116 s.insert( pos , 1U , c_escape ) ;
117 pos++ ;
118 s.at(pos) = c_out ;
119 pos++ ;
120 }
121}
122
123 void G::Str::escape( std::string & s )
124{
125 Str::escape( s , '\\' , "\0\\\r\n\t"_sv , "0\\rnt"_sv ) ;
126}
127
128std::string G::Str::dequote( const std::string & s , char qq , char esc , std::string_view ws , std::string_view nbws )
129{
130 G_ASSERT( ws.size() == nbws.size() ) ;
131 std::string result ;
132 result.reserve( s.size() ) ;
133 bool in_quote = false ;
134 bool escaped = false ;
135 for( char c : s )
136 {
137 if( c == esc && !escaped )
138 {
139 escaped = true ;
140 result.append( 1U , esc ) ;
141 }
142 else
143 {
144 std::size_t wspos = 0 ;
145 if( c == qq && !escaped && !in_quote )
146 {
147 in_quote = true ;
148 }
149 else if( c == qq && !escaped )
150 {
151 in_quote = false ;
152 }
153 else if( in_quote && (wspos=ws.find(c)) != std::string::npos ) // NOLINT assignment
154 {
155 if( escaped )
156 {
157 result.append( 1U , nbws.at(wspos) ) ;
158 }
159 else
160 {
161 result.append( 1U , esc ) ;
162 result.append( 1U , c ) ;
163 }
164 }
165 else
166 {
167 result.append( 1U , c ) ;
168 }
169 escaped = false ;
170 }
171 }
172 return result ;
173}
174
175 void G::Str::unescape( std::string & s )
176{
177 unescape( s , '\\' , "0rnt"_sv , "\0\r\n\t"_sv ) ;
178}
179
180 void G::Str::unescape( std::string & s , char c_escape , std::string_view specials_in , std::string_view specials_out )
181{
182 G_ASSERT( specials_in.size() == specials_out.size() ) ;
183 bool escaped = false ;
184 std::size_t cpos = 0U ;
185 auto out = s.begin() ; // output in-place
186 for( char & c_in : s )
187 {
188 if( escaped && (cpos=specials_in.find(c_in)) != std::string::npos ) // was BS now 'n' // NOLINT assigmnent
189 *out++ = specials_out.at(cpos) , escaped = false ; // emit NL
190 else if( escaped && c_in == c_escape ) // was BS now BS
191 *out++ = c_escape , escaped = false ; // emit BS
192 else if( escaped ) // was BS
193 *out++ = c_in , escaped = false ; // emit c
194 else if( c_in == c_escape ) // is BS
195 escaped = true ;
196 else
197 *out++ = c_in , escaped = false ;
198 }
199 if( out != s.end() ) s.erase( out , s.end() ) ;
200}
201
202#ifndef G_LIB_SMALL
203std::string G::Str::unescaped( const std::string & s_in )
204{
205 std::string s( s_in ) ;
206 unescape( s ) ;
207 return s ;
208}
209#endif
210
211 void G::Str::replace( std::string & s , char from , char to )
212{
213 for( char & c : s )
214 {
215 if( c == from )
216 c = to ;
217 }
218}
219
220 void G::Str::replace( StringArray & a , char from , char to )
221{
222 for( std::string & s : a )
223 replace( s , from , to ) ;
224}
225
226 bool G::Str::replace( std::string & s , std::string_view from , std::string_view to , std::size_t * pos_p )
227{
228 if( from.empty() )
229 return false ;
230
231 std::size_t pos = pos_p == nullptr ? 0 : *pos_p ;
232 if( pos >= s.size() )
233 return false ;
234
235 pos = s.find( from.data() , pos , from.size() ) ;
236 if( pos == std::string::npos )
237 return false ;
238 else if( to.empty() )
239 s.erase( pos , from.size() ) ;
240 else
241 s.replace( pos , from.size() , to.data() , to.size() ) ;
242 if( pos_p != nullptr )
243 *pos_p = pos + to.size() ;
244 return true ;
245}
246
247 unsigned int G::Str::replaceAll( std::string & s , std::string_view from , std::string_view to )
248{
249 unsigned int count = 0U ;
250 for( std::size_t pos = 0U ; replace(s,from,to,&pos) ; count++ )
251 {;} // no-op
252 return count ;
253}
254
255std::string G::Str::replaced( const std::string & s , char from , char to )
256{
257 std::string result( s ) ;
258 replaceAll( result , {&from,1U} , {&to,1U} ) ;
259 return result ;
260}
261
262 void G::Str::removeAll( std::string & s , char c )
263{
264 s.erase( std::remove_if( s.begin() , s.end() , [c](char x){return x==c;} ) , s.end() ) ;
265}
266
267#ifndef G_LIB_SMALL
268std::string G::Str::removedAll( const std::string & s_in , char c )
269{
270 std::string s( s_in ) ;
271 removeAll( s , c ) ;
272 return s ;
273}
274#endif
275
276std::string G::Str::only( std::string_view chars , std::string_view s )
277{
278 std::string result ;
279 result.reserve( s.size() ) ;
280 for( char c : s )
281 {
282 if( chars.find(c) != std::string::npos )
283 result.append( 1U , c ) ;
284 }
285 return result ;
286}
287
288std::string & G::Str::trimLeft( std::string & s , std::string_view ws , std::size_t limit )
289{
290 std::size_t n = s.find_first_not_of( ws.data() , 0U , ws.size() ) ;
291 if( limit != 0U && ( n == std::string::npos || n > limit ) )
292 n = limit >= s.size() ? std::string::npos : limit ;
293 if( n == std::string::npos )
294 s.erase() ;
295 else if( n != 0U )
296 s.erase( 0U , n ) ;
297 return s ;
298}
299
300std::string_view G::Str::trimLeftView( std::string_view sv , std::string_view ws , std::size_t limit ) noexcept
301{
302 std::size_t n = sv.find_first_not_of( ws ) ;
303 if( limit != 0U && ( n == std::string::npos || n > limit ) )
304 n = limit >= sv.size() ? std::string::npos : limit ;
305 if( n == std::string::npos )
306 return sv_substr_noexcept( sv , 0U , 0U ) ;
307 else if( n != 0U )
308 return sv_substr_noexcept( sv , n ) ;
309 else
310 return sv ;
311}
312
313std::string & G::Str::trimRight( std::string & s , std::string_view ws , std::size_t limit )
314{
315 std::size_t n = s.find_last_not_of( ws.data() , std::string::npos , ws.size() ) ;
316 if( limit != 0U && ( n == std::string::npos || s.size() > (limit+n+1U) ) )
317 n = limit >= s.size() ? std::string::npos : (s.size()-limit-1U) ;
318 if( n == std::string::npos )
319 s.erase() ;
320 else if( (n+1U) != s.size() )
321 s.resize( n + 1U ) ;
322 return s ;
323}
324
325std::string_view G::Str::trimRightView( std::string_view sv , std::string_view ws , std::size_t limit ) noexcept
326{
327 std::size_t n = sv.find_last_not_of( ws ) ;
328 if( limit != 0U && ( n == std::string::npos || sv.size() > (limit+n+1U) ) )
329 n = limit >= sv.size() ? std::string::npos : (sv.size()-limit-1U) ;
330 if( n == std::string::npos )
331 return sv_substr_noexcept( sv , 0U , 0U ) ;
332 else if( (n+1U) != sv.size() )
333 return sv_substr_noexcept( sv , 0U , n+1U ) ;
334 else
335 return sv ;
336}
337
338std::string & G::Str::trim( std::string & s , std::string_view ws )
339{
340 return trimLeft( trimRight(s,ws) , ws ) ;
341}
342
343std::string G::Str::trimmed( const std::string & s_in , std::string_view ws )
344{
345 std::string s( s_in ) ;
346 trimRight( s , ws ) ;
347 trimLeft( s , ws ) ;
348 return s ;
349}
350
351std::string_view G::Str::trimmedView( std::string_view s , std::string_view ws ) noexcept
352{
353 return trimLeftView( trimRightView(s,ws) , ws ) ;
354}
355
356 bool G::StrImp::isDigit( char c ) noexcept
357{
358 unsigned int uc = static_cast<unsigned char>(c) ;
359 return uc >= 48U && uc <= 57U ;
360}
361
362 bool G::StrImp::isHex( char c ) noexcept
363{
364 unsigned int uc = static_cast<unsigned char>(c) ;
365 return ( uc >= 48U && uc <= 57U ) || ( uc >= 65U && uc <= 70U ) || ( uc >= 97U && uc <= 102U ) ;
366}
367
368 bool G::StrImp::isPrintableAscii( char c ) noexcept
369{
370 unsigned int uc = static_cast<unsigned char>(c) ;
371 return uc >= 32U && uc < 127U ;
372}
373
374 bool G::StrImp::isPrintable( char c ) noexcept
375{
376 unsigned int uc = static_cast<unsigned char>(c) ;
377 return uc >= 32U && uc != 127U ;
378}
379
380 bool G::StrImp::isSimple( char c ) noexcept
381{
382 unsigned int uc = static_cast<unsigned char>(c) ;
383 return isDigit(c) || c == '-' || c == '_' ||
384 ( uc >= 65U && uc <= 90U ) ||
385 ( uc >= 97U && uc <= 122U ) ;
386}
387
388 char G::StrImp::toLower( char c ) noexcept
389{
390 const auto uc = static_cast<unsigned char>(c) ;
391 return ( uc >= 65U && uc <= 90U ) ? static_cast<char>(c+'\x20') : c ;
392}
393
394 char G::StrImp::toUpper( char c ) noexcept
395{
396 const auto uc = static_cast<unsigned char>(c) ;
397 return ( uc >= 97U && uc <= 122U ) ? static_cast<char>(c-'\x20') : c ;
398}
399
400 bool G::Str::isNumeric( std::string_view s , bool allow_minus_sign ) noexcept
401{
402 bool bump = allow_minus_sign && s.size() > 1U && s[0] == '-' ;
403 return StrImp::allOf( bump?sv_substr_noexcept(s,1U):s , StrImp::isDigit ) ; // (true if empty)
404}
405
406#ifndef G_LIB_SMALL
407 bool G::Str::isHex( std::string_view s ) noexcept
408{
409 return StrImp::allOf( s , StrImp::isHex ) ;
410}
411#endif
412
413 bool G::Str::isPrintableAscii( std::string_view s ) noexcept
414{
415 return StrImp::allOf( s , StrImp::isPrintableAscii ) ;
416}
417
418 bool G::Str::isPrintable( std::string_view s ) noexcept
419{
420 return StrImp::allOf( s , StrImp::isPrintable ) ;
421}
422
423 bool G::Str::isSimple( std::string_view s ) noexcept
424{
425 return StrImp::allOf( s , StrImp::isSimple ) ;
426}
427
428 bool G::Str::isInt( std::string_view s ) noexcept
429{
430 bool overflow = false ;
431 bool invalid = false ;
432 StrImp::toInt( s , overflow , invalid ) ;
433 return !overflow && !invalid ;
434}
435
436#ifndef G_LIB_SMALL
437 bool G::Str::isUShort( std::string_view s ) noexcept
438{
439 bool overflow = false ;
440 bool invalid = false ;
441 StrImp::toUShort( s , overflow , invalid ) ;
442 return !overflow && !invalid ;
443}
444#endif
445
446 bool G::Str::isUInt( std::string_view s ) noexcept
447{
448 bool overflow = false ;
449 bool invalid = false ;
450 StrImp::toUInt( s , overflow , invalid ) ;
451 return !overflow && !invalid ;
452}
453
454 bool G::Str::isULong( std::string_view s ) noexcept
455{
456 bool overflow = false ;
457 bool invalid = false ;
458 StrImp::toULong( s , overflow , invalid ) ;
459 return !overflow && !invalid ;
460}
461
462#ifndef G_LIB_SMALL
463std::string G::Str::fromBool( bool b )
464{
465 return b ? "true" : "false" ;
466}
467#endif
468
469#ifndef G_LIB_SMALL
470std::string G::Str::fromDouble( double d )
471{
472 if( std::isnan(d) ) return "nan" ;
473 if( std::isinf(d) ) return "inf" ;
474 std::ostringstream ss ;
475 ss << std::setprecision(16) << d ;
476 return ss.str() ;
477}
478#endif
479
480#ifndef G_LIB_SMALL
481 bool G::Str::toBool( std::string_view s )
482{
483 bool result = true ;
484 if( imatch( s , "true"_sv ) )
485 {;}
486 else if( imatch( s , "false"_sv ) )
487 result = false ;
488 else
489 throw InvalidFormat( "expected true/false" , s ) ;
490 return result ;
491}
492#endif
493
494#ifndef G_LIB_SMALL
495 double G::Str::toDouble( const std::string & s )
496{
497 try
498 {
499 std::size_t end = 0U ;
500 double result = std::stod( s , &end ) ;
501 if( end != s.size() )
502 throw InvalidFormat( "expected floating point number" , s ) ;
503 return result ;
504 }
505 catch( std::invalid_argument & )
506 {
507 throw InvalidFormat( "expected floating point number" , s ) ;
508 }
509 catch( std::out_of_range & )
510 {
511 throw Overflow( s ) ;
512 }
513}
514#endif
515
516#ifndef G_LIB_SMALL
517 float G::Str::toFloat( const std::string & s )
518{
519 try
520 {
521 std::size_t end = 0U ;
522 float result = std::stof( s , &end ) ;
523 if( end != s.size() )
524 throw InvalidFormat( "expected floating point number" , s ) ;
525 return result ;
526 }
527 catch( std::invalid_argument & )
528 {
529 throw InvalidFormat( "expected floating point number" , s ) ;
530 }
531 catch( std::out_of_range & )
532 {
533 throw Overflow( s ) ;
534 }
535}
536#endif
537
538 int G::Str::toInt( std::string_view s )
539{
540 bool overflow = false ;
541 bool invalid = false ;
542 int result = StrImp::toInt( s , overflow , invalid ) ;
543 if( invalid )
544 throw InvalidFormat( "expected integer" , s ) ;
545 if( overflow )
546 throw Overflow( s ) ;
547 return result ;
548}
549
550#ifndef G_LIB_SMALL
551 int G::Str::toInt( std::string_view s1 , std::string_view s2 )
552{
553 return !s1.empty() && isInt(s1) ? toInt(s1) : toInt(s2) ;
554}
555#endif
556
557 int G::StrImp::toInt( std::string_view s , bool & overflow , bool & invalid ) noexcept
558{
559 long long_val = toLong( s , overflow , invalid ) ;
560 int result = static_cast<int>( long_val ) ;
561 if( result != long_val )
562 overflow = true ;
563 return result ;
564}
565
566#ifndef G_LIB_SMALL
567 long G::Str::toLong( std::string_view s )
568{
569 bool overflow = false ;
570 bool invalid = false ;
571 long result = StrImp::toLong( s , overflow , invalid ) ;
572 if( invalid )
573 throw InvalidFormat( "expected long integer" , s ) ;
574 if( overflow )
575 throw Overflow( s ) ;
576 return result ;
577}
578#endif
579
580 long G::StrImp::toLong( std::string_view s , bool & overflow , bool & invalid ) noexcept
581{
582 bool negative = !s.empty() && s[0] == '-' ;
583 bool positive = !s.empty() && s[0] == '+' ;
584 if( (negative || positive) && s.size() == 1U )
585 {
586 invalid = true ;
587 return 0L ;
588 }
589 unsigned long ul = toULong( sv_substr_noexcept(s,(negative||positive)?1U:0U) , overflow , invalid ) ;
590 static constexpr long long_max = std::numeric_limits<long>::max() ;
591 if( ul > long_max || (negative && (ul==long_max)) )
592 {
593 overflow = true ;
594 return 0L ;
595 }
596 return negative ? -static_cast<long>(ul) : static_cast<long>(ul) ;
597}
598
599#ifndef G_LIB_SMALL
600 short G::Str::toShort( std::string_view s )
601{
602 bool overflow = false ;
603 bool invalid = false ;
604 short result = StrImp::toShort( s , overflow , invalid ) ;
605 if( invalid )
606 throw InvalidFormat( "expected short integer" , s ) ;
607 if( overflow )
608 throw Overflow( s ) ;
609 return result ;
610}
611#endif
612
613 short G::StrImp::toShort( std::string_view s , bool & overflow , bool & invalid ) noexcept
614{
615 long long_val = toLong( s , overflow , invalid ) ;
616 auto result = static_cast<short>( long_val ) ;
617 if( result != long_val )
618 overflow = true ;
619 return result ;
620}
621
622 unsigned int G::Str::toUInt( std::string_view s1 , std::string_view s2 )
623{
624 return !s1.empty() && isUInt(s1) ? toUInt(s1) : toUInt(s2) ;
625}
626
627 unsigned int G::Str::toUInt( std::string_view s , unsigned int default_ ) noexcept
628{
629 bool overflow = false ;
630 bool invalid = false ;
631 return !s.empty() && isUInt(s) ? StrImp::toUInt(s,overflow,invalid) : default_ ;
632}
633
634#ifndef G_LIB_SMALL
635 unsigned int G::Str::toUInt( std::string_view s , Limited )
636{
637 bool overflow = false ;
638 bool invalid = false ;
639 unsigned int result = StrImp::toUInt( s , overflow , invalid ) ;
640 if( invalid )
641 throw InvalidFormat( "expected unsigned integer" , s ) ;
642 if( overflow )
643 result = std::numeric_limits<unsigned int>::max() ;
644 return result ;
645}
646#endif
647
648 unsigned int G::Str::toUInt( std::string_view s )
649{
650 bool overflow = false ;
651 bool invalid = false ;
652 unsigned int result = StrImp::toUInt( s , overflow , invalid ) ;
653 if( invalid )
654 throw InvalidFormat( "expected unsigned integer" , s ) ;
655 if( overflow )
656 throw Overflow( s ) ;
657 return result ;
658}
659
660 unsigned int G::StrImp::toUInt( std::string_view s , bool & overflow , bool & invalid ) noexcept
661{
662 unsigned long ulong_val = toULong( s , overflow , invalid ) ;
663 auto result = static_cast<unsigned int>( ulong_val ) ;
664 if( result != ulong_val )
665 overflow = true ;
666 return result ;
667}
668
669 unsigned long G::Str::toULong( std::string_view s , Limited )
670{
671 bool overflow = false ;
672 bool invalid = false ;
673 unsigned long result = StrImp::toULong( s , overflow , invalid ) ;
674 if( invalid )
675 throw InvalidFormat( "expected unsigned long integer" , s ) ;
676 if( overflow )
677 result = std::numeric_limits<unsigned long>::max() ;
678 return result ;
679}
680
681#ifndef G_LIB_SMALL
682 unsigned long G::Str::toULong( std::string_view s , Hex , Limited )
683{
684 return StrImp::toULongHex( s , true ) ;
685}
686#endif
687
688#ifndef G_LIB_SMALL
689 unsigned long G::Str::toULong( std::string_view s , Hex )
690{
691 return StrImp::toULongHex( s , false ) ;
692}
693#endif
694
695 unsigned long G::StrImp::toULongHex( std::string_view s , bool limited )
696{
697 unsigned long n = 0U ;
698 if( s.empty() ) return 0U ;
699 std::size_t i0 = s.find_first_not_of( '0' ) ;
700 if( i0 == std::string::npos ) i0 = 0U ;
701 if( (s.size()-i0) > (sizeof(unsigned long)*2U) )
702 {
703 if( limited ) return ~0UL ;
704 throw Str::Overflow( s ) ;
705 }
706 for( std::size_t i = i0 ; i < s.size() ; i++ )
707 {
708 unsigned int c = static_cast<unsigned char>(s.at(i)) ;
709 if( c >= 97U && c <= 102U ) c -= 87U ;
710 else if( c >= 65U && c <= 70U ) c -= 55U ;
711 else if( c >= 48U && c <= 57U ) c -= 48U ;
712 else throw Str::InvalidFormat( "invalid hexadecimal" , s ) ;
713 n <<= 4U ;
714 n += c ;
715 }
716 return n ;
717}
718
719 unsigned long G::Str::toULong( std::string_view s )
720{
721 bool overflow = false ;
722 bool invalid = false ;
723 unsigned long result = StrImp::toULong( s , overflow , invalid ) ;
724 if( invalid )
725 throw InvalidFormat( "expected unsigned long integer" , s ) ;
726 else if( overflow )
727 throw Overflow( s ) ;
728 return result ;
729}
730
731#ifndef G_LIB_SMALL
732 unsigned long G::Str::toULong( std::string_view s1 , std::string_view s2 )
733{
734 return !s1.empty() && isULong(s1) ? toULong(s1) : toULong(s2) ;
735}
736#endif
737
738 unsigned long G::StrImp::toULong( std::string_view s , bool & overflow , bool & invalid ) noexcept
739{
740 // note that stoul()/strtoul() do too much in that they skip leading
741 // spaces, allow initial +/- even for unsigned, and are C-locale
742 // dependent -- also there is no std::string_view version of std::stol()
743 // and std::strtol() requires a null-terminated string and so cannot
744 // take std::string_view::data()
745
746 return Str::toUnsigned<unsigned long>( s.data() , s.data()+s.size() , overflow , invalid ) ;
747}
748
749#ifndef G_LIB_SMALL
750 unsigned short G::Str::toUShort( std::string_view s , Limited )
751{
752 bool overflow = false ;
753 bool invalid = false ;
754 unsigned short result = StrImp::toUShort( s , overflow , invalid ) ;
755 if( invalid )
756 throw InvalidFormat( "expected unsigned short integer" , s ) ;
757 if( overflow )
758 result = std::numeric_limits<unsigned short>::max() ;
759 return result ;
760}
761#endif
762
763#ifndef G_LIB_SMALL
764 unsigned short G::Str::toUShort( std::string_view s )
765{
766 bool overflow = false ;
767 bool invalid = false ;
768 unsigned short result = StrImp::toUShort( s , overflow , invalid ) ;
769 if( invalid )
770 throw InvalidFormat( "expected unsigned short integer" , s ) ;
771 else if( overflow )
772 throw Overflow( s ) ;
773 return result ;
774}
775#endif
776
777 unsigned short G::StrImp::toUShort( std::string_view s , bool & overflow , bool & invalid ) noexcept
778{
779 unsigned long ulong_val = toULong( s , overflow , invalid ) ;
780 auto result = static_cast<unsigned short>( ulong_val ) ;
781 if( result != ulong_val )
782 overflow = true ;
783 return result ;
784}
785
786#ifndef G_LIB_SMALL
787std::string_view G::Str::fromULongToHex( unsigned long u , char * out_p ) noexcept
788{
789 return StrImp::fromUnsignedToHex( u , out_p ) ;
790}
791#endif
792
793#ifndef G_LIB_SMALL
794std::string_view G::Str::fromULongLongToHex( unsigned long long u , char * out_p ) noexcept
795{
796 return StrImp::fromUnsignedToHex( u , out_p ) ;
797}
798#endif
799
800 template <typename U>
801std::string_view G::StrImp::fromUnsignedToHex( U u , char * out_p ) noexcept
802{
803 // (std::to_chars() is c++17 so do it long-hand)
804 static constexpr unsigned bytes = sizeof(U) ;
805 static constexpr unsigned bits = 8U * bytes ;
806 static constexpr unsigned buffer_size = bytes * 2U ;
807 static const char * map = "0123456789abcdef" ;
808 unsigned int shift = bits - 4U ;
809 char * out = out_p ;
810 for( unsigned i = 0 ; i < (bytes*2U) ; i++ ) // unrollable, no break
811 {
812 *out++ = map[(u>>shift) & 0xfUL] ;
813 shift -= 4U ;
814 }
815 std::string_view sv( out_p , buffer_size ) ;
816 return sv_substr_noexcept( sv , std::min( sv.find_first_not_of('0') , static_cast<std::size_t>(buffer_size-1U) ) ) ;
817}
818
819 void G::Str::toLower( std::string & s )
820{
821 std::transform( s.begin() , s.end() , s.begin() , StrImp::toLower ) ;
822}
823
824std::string G::Str::lower( std::string_view in )
825{
826 std::string out = sv_to_string( in ) ;
827 toLower( out ) ;
828 return out ;
829}
830
831 void G::Str::toUpper( std::string & s )
832{
833 std::transform( s.begin() , s.end() , s.begin() , StrImp::toUpper ) ;
834}
835
836std::string G::Str::upper( std::string_view in )
837{
838 std::string out = sv_to_string( in ) ;
839 toUpper( out ) ;
840 return out ;
841}
842
843std::string_view G::StrImp::hexmap() noexcept
844{
845 static constexpr std::string_view chars_hexmap = "0123456789abcdef"_sv ;
846 static_assert( chars_hexmap.size() == 16U , "" ) ;
847 return chars_hexmap ;
848}
849
850 template <typename Tout>
851std::size_t G::StrImp::outputHex( Tout out , char c )
852{
853 std::size_t n = static_cast<unsigned char>( c ) ;
854 n &= 0xffU ;
855 out( hexmap()[(n>>4U)%16U] ) ;
856 out( hexmap()[(n>>0U)%16U] ) ;
857 return 2U ;
858}
859
860 template <typename Tout>
861std::size_t G::StrImp::outputHex( Tout out , wchar_t c )
862{
863 using uwchar_t = typename std::make_unsigned<wchar_t>::type ;
864 std::size_t n = static_cast<uwchar_t>( c ) ;
865 n &= 0xffffU ;
866 out( hexmap()[(n>>12U)%16U] ) ;
867 out( hexmap()[(n>>8U)%16U] ) ;
868 out( hexmap()[(n>>4U)%16U] ) ;
869 out( hexmap()[(n>>0U)%16U] ) ;
870 return 4U ;
871}
872
873 template <typename Tout, typename Tchar>
874std::size_t G::StrImp::outputPrintable( Tout out , Tchar c , Tchar escape_in , char escape_out , bool eight_bit )
875{
876 using Tuchar = typename std::make_unsigned<Tchar>::type ;
877 const auto uc = static_cast<Tuchar>( c ) ; // NOLINT not bugprone-signed-char-misuse
878 std::size_t n = 1U ;
879 if( c == escape_in )
880 {
881 out( escape_out ) ;
882 n++ ;
883 out( escape_out ) ;
884 }
885 else if( !eight_bit && uc >= 0x20U && uc < 0x7fU )
886 {
887 out( static_cast<char>(c) ) ;
888 }
889 else if( eight_bit && ( uc >= 0x20U && uc != 0x7fU ) )
890 {
891 out( static_cast<char>(c) ) ;
892 }
893 else
894 {
895 out( escape_out ) ;
896 n++ ;
897 char c_out = 'x' ;
898 if( uc == 10U )
899 c_out = 'n' ;
900 else if( uc == 13U )
901 c_out = 'r' ;
902 else if( uc == 9U )
903 c_out = 't' ;
904 else if( uc == 0U )
905 c_out = '0' ;
906 out( c_out ) ;
907 if( c_out == 'x' )
908 n += outputHex( out , c ) ;
909 }
910 return n ;
911}
912
913std::string G::Str::printable( const std::string & in , char escape )
914{
915 std::string result ;
916 result.reserve( in.length()*2U + 1U ) ;
917 for( auto c : in )
918 StrImp::outputPrintable( [&result](char cc){result.append(1U,cc);} , c , escape , escape , true ) ;
919 return result ;
920}
921
922std::string G::Str::printable( std::string_view in , char escape )
923{
924 std::string result ;
925 result.reserve( in.length()*2U + 1U ) ;
926 for( auto c : in )
927 StrImp::outputPrintable( [&result](char cc){result.append(1U,cc);} , c , escape , escape , true ) ;
928 return result ;
929}
930
931std::string G::Str::toPrintableAscii( const std::string & in , char escape )
932{
933 std::string result ;
934 result.reserve( in.length()*2U + 1U ) ;
935 for( auto c : in )
936 StrImp::outputPrintable( [&result](char cc){result.append(1U,cc);} , c , escape , escape , false ) ;
937 return result ;
938}
939
940#ifndef G_LIB_SMALL
941std::string G::Str::toPrintableAscii( const std::wstring & in , wchar_t escape )
942{
943 std::string result ;
944 result.reserve( in.length()*2U + 1U ) ;
945 for( auto c : in )
946 StrImp::outputPrintable( [&result](wchar_t cc){result.append(1U,static_cast<char>(cc));} , c , escape , static_cast<char>(escape) , false ) ;
947 return result ;
948}
949#endif
950
951std::string G::Str::readLineFrom( std::istream & stream , std::string_view eol )
952{
953 std::string result ;
954 readLine( stream , result , eol , false , 0U ) ;
955 return result ;
956}
957
958std::istream & G::Str::readLine( std::istream & stream , std::string & line , std::string_view eol ,
959 bool pre_erase , std::size_t limit )
960{
961 if( !stream.good() ) // (new)
962 {
963 // don't pre-erase -- surprising but cf. std::getline()
964 stream.setstate( std::ios_base::failbit ) ;
965 return stream ;
966 }
967
968 if( pre_erase )
969 line.clear() ;
970
971 if( line.empty() && ( eol.empty() || eol.size() == 1U ) && limit == 0U )
972 {
973 std::getline( stream , line , eol.empty() ? '\n' : eol[0] ) ;
974 }
975 else
976 {
977 if( eol.empty() ) eol = std::string_view( "\n" , 1U ) ; // new
978 const char eol_last = eol.at( eol.size()-1U ) ;
979 const std::size_t eol_size = eol.size() ;
980 bool got_eol = StrImp::readLine( stream , line , nullptr , limit ? limit : line.max_size() ,
981 [eol,eol_size,eol_last](std::string &s,char c)
982 {
983 return
984 ( c == eol_last && s.size() >= eol_size ) ?
985 s.find(eol.data(),s.size()-eol_size,eol_size) == (s.size()-eol_size) :
986 false ;
987 } ) ;
988 if( got_eol )
989 line.erase( line.size() - eol.size() ) ;
990 }
991 return stream ;
992}
993
994std::istream & G::Str::readLine( std::istream & stream , std::string & line , Eol eol ,
995 bool pre_erase , std::size_t limit )
996{
997 if( eol == Eol::CrLf )
998 {
999 return readLine( stream , line , "\r\n"_sv , pre_erase , limit ) ;
1000 }
1001 else // Cr_Lf_CrLf
1002 {
1003 if( pre_erase && stream.good() ) // cf. std::getline()
1004 line.clear() ;
1005
1006 char next = '\0' ;
1007 bool got_eol = StrImp::readLine( stream , line , &next , limit ? limit : line.max_size() ,
1008 [](std::string &,char c)
1009 {
1010 return c == '\n' || c == '\r' ;
1011 } ) ;
1012 if( got_eol )
1013 {
1014 if( line.at(line.size()-1U) == '\r' && next == '\n' )
1015 stream.get() ; // advance over the peeked '\n'
1016 line.erase( line.size()-1U ) ;
1017 }
1018 return stream ;
1019 }
1020}
1021
1022 template <typename Tstring, typename Fn>
1023 bool G::StrImp::readLine( std::istream & stream , Tstring & line , char * next_p , std::size_t limit , Fn eol_fn )
1024{
1025 bool got_eol = false ;
1026 bool got_some = false ;
1027 std::istream::sentry sentry( stream , true ) ;
1028 if( sentry )
1029 {
1030 using traits = std::istream::traits_type ;
1031 std::size_t count = 0U ;
1032 int c = stream.rdbuf()->sgetc() ; // c = *p
1033 while( count < limit && c != traits::eof() )
1034 {
1035 line.append( 1U , traits::to_char_type(c) ) ;
1036 got_eol = eol_fn( line , traits::to_char_type(c) ) ;
1037 if( got_eol )
1038 break ;
1039
1040 count++ ;
1041 c = stream.rdbuf()->snextc() ; // c = *(++p)
1042 }
1043 got_some = count > 0U ;
1044 if( count == limit )
1045 {
1046 stream.setstate( std::ios_base::failbit ) ;
1047 }
1048 else if( c == traits::eof() )
1049 {
1050 stream.setstate( std::ios_base::eofbit ) ;
1051 }
1052 else
1053 {
1054 got_some = true ;
1055 stream.rdbuf()->sbumpc() ; // ++p
1056 if( next_p )
1057 *next_p = traits::to_char_type( stream.rdbuf()->sgetc() ) ; // next = *p
1058 }
1059 }
1060 if( !got_some )
1061 stream.setstate( std::ios_base::failbit ) ;
1062 return got_eol ;
1063}
1064
1065 template <typename S, typename T, typename SV>
1066 void G::StrImp::splitIntoTokens( const S & in , T & out , const SV & ws )
1067{
1068 for( std::size_t p = 0U ; p != S::npos ; )
1069 {
1070 p = in.find_first_not_of( ws.data() , p , ws.size() ) ;
1071 if( p != S::npos )
1072 {
1073 std::size_t end = in.find_first_of( ws.data() , p , ws.size() ) ;
1074 std::size_t len = end == S::npos ? end : (end-p) ;
1075 out.push_back( in.substr(p,len) ) ;
1076 p = end ;
1077 }
1078 }
1079}
1080
1081 template <typename S, typename T>
1082 void G::StrImp::splitIntoTokens( const S & in , T & out , const S & ws , typename S::value_type esc )
1083{
1084 using string_type = S ;
1085 string_type ews = ws + string_type(1U,esc) ;
1086 for( std::size_t p = 0U ; p != S::npos ; )
1087 {
1088 // find the token start
1089 p = in.find_first_not_of( ws.data() , p , ws.size() ) ;
1090 if( p == S::npos || ( in.at(p) == esc && (p+1) == in.size() ) )
1091 break ;
1092
1093 // find the token end
1094 std::size_t end = in.find_first_of( ews.data() , p , ews.size() ) ;
1095 while( end != S::npos && end < in.size() && in.at(end) == esc )
1096 end = (end+2) < in.size() ? in.find_first_of( ews.data() , end+2 , ews.size() ) : S::npos ;
1097
1098 // extract the token
1099 std::size_t len = end == std::string::npos ? end : (end-p) ;
1100 string_type w( in.substr(p,len) ) ;
1101
1102 // remove whitespace escapes
1103 for( std::size_t i = 0 ; esc && i < w.size() ; i++ )
1104 {
1105 if( w[i] == esc )
1106 {
1107 if( (i+1) < w.size() && ws.find(w[i+1]) != S::npos )
1108 w.erase( i , 1U ) ;
1109 else
1110 i++ ;
1111 }
1112 }
1113
1114 out.push_back( w ) ;
1115 p = end ;
1116 }
1117}
1118
1119 void G::Str::splitIntoTokens( const std::string & in , StringArray & out , std::string_view ws , char esc )
1120{
1121 if( esc && in.find(esc) != std::string::npos )
1122 StrImp::splitIntoTokens( in , out , sv_to_string(ws) , esc ) ;
1123 else
1124 StrImp::splitIntoTokens( in , out , ws ) ;
1125}
1126
1127 G::StringArray G::Str::splitIntoTokens( const std::string & in , std::string_view ws , char esc )
1128{
1129 StringArray out ;
1130 splitIntoTokens( in , out , ws , esc ) ;
1131 return out ;
1132}
1133
1134 template <typename T>
1135 void G::StrImp::splitIntoFields( std::string_view in , T & out , std::string_view ws )
1136{
1137 if( !in.empty() )
1138 {
1139 std::size_t start = 0U ;
1140 std::size_t pos = 0U ;
1141 for(;;)
1142 {
1143 if( pos >= in.size() ) break ;
1144 pos = in.find_first_of( ws.data() , pos , ws.size() ) ;
1145 if( pos == std::string::npos ) break ;
1146 out.push_back( sv_to_string(in.substr(start,pos-start)) ) ;
1147 pos++ ;
1148 start = pos ;
1149 }
1150 out.push_back( sv_to_string(in.substr(start,pos-start)) ) ;
1151 }
1152}
1153
1154 template <typename T>
1155 void G::StrImp::splitIntoFields( std::string_view in_in , T & out , std::string_view ws ,
1156 char escape , bool remove_escapes )
1157{
1158 std::string ews ; // escape+whitespace
1159 ews.reserve( ws.size() + 1U ) ;
1160 ews.assign( ws.data() , ws.size() ) ;
1161 if( escape != '\0' ) ews.append( 1U , escape ) ;
1162 if( !in_in.empty() )
1163 {
1164 std::string in = sv_to_string( in_in ) ;
1165 std::size_t start = 0U ;
1166 std::size_t pos = 0U ;
1167 for(;;)
1168 {
1169 if( pos >= in.size() ) break ;
1170 pos = in.find_first_of( ews , pos ) ;
1171 if( pos == std::string::npos ) break ;
1172 if( in.at(pos) == escape )
1173 {
1174 if( remove_escapes )
1175 in.erase( pos , 1U ) ;
1176 else
1177 pos++ ;
1178 pos++ ;
1179 }
1180 else
1181 {
1182 out.push_back( in.substr(start,pos-start) ) ;
1183 pos++ ;
1184 start = pos ;
1185 }
1186 }
1187 out.push_back( in.substr(start,pos-start) ) ;
1188 }
1189}
1190
1191 void G::Str::splitIntoFields( std::string_view in , StringArray & out , char sep ,
1192 char escape , bool remove_escapes )
1193{
1194 StrImp::splitIntoFields( in , out , std::string_view(&sep,1U) , escape , remove_escapes ) ;
1195}
1196
1197 G::StringArray G::Str::splitIntoFields( std::string_view in , char sep )
1198{
1199 G::StringArray out ;
1200 StrImp::splitIntoFields( in , out , std::string_view(&sep,1U) ) ;
1201 return out ;
1202}
1203
1204#ifndef G_LIB_SMALL
1205std::string G::Str::join( std::string_view sep , const StringMap & map , std::string_view pre ,
1206 std::string_view post )
1207{
1208 std::string result ;
1209 int n = 0 ;
1210 for( const auto & map_item : map )
1211 result
1212 .append(sep.data(),(n++)?sep.size():0U)
1213 .append(map_item.first)
1214 .append(pre.data(),pre.size())
1215 .append(map_item.second)
1216 .append(post.data(),post.size()) ;
1217 return result ;
1218}
1219#endif
1220
1221std::string G::Str::join( std::string_view sep , const StringArray & strings )
1222{
1223 std::string result ;
1224 int n = 0 ;
1225 for( const auto & item : strings )
1226 result
1227 .append(sep.data(),(n++)?sep.size():0U)
1228 .append(item) ;
1229 return result ;
1230}
1231
1232std::string G::Str::join( std::string_view sep , std::string_view s1 , std::string_view s2 ,
1233 std::string_view s3 , std::string_view s4 , std::string_view s5 , std::string_view s6 ,
1234 std::string_view s7 , std::string_view s8 , std::string_view s9 )
1235{
1236 std::string result ;
1237 StrImp::join( sep , result , s1 ) ;
1238 StrImp::join( sep , result , s2 ) ;
1239 StrImp::join( sep , result , s3 ) ;
1240 StrImp::join( sep , result , s4 ) ;
1241 StrImp::join( sep , result , s5 ) ;
1242 StrImp::join( sep , result , s6 ) ;
1243 StrImp::join( sep , result , s7 ) ;
1244 StrImp::join( sep , result , s8 ) ;
1245 StrImp::join( sep , result , s9 ) ;
1246 return result ;
1247}
1248
1249 void G::StrImp::join( std::string_view sep , std::string & result , std::string_view s )
1250{
1251 if( !result.empty() && !s.empty() )
1252 result.append( sep.data() , sep.size() ) ;
1253 result.append( s.data() , s.size() ) ;
1254}
1255
1256 G::StringArray G::Str::keys( const StringMap & map )
1257{
1258 StringArray result ;
1259 result.reserve( map.size() ) ;
1260 std::transform( map.begin() , map.end() , std::back_inserter(result) ,
1261 [](const StringMap::value_type & pair){return pair.first;} ) ;
1262 return result ;
1263}
1264
1265std::string_view G::Str::ws() noexcept
1266{
1267 static constexpr std::string_view chars_ws = " \t\n\r"_sv ;
1268 static_assert( chars_ws.size() == 4U , "" ) ;
1269 return chars_ws ;
1270}
1271
1272#ifndef G_LIB_SMALL
1273std::string_view G::Str::alnum() noexcept
1274{
1275 return sv_substr_noexcept( alnum_() , 0U , alnum_().size()-1U ) ;
1276}
1277#endif
1278
1279std::string_view G::Str::alnum_() noexcept
1280{
1281 static constexpr std::string_view chars_alnum_ = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz_"_sv ;
1282 static_assert( chars_alnum_.size() == 26U+10U+26U+1U , "" ) ;
1283 return chars_alnum_ ;
1284}
1285
1286#ifndef G_LIB_SMALL
1287std::string_view G::Str::meta() noexcept
1288{
1289 static constexpr std::string_view chars_meta = "~<>[]*$|?\\(){}\"`'&;="_sv ; // bash meta-chars plus "~"
1290 return chars_meta ;
1291}
1292#endif
1293
1294std::string G::Str::head( std::string_view in , std::size_t pos , std::string_view default_ )
1295{
1296 return
1297 pos == std::string::npos ?
1298 sv_to_string(default_) :
1299 ( pos == 0U ? std::string() : sv_to_string( pos >= in.size() ? in : in.substr(0U,pos) ) ) ;
1300}
1301
1302std::string G::Str::head( std::string_view in , std::string_view sep , bool default_empty )
1303{
1304 std::size_t pos = sep.empty() ? std::string::npos : in.find( sep ) ;
1305 return head( in , pos , default_empty ? std::string_view() : in ) ;
1306}
1307
1308std::string_view G::Str::headView( std::string_view in , std::size_t pos , std::string_view default_ ) noexcept
1309{
1310 return
1311 pos == std::string::npos ?
1312 default_ :
1313 ( pos == 0U ? std::string_view(in.data(),std::size_t(0U)) : ( pos >= in.size() ? in : sv_substr_noexcept(in,0U,pos) ) ) ;
1314}
1315
1316std::string_view G::Str::headView( std::string_view in , std::string_view sep , bool default_empty ) noexcept
1317{
1318 std::size_t pos = sep.empty() ? std::string::npos : in.find( sep ) ;
1319 return headView( in , pos , default_empty ? std::string_view(in.data(),std::size_t(0U)) : in ) ;
1320}
1321
1322std::string G::Str::tail( std::string_view in , std::size_t pos , std::string_view default_ )
1323{
1324 return
1325 pos == std::string::npos ?
1326 sv_to_string(default_) :
1327 ( (pos+1U) >= in.size() ? std::string() : sv_to_string(in.substr(pos+1U)) ) ;
1328}
1329
1330std::string G::Str::tail( std::string_view in , std::string_view sep , bool default_empty )
1331{
1332 std::size_t pos = sep.empty() ? std::string::npos : in.find(sep) ;
1333 if( pos != std::string::npos ) pos += (sep.size()-1U) ;
1334 return tail( in , pos , default_empty ? std::string_view() : in ) ;
1335}
1336
1337std::string_view G::Str::tailView( std::string_view in , std::size_t pos , std::string_view default_ ) noexcept
1338{
1339 return
1340 pos == std::string::npos ?
1341 default_ :
1342 ( (pos+1U) >= in.size() ? std::string_view() : sv_substr_noexcept(in,pos+1U) ) ;
1343}
1344
1345std::string_view G::Str::tailView( std::string_view in , std::string_view sep , bool default_empty ) noexcept
1346{
1347 std::size_t pos = sep.empty() ? std::string::npos : in.find(sep) ;
1348 if( pos != std::string::npos ) pos += (sep.size()-1U) ;
1349 return tailView( in , pos , default_empty ? std::string_view() : in ) ;
1350}
1351
1352 bool G::Str::tailMatch( std::string_view in , std::string_view tail ) noexcept
1353{
1354 return
1355 tail.empty() ||
1356 ( in.size() >= tail.size() && 0 == in.compare(in.size()-tail.size(),tail.size(),tail.data()) ) ;
1357}
1358
1359 bool G::Str::headMatch( std::string_view in , std::string_view head ) noexcept
1360{
1361 return
1362 head.empty() ||
1363 ( in.size() >= head.size() && 0 == in.compare(0U,head.size(),head.data()) ) ;
1364}
1365
1366std::string G::Str::positive()
1367{
1368 return "yes" ;
1369}
1370
1371std::string G::Str::negative()
1372{
1373 return "no" ;
1374}
1375
1376 bool G::Str::isPositive( std::string_view s_in ) noexcept
1377{
1378 std::string_view s = trimmedView( s_in , ws() ) ;
1379 return !s.empty() && (
1380 sv_imatch(s,"y"_sv) || sv_imatch(s,"yes"_sv) || sv_imatch(s,"t"_sv) ||
1381 sv_imatch(s,"true"_sv) || sv_imatch(s,"1"_sv) || sv_imatch(s,"on"_sv) ) ;
1382}
1383
1384 bool G::Str::isNegative( std::string_view s_in ) noexcept
1385{
1386 std::string_view s = trimmedView( s_in , ws() ) ;
1387 return !s.empty() && (
1388 sv_imatch(s,"n"_sv) || sv_imatch(s,"no"_sv) || sv_imatch(s,"f"_sv) ||
1389 sv_imatch(s,"false"_sv) || sv_imatch(s,"0"_sv) || sv_imatch(s,"off"_sv) ) ;
1390}
1391
1392 bool G::Str::match( std::string_view a , std::string_view b ) noexcept
1393{
1394 return a == b ;
1395}
1396
1397 bool G::StrImp::ilessc( char c1 , char c2 ) noexcept
1398{
1399 if( c1 >= 'a' && c1 <= 'z' ) c1 -= '\x20' ;
1400 if( c2 >= 'a' && c2 <= 'z' ) c2 -= '\x20' ;
1401 return c1 < c2 ;
1402}
1403
1404 bool G::Str::iless( std::string_view a , std::string_view b ) noexcept
1405{
1406 return std::lexicographical_compare( a.begin() , a.end() , b.begin() , b.end() , StrImp::ilessc ) ; // noexcept?
1407}
1408
1409 bool G::StrImp::imatchc( char c1 , char c2 ) noexcept
1410{
1411 return sv_imatch( std::string_view(&c1,1U) , std::string_view(&c2,1U) ) ;
1412}
1413
1414#ifndef G_LIB_SMALL
1415 bool G::Str::imatch( char c1 , char c2 ) noexcept
1416{
1417 return StrImp::imatchc( c1 , c2 ) ;
1418}
1419#endif
1420
1421 bool G::Str::imatch( std::string_view a , std::string_view b ) noexcept
1422{
1423 return sv_imatch( a , b ) ;
1424}
1425
1426#ifndef G_LIB_SMALL
1427 bool G::StrImp::imatch( const std::string & a , const std::string & b )
1428{
1429 return sv_imatch( std::string_view(a) , std::string_view(b) ) ;
1430}
1431#endif
1432
1433std::size_t G::Str::ifind( std::string_view s , std::string_view key )
1434{
1435 return ifindat( s , key , 0U ) ;
1436}
1437
1438std::size_t G::Str::ifindat( std::string_view s , std::string_view key , std::size_t pos )
1439{
1440 if( s.empty() || key.empty() || pos >= s.size() ) return std::string::npos ;
1441 const char * p = std::search( s.data()+pos , s.data()+s.size() , key.data() , key.data()+key.size() , StrImp::imatchc ) ; // NOLINT narrowing
1442 return p == (s.data()+s.size()) ? std::string::npos : std::distance(s.data(),p) ;
1443}
1444
1445 template <typename T, typename V>
1446T G::StrImp::unique( T in , T end , V repeat , V replacement )
1447{
1448 // (maybe use std::adjacent_find())
1449 T out = in ;
1450 while( in != end )
1451 {
1452 T in_next = in ; ++in_next ;
1453 if( *in == repeat && in_next != end && *in_next == repeat )
1454 {
1455 while( in != end && *in == repeat )
1456 ++in ;
1457 *out++ = replacement ;
1458 }
1459 else
1460 {
1461 *out++ = *in++ ;
1462 }
1463 }
1464 return out ; // new end
1465}
1466
1467std::string G::Str::unique( const std::string & s_in , char c , char r )
1468{
1469 std::string s( s_in ) ;
1470 s.erase( StrImp::unique( s.begin() , s.end() , c , r ) , s.end() ) ;
1471 return s ;
1472}
1473
1474std::string G::Str::unique( const std::string & s , char c )
1475{
1476 return s.find(c) == std::string::npos ? s : unique( s , c , c ) ;
1477}
1478
1479 void G::StrImp::strncpy( char * dst , const char * src , std::size_t n ) noexcept
1480{
1481 // (because 'strncpy considered dangerous' analytics)
1482 for( ; n ; n-- , dst++ , src++ )
1483 {
1484 *dst = *src ;
1485 if( *src == '\0' )
1486 break ;
1487 }
1488}
1489
1490errno_t G::Str::strncpy_s( char * dst , std::size_t n_dst , const char * src , std::size_t count ) noexcept
1491{
1492 if( dst == nullptr || n_dst == 0U )
1493 return EINVAL ;
1494
1495 if( src == nullptr )
1496 {
1497 *dst = '\0' ;
1498 return EINVAL ;
1499 }
1500
1501 std::size_t n = std::strlen( src ) ;
1502 if( count != truncate && count < n )
1503 n = count ;
1504
1505 if( count == truncate && n >= n_dst )
1506 {
1507 StrImp::strncpy( dst , src , n_dst ) ;
1508 dst[n_dst-1U] = '\0' ;
1509 return 0 ; // STRUNCATE
1510 }
1511 else if( n >= n_dst )
1512 {
1513 *dst = '\0' ;
1514 return ERANGE ; // dst too small
1515 }
1516 else
1517 {
1518 StrImp::strncpy( dst , src , n ) ;
1519 dst[n] = '\0' ;
1520 return 0 ;
1521 }
1522}
1523
G::Str::trimRight
static std::string & trimRight(std::string &s, std::string_view ws, std::size_t limit=0U)
Trims the rhs of s, taking off up to 'limit' of the 'ws' characters.
Definition: gstr.cpp:313
G::Str::lower
static std::string lower(std::string_view)
Returns a copy of 's' in which all seven-bit upper-case characters have been replaced by lower-case c...
Definition: gstr.cpp:824
G::Str::isUInt
static bool isUInt(std::string_view s) noexcept
Returns true if the string can be converted into an unsigned integer without throwing an exception.
Definition: gstr.cpp:446
G::Str::isPrintable
static bool isPrintable(std::string_view s) noexcept
Returns true if every character is 0x20 or above but not 0x7f.
Definition: gstr.cpp:418
G::Str::trimLeftView
static std::string_view trimLeftView(std::string_view, std::string_view ws, std::size_t limit=0U) noexcept
Trims the lhs of s, taking off up to 'limit' of the 'ws' characters.
Definition: gstr.cpp:300
G::Str::toShort
static short toShort(std::string_view s)
Converts string 's' to a short.
Definition: gstr.cpp:600
G::Str::splitIntoTokens
static void splitIntoTokens(const std::string &in, StringArray &out, std::string_view ws, char esc='\0')
Splits the string into 'ws'-delimited tokens.
Definition: gstr.cpp:1119
G::Str::trimmedView
static std::string_view trimmedView(std::string_view s, std::string_view ws) noexcept
Returns a trim()med view of the input view.
Definition: gstr.cpp:351
G::Str::tailMatch
static bool tailMatch(std::string_view in, std::string_view ending) noexcept
Returns true if the string has the given ending (or the given ending is empty).
Definition: gstr.cpp:1352
G::Str::toInt
static int toInt(std::string_view s)
Converts string 's' to an int.
Definition: gstr.cpp:538
G::Str::toLower
static void toLower(std::string &s)
Replaces all seven-bit upper-case characters in string 's' by lower-case characters.
Definition: gstr.cpp:819
G::Str::escape
static void escape(std::string &s, char c_escape, std::string_view specials_in, std::string_view specials_out)
Prefixes each occurrence of one of the special-in characters with the escape character and its corres...
Definition: gstr.cpp:100
G::Str::join
static std::string join(std::string_view sep, const StringArray &strings)
Concatenates an array of strings with separators.
Definition: gstr.cpp:1221
G::Str::keys
static StringArray keys(const StringMap &string_map)
Extracts the keys from a map of strings.
Definition: gstr.cpp:1256
G::Str::toFloat
static float toFloat(const std::string &s)
Converts string 's' to a float.
Definition: gstr.cpp:517
G::Str::positive
static std::string positive()
Returns a default positive string. See isPositive().
Definition: gstr.cpp:1366
G::Str::trimRightView
static std::string_view trimRightView(std::string_view sv, std::string_view ws, std::size_t limit=0U) noexcept
Trims the rhs of s, taking off up to 'limit' of the 'ws' characters.
Definition: gstr.cpp:325
G::Str::imatch
static bool imatch(char, char) noexcept
Returns true if the two characters are the same, ignoring seven-bit case.
Definition: gstr.cpp:1415
G::Str::unescaped
static std::string unescaped(const std::string &s)
Returns the unescape()d version of s.
Definition: gstr.cpp:203
G::Str::isSimple
static bool isSimple(std::string_view s) noexcept
Returns true if every character is alphanumeric or "-" or "_".
Definition: gstr.cpp:423
G::Str::toDouble
static double toDouble(const std::string &s)
Converts string 's' to a double.
Definition: gstr.cpp:495
G::Str::ifindat
static std::size_t ifindat(std::string_view s, std::string_view key, std::size_t pos)
Returns the position of the key in 's' at of after position 'pos' using a seven-bit case-insensitive ...
Definition: gstr.cpp:1438
G::Str::replaced
static std::string replaced(const std::string &s, char from, char to)
Returns the string 's' with all occurrences of 'from' replaced by 'to'.
Definition: gstr.cpp:255
G::Str::fromBool
static std::string fromBool(bool b)
Converts boolean 'b' to a string.
Definition: gstr.cpp:463
G::Str::isPositive
static bool isPositive(std::string_view) noexcept
Returns true if the string has a positive meaning, such as "1", "true", "yes".
Definition: gstr.cpp:1376
G::Str::headMatch
static bool headMatch(std::string_view in, std::string_view head) noexcept
Returns true if the string has the given start (or head is empty).
Definition: gstr.cpp:1359
G::Str::toPrintableAscii
static std::string toPrintableAscii(const std::string &in, char escape='\\')
Returns a 7-bit printable representation of the given input string.
Definition: gstr.cpp:931
G::Str::readLine
static std::istream & readLine(std::istream &stream, std::string &result, std::string_view eol={}, bool pre_erase_result=true, std::size_t limit=0U)
Reads a line from the stream using the given line terminator, which may be multi-character.
Definition: gstr.cpp:958
G::Str::trim
static std::string & trim(std::string &s, std::string_view ws)
Trims both ends of s, taking off any of the 'ws' characters.
Definition: gstr.cpp:338
G::Str::unique
static std::string unique(const std::string &s, char c, char r)
Returns a string with repeated 'c' characters replaced by one 'r' character.
Definition: gstr.cpp:1467
G::Str::isInt
static bool isInt(std::string_view s) noexcept
Returns true if the string can be converted into an integer without throwing an exception.
Definition: gstr.cpp:428
G::Str::splitIntoFields
static void splitIntoFields(std::string_view in, StringArray &out, char sep, char escape='\0', bool remove_escapes=true)
Splits the string into fields.
Definition: gstr.cpp:1191
G::Str::fromULongToHex
static std::string_view fromULongToHex(unsigned long, char *out) noexcept
Low-level conversion from an unsigned value to a lower-case hex string with no leading zeros.
Definition: gstr.cpp:787
G::Str::fromULongLongToHex
static std::string_view fromULongLongToHex(unsigned long long, char *out) noexcept
Low-level conversion from an unsigned value to a lower-case hex string with no leading zeros.
Definition: gstr.cpp:794
G::Str::removeAll
static void removeAll(std::string &, char)
Removes all occurrences of the character from the string. See also only().
Definition: gstr.cpp:262
G::Str::alnum_
static std::string_view alnum_() noexcept
Returns alnum() with an additional trailing underscore character.
Definition: gstr.cpp:1279
G::Str::isNumeric
static bool isNumeric(std::string_view s, bool allow_minus_sign=false) noexcept
Returns true if every character is a decimal digit.
Definition: gstr.cpp:400
G::Str::negative
static std::string negative()
Returns a default negative string. See isNegative().
Definition: gstr.cpp:1371
G::Str::isUShort
static bool isUShort(std::string_view s) noexcept
Returns true if the string can be converted into an unsigned short without throwing an exception.
Definition: gstr.cpp:437
G::Str::toUShort
static unsigned short toUShort(std::string_view s, Limited)
Converts string 's' to an unsigned short.
Definition: gstr.cpp:750
G::Str::isULong
static bool isULong(std::string_view s) noexcept
Returns true if the string can be converted into an unsigned long without throwing an exception.
Definition: gstr.cpp:454
G::Str::removedAll
static std::string removedAll(const std::string &, char)
Removes all occurrences of the character from the string and returns the result.
Definition: gstr.cpp:268
G::Str::printable
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 ...
Definition: gstr.cpp:913
G::Str::upper
static std::string upper(std::string_view)
Returns a copy of 's' in which all seven-bit lower-case characters have been replaced by upper-case c...
Definition: gstr.cpp:836
G::Str::ifind
static std::size_t ifind(std::string_view s, std::string_view key)
Returns the position of the key in 's' using a seven-bit case-insensitive search.
Definition: gstr.cpp:1433
G::Str::tail
static std::string tail(std::string_view in, std::size_t pos, std::string_view default_={})
Returns the last part of the string after the given position.
Definition: gstr.cpp:1322
G::Str::trimLeft
static std::string & trimLeft(std::string &s, std::string_view ws, std::size_t limit=0U)
Trims the lhs of s, taking off up to 'limit' of the 'ws' characters.
Definition: gstr.cpp:288
G::Str::isHex
static bool isHex(std::string_view s) noexcept
Returns true if every character is a hexadecimal digit.
Definition: gstr.cpp:407
G::Str::readLineFrom
static std::string readLineFrom(std::istream &stream, std::string_view eol={})
Reads a line from the stream using the given line terminator.
Definition: gstr.cpp:951
G::Str::toUpper
static void toUpper(std::string &s)
Replaces all seven-bit lower-case characters in string 's' by upper-case characters.
Definition: gstr.cpp:831
G::Str::alnum
static std::string_view alnum() noexcept
Returns a string of seven-bit alphanumeric characters, ie A-Z, a-z and 0-9.
Definition: gstr.cpp:1273
G::Str::tailView
static std::string_view tailView(std::string_view in, std::size_t pos, std::string_view default_={}) noexcept
Like tail() but returning a view into the input string.
Definition: gstr.cpp:1337
G::Str::toUInt
static unsigned int toUInt(std::string_view s)
Converts string 's' to an unsigned int.
Definition: gstr.cpp:648
G::Str::toLong
static long toLong(std::string_view s)
Converts string 's' to a long.
Definition: gstr.cpp:567
G::Str::fromDouble
static std::string fromDouble(double d)
Converts double 'd' to a string.
Definition: gstr.cpp:470
G::Str::replaceAll
static unsigned int replaceAll(std::string &s, std::string_view from, std::string_view to)
Does a global replace on string 's', replacing all occurrences of sub-string 'from' with 'to'.
Definition: gstr.cpp:247
G::Str::dequote
static std::string dequote(const std::string &, char qq='\"' , char esc = '\\' , std::string_view ws = Str::ws() , std::string_view nbws = Str::ws() )
Dequotes a string by removing unescaped quotes and escaping quoted whitespace, so "qq-aaa-esc-qq-bbb-...
Definition: gstr.cpp:128
G::Str::ws
static std::string_view ws() noexcept
Returns a string of standard whitespace characters.
Definition: gstr.cpp:1265
G::Str::replace
static bool replace(std::string &s, std::string_view from, std::string_view to, std::size_t *pos_p=nullptr)
A std::string_view overload.
Definition: gstr.cpp:226
G::Str::match
static bool match(std::string_view, std::string_view) noexcept
Returns true if the two strings are the same.
Definition: gstr.cpp:1392
G::Str::strncpy_s
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().
Definition: gstr.cpp:1490
G::Str::toULong
static unsigned long toULong(std::string_view s, Limited)
Converts string 's' to an unsigned long.
Definition: gstr.cpp:669
G::Str::headView
static std::string_view headView(std::string_view in, std::size_t pos, std::string_view default_={}) noexcept
Like head() but returning a view into the input string.
Definition: gstr.cpp:1308
G::Str::head
static std::string head(std::string_view in, std::size_t pos, std::string_view default_={})
Returns the first part of the string up to just before the given position.
Definition: gstr.cpp:1294
G::Str::iless
static bool iless(std::string_view, std::string_view) noexcept
Returns true if the first string is lexicographically less than the first, after seven-bit lower-case...
Definition: gstr.cpp:1404
G::Str::isNegative
static bool isNegative(std::string_view) noexcept
Returns true if the string has a negative meaning, such as "0", "false", "no".
Definition: gstr.cpp:1384
G::Str::toBool
static bool toBool(std::string_view s)
Converts string 's' to a bool.
Definition: gstr.cpp:481
G::Str::meta
static std::string_view meta() noexcept
Returns a list of shell meta-characters with a tilde as the first character.
Definition: gstr.cpp:1287
G::Str::unescape
static void unescape(std::string &s, char c_escape, std::string_view specials_in, std::string_view 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:180
G::Str::isPrintableAscii
static bool isPrintableAscii(std::string_view s) noexcept
Returns true if every character is between 0x20 and 0x7e inclusive.
Definition: gstr.cpp:413
G::Str::trimmed
static std::string trimmed(const std::string &s, std::string_view ws)
Returns a trim()med version of s.
Definition: gstr.cpp:343
G::Str::escaped
static std::string escaped(std::string_view, char c_escape, std::string_view specials_in, std::string_view specials_out)
Returns the escape()d string.
Definition: gstr.cpp:92
G::Str::only
static std::string only(std::string_view allow_chars, std::string_view s)
Returns the 's' with all occurrences of the characters not appearing in the first string deleted.
Definition: gstr.cpp:276
gassert.h
gstr.h
G
Low-level classes.
Definition: garg.h:36
G::StringArray
std::vector< std::string > StringArray
A std::vector of std::strings.
Definition: gstringarray.h:30
G::StringMap
std::map< std::string, std::string > StringMap
A std::map of std::strings.
Definition: gstringmap.h:30
std
STL namespace.
G::Str::Hex
Overload discrimiator for G::Str::toUWhatever() indicating hexadecimal strings.
Definition: gstr.h:59
G::Str::Limited
Overload discrimiator for G::Str::toUWhatever() requesting a range-limited result.
Definition: gstr.h:56