1 //==============================================================================
2 //
3 //  This file is part of GPSTk, the GPS Toolkit.
4 //
5 //  The GPSTk is free software; you can redistribute it and/or modify
6 //  it under the terms of the GNU Lesser General Public License as published
7 //  by the Free Software Foundation; either version 3.0 of the License, or
8 //  any later version.
9 //
10 //  The GPSTk is distributed in the hope that it will be useful,
11 //  but WITHOUT ANY WARRANTY; without even the implied warranty of
12 //  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 //  GNU Lesser General Public License for more details.
14 //
15 //  You should have received a copy of the GNU Lesser General Public
16 //  License along with GPSTk; if not, write to the Free Software Foundation,
17 //  Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
18 //
19 //  This software was developed by Applied Research Laboratories at the
20 //  University of Texas at Austin.
21 //  Copyright 2004-2020, The Board of Regents of The University of Texas System
22 //
23 //==============================================================================
24 
25 //==============================================================================
26 //
27 //  This software was developed by Applied Research Laboratories at the
28 //  University of Texas at Austin, under contract to an agency or agencies
29 //  within the U.S. Department of Defense. The U.S. Government retains all
30 //  rights to use, duplicate, distribute, disclose, or release this software.
31 //
32 //  Pursuant to DoD Directive 523024
33 //
34 //  DISTRIBUTION STATEMENT A: This software has been approved for public
35 //                            release, distribution is unlimited.
36 //
37 //==============================================================================
38 
39 /**
40  * @file StringUtils.hpp
41  * StringUtils namespace and GPSTK string utility functions
42  */
43 
44 #ifndef GPSTK_STRINGUTILS_HPP
45 #define GPSTK_STRINGUTILS_HPP
46 
47 #include <string>
48 #include <sstream>
49 #include <iomanip>
50 #include <iostream>
51 #include <list>
52 #include <vector>
53 #include <cstdio>   /// @todo Get rid of the stdio.h dependency if possible.
54 #include <cctype>
55 #include <limits>
56 
57 #ifdef _WIN32
58 #if _MSC_VER < 1700
59 // For lower version of visual studio 2012 use gnu regex
60 #include <regex.h>
61 #pragma comment(lib, "regex.lib")
62 #else
63 // visual studio 2012 support c++ 0x, and we use std::regex
64 #include <regex>
65 #endif
66 #else
67 // TODO: we should use std::regex for upper than g++ 4.6
68 #include <regex.h>
69 #endif
70 
71 #include "Exception.hpp"
72 #include "HexDumpDataConfig.hpp"
73 
74 namespace gpstk
75 {
76       /** @defgroup stringutilsgroup Text String Manipulation Tools
77        * @copydoc gpstk::StringUtils
78        */
79 
80       /**
81        * Stuff to make the C++ string class a little easier to use.
82        *
83        * All functions here will throw
84        * gpstk::StringUtils::StringException on an error. Any
85        * std::exception is converted to a
86        * gpstk::StringUtils::StringException so that's the only
87        * exception a user of this class needs to catch.
88        *
89        * For any function that modifies a string, make sure there is a
90        * non-const (std::string&) version and a const (const
91        * std::string&) version. The convention for writing the
92        * functions is the non-const version fully implements the
93        * function and the const version calls the non-const version.
94        */
95    namespace StringUtils
96    {
97          // Most of the functionality here is inlined since they are
98          // fairly small functions.
99 
100          /// @ingroup stringutilsgroup
101          //@{
102 
103          /// This is thrown instread of a std::exception when a
104          /// gpstk::StringUtils function fails.
105          /// @ingroup exceptiongroup
106       NEW_EXCEPTION_CLASS(StringException, Exception);
107 
108          /// Leading character for floatFormat(), after any whitespace or sign.
109       enum class FFLead
110       {
111          Zero,    ///< Start with zero, e.g. 0.12345
112          Decimal, ///< Start with decimal, e.g. .12345
113          NonZero  ///< Start with the first non-zero digit, e.g. 1.2345
114       };
115 
116          /// How to handle sign in floatFormat()
117       enum class FFSign
118       {
119          NegOnly,  ///< Prefix output with a minus sign (neg) or nothing (pos)
120          NegSpace, ///< Prefix output with a minus sign (neg) or space (pos)
121          NegPos    ///< Prefix output with a minus sign (neg) or plus sign (pos)
122       };
123 
124          /// Alignment of data for floatFormat()
125       enum class FFAlign
126       {
127          Left,  ///< Formatted output will be left-aligned.
128          Right  ///< Formatted output will be right-aligned.
129       };
130 
131          /**
132           * Perform a formatted hex-dump of the (potentially) binary
133           * data to the given stream.
134           *
135           * @note argument order reversed from earlier implementations
136           *   to avoid ambiguity of prototypes.
137           *
138           * @param[in] data data to hex-dump.
139           * @param[in,out] s stream to dump data to.
140           * @param[in] cfg formatting configuration.
141           */
142       void hexDumpData(const std::string& data,
143                        std::ostream& s,
144                        const HexDumpDataConfig& cfg = HexDumpDataConfig());
145 
146          /**
147           * Perform a formatted hex-dump of the (potentially) binary
148           * data to the given stream.
149           *
150           * @deprecated Set cfg.prefix and use the 3-parameter
151           *   function instead.
152           *
153           * @param s stream to dump data to.
154           * @param data data to hex-dump.
155           * @param indent indents the string by that many spaces.
156           * @param cfg formatting configuration.
157           */
158       inline void hexDumpData(std::ostream& s,
159                               const std::string& data,
160                               unsigned indent = 0,
161                               const HexDumpDataConfig& cfg = HexDumpDataConfig());
162 
163          /**
164           * Perform a formatted hex-dump of the (potentially) binary
165           * data to the given stream.
166           *
167           * @deprecated Set cfg.prefix and use the 3-parameter
168           *   function instead.
169           *
170           * @param s stream to dump data to.
171           * @param data data to hex-dump.
172           * @param tag string to put at the beginning of each line of output.
173           * @param cfg formatting configuration.
174           */
175       inline void hexDumpData(std::ostream& s,
176                               const std::string& data,
177                               const std::string& tag,
178                               HexDumpDataConfig cfg = HexDumpDataConfig());
179 
180          /**
181           * Remove a string from the beginning of another string.
182           * Occurrences of the string \a aString appearing
183           * at the beginning of the string \a s are removed.
184           * @param s string to be stripped (modified).
185           * @param aString string to remove.
186           * @param num maximum number of occurrences to remove.
187           * @throw StringException if there's a std::exception thrown.
188           * @return a reference to \a s.
189           */
190       inline
191       std::string& stripLeading(std::string& s,
192                                 const std::string& aString,
193                                 std::string::size_type num = std::string::npos);
194 
195          /**
196           * Remove a string from the beginning of another string const version.
197           * Occurrences of the string \a aString appearing
198           * at the beginning of the string \a s are removed.
199           * @param s string to be stripped (modified).
200           * @param aString string to remove.
201           * @param num maximum number of occurrences to remove.
202           * @throw StringException if there's a std::exception thrown.
203           * @return a reference to \a s.
204           */
stripLeading(const std::string & s,const std::string & aString,std::string::size_type num=std::string::npos)205       inline std::string stripLeading(const std::string& s,
206                                       const std::string& aString,
207                                       std::string::size_type num = std::string::npos)
208       { std::string t(s); stripLeading(t, aString, num); return t; }
209 
210          /**
211           * Remove a string from the beginning of another string.
212           * Occurrences of the string \a pString appearing
213           * at the beginning of the string \a s are removed.
214           * @param s string to be stripped (modified).
215           * @param pString string to remove.
216           * @param num maximum number of occurrences to remove.
217           * @throw StringException if there's a std::exception thrown.
218           * @return a reference to \a s.
219           */
stripLeading(std::string & s,const char * pString,std::string::size_type num=std::string::npos)220       inline std::string& stripLeading(std::string& s,
221                                        const char* pString,
222                                        std::string::size_type num = std::string::npos)
223       { return stripLeading(s, std::string(pString), num); }
224 
225          /**
226           * Remove a string from the beginning of another string const version.
227           * Occurrences of the string \a pString appearing
228           * at the beginning of the string \a s are removed.
229           * @param s string to be stripped (modified).
230           * @param pString string to remove.
231           * @param num maximum number of occurrences to remove.
232           * @throw StringException if there's a std::exception thrown.
233           * @return a reference to \a s.
234           */
stripLeading(const std::string & s,const char * pString,std::string::size_type num=std::string::npos)235       inline std::string stripLeading(const std::string& s,
236                                       const char* pString,
237                                       std::string::size_type num = std::string::npos)
238       { std::string t(s); stripLeading(t, std::string(pString), num); return t; }
239 
240          /**
241           * Strip character(s) from the beginning of a string.
242           * Occurrences of the character \a aCharacter appearing
243           * at the beginning of the string \a s are removed.
244           * @param s string to be stripped (modified).
245           * @param aCharacter character to remove.
246           * @param num maximum number of occurrences to remove.
247           * @throw StringException if there's a std::exception thrown.
248           * @return a reference to \a s.
249           */
stripLeading(std::string & s,const char aCharacter,std::string::size_type num=std::string::npos)250       inline std::string& stripLeading(std::string& s,
251                                        const char aCharacter,
252                                        std::string::size_type num = std::string::npos)
253       { return stripLeading(s, std::string(1,aCharacter), num); }
254 
255          /**
256           * Strip character(s) from the beginning of a string const version.
257           * Occurrences of the character \a aCharacter appearing
258           * at the beginning of the string \a s are removed.
259           * @param s string to be stripped (modified).
260           * @param aCharacter character to remove.
261           * @param num maximum number of occurrences to remove.
262           * @throw StringException if there's a std::exception thrown.
263           * @return a reference to \a s.
264           */
stripLeading(const std::string & s,const char aCharacter,std::string::size_type num=std::string::npos)265       inline std::string stripLeading(const std::string& s,
266                                       const char aCharacter,
267                                       std::string::size_type num = std::string::npos)
268       { std::string t(s); stripLeading(t, std::string(1,aCharacter), num); return t; }
269 
270          /**
271           * Strip blanks from the beginning of a string.
272           * Occurrences of the space character appearing
273           * at the beginning of the string \a s are removed.
274           * @param s string to be stripped (modified).
275           * @param num maximum number of occurrences to remove.
276           * @throw StringException if there's a std::exception thrown.
277           * @return a reference to \a s.
278           */
stripLeading(std::string & s,std::string::size_type num=std::string::npos)279       inline std::string& stripLeading(std::string& s,
280                                        std::string::size_type num = std::string::npos)
281       { return stripLeading(s,std::string(1,' '),num); }
282 
283          /**
284           * Strip blanks from the beginning of a string const version.
285           * Occurrences of the space character appearing
286           * at the beginning of the string \a s are removed.
287           * @param s string to be stripped (modified).
288           * @param num maximum number of occurrences to remove.
289           * @throw StringException if there's a std::exception thrown.
290           * @return a reference to \a s.
291           */
stripLeading(const std::string & s,std::string::size_type num=std::string::npos)292       inline std::string stripLeading(const std::string& s,
293                                       std::string::size_type num = std::string::npos)
294       { std::string t(s); stripLeading(t,std::string(1,' '),num); return t; }
295 
296          /**
297           * Remove a string from the end of another string.
298           * Occurrences of the string \a aString appearing
299           * at the end of the string \a s are removed.
300           * @param s string to be stripped (modified).
301           * @param aString string to remove.
302           * @param num maximum number of occurrences to remove.
303           * @throw StringException if there's a std::exception thrown.
304           * @return a reference to \a s.
305           */
306       inline std::string& stripTrailing(std::string& s,
307                                         const std::string& aString,
308                                         std::string::size_type num = std::string::npos);
309 
310          /**
311           * Remove a string from the end of another string const version.
312           * Occurrences of the string \a aString appearing
313           * at the end of the string \a s are removed.
314           * @param s string to be stripped (modified).
315           * @param aString string to remove.
316           * @param num maximum number of occurrences to remove.
317           * @throw StringException if there's a std::exception thrown.
318           * @return a reference to \a s.
319           */
stripTrailing(const std::string & s,const std::string & aString,std::string::size_type num=std::string::npos)320       inline std::string stripTrailing(const std::string& s,
321                                        const std::string& aString,
322                                        std::string::size_type num = std::string::npos)
323       { std::string t(s); stripTrailing(t, aString, num); return t;}
324 
325          /**
326           * Remove a string from the end of another string.
327           * Occurrences of the string \a pString appearing
328           * at the end of the string \a s are removed.
329           * @param s string to be stripped (modified).
330           * @param pString string to remove.
331           * @param num maximum number of occurrences to remove.
332           * @throw StringException if there's a std::exception thrown.
333           * @return a reference to \a s.
334           */
stripTrailing(std::string & s,const char * pString,std::string::size_type num=std::string::npos)335       inline std::string& stripTrailing(std::string& s,
336                                         const char* pString,
337                                         std::string::size_type num = std::string::npos)
338       { return stripTrailing(s, std::string(pString), num); }
339 
340          /**
341           * Remove a string from the end of another string const version.
342           * Occurrences of the string \a pString appearing
343           * at the end of the string \a s are removed.
344           * @param s string to be stripped (modified).
345           * @param pString string to remove.
346           * @param num maximum number of occurrences to remove.
347           * @throw StringException if there's a std::exception thrown.
348           * @return a reference to \a s.
349           */
stripTrailing(const std::string & s,const char * pString,std::string::size_type num=std::string::npos)350       inline std::string stripTrailing(const std::string& s,
351                                        const char* pString,
352                                        std::string::size_type num = std::string::npos)
353       { std::string t(s); stripTrailing(t, std::string(pString), num); return t; }
354 
355          /**
356           * Strip character(s) from the end of a string.
357           * Occurrences of the character \a aCharacter appearing
358           * at the end of the string \a s are removed.
359           * @param s string to be stripped (modified).
360           * @param aCharacter character to remove.
361           * @param num maximum number of occurrences to remove.
362           * @throw StringException if there's a std::exception thrown.
363           * @return a reference to \a s.
364           */
stripTrailing(std::string & s,const char aCharacter,std::string::size_type num=std::string::npos)365       inline std::string& stripTrailing(std::string& s,
366                                         const char aCharacter,
367                                         std::string::size_type num = std::string::npos)
368       { return stripTrailing(s, std::string(1,aCharacter), num); }
369 
370          /**
371           * Strip character(s) from the end of a string const version.
372           * Occurrences of the character \a aCharacter appearing
373           * at the end of the string \a s are removed.
374           * @param s string to be stripped (modified).
375           * @param aCharacter character to remove.
376           * @param num maximum number of occurrences to remove.
377           * @throw StringException if there's a std::exception thrown.
378           * @return a reference to \a s.
379           */
stripTrailing(const std::string & s,const char aCharacter,std::string::size_type num=std::string::npos)380       inline std::string stripTrailing(const std::string& s,
381                                        const char aCharacter,
382                                        std::string::size_type num = std::string::npos)
383       { std::string t(s); stripTrailing(t, std::string(1,aCharacter), num); return t; }
384 
385          /**
386           * Strip blanks from the end of a string.
387           * Occurrences of the space character appearing
388           * at the end of the string \a s are removed.
389           * @param s string to be stripped (modified).
390           * @param num maximum number of occurrences to remove.
391           * @throw StringException if there's a std::exception thrown.
392           * @return a reference to \a s.
393           */
stripTrailing(std::string & s,std::string::size_type num=std::string::npos)394       inline std::string& stripTrailing(std::string& s,
395                                         std::string::size_type num = std::string::npos)
396       { return stripTrailing(s, std::string(1,' '), num); }
397 
398          /**
399           * Strip blanks from the end of a string const version.
400           * Occurrences of the space character appearing
401           * at the end of the string \a s are removed.
402           * @param s string to be stripped (modified).
403           * @param num maximum number of occurrences to remove.
404           * @throw StringException if there's a std::exception thrown.
405           * @return a reference to \a s.
406           */
stripTrailing(const std::string & s,std::string::size_type num=std::string::npos)407       inline std::string stripTrailing(const std::string& s,
408                                        std::string::size_type num = std::string::npos)
409       { std::string t(s); stripTrailing(t, std::string(1,' '), num); return t;}
410 
411          /**
412           * Remove a string from the beginning and end of another string.
413           * Occurrences of the string \a aString appearing
414           * at the beginning and end of the string \a s are removed.
415           * @param s string to be stripped (modified).
416           * @param aString string to remove.
417           * @param num maximum number of occurrences to remove.
418           * @throw StringException if there's a std::exception thrown.
419           * @return a reference to \a s.
420           */
421       inline std::string& strip(std::string& s,
422                                 const std::string& aString,
423                                 std::string::size_type num = std::string::npos);
424 
425 
426          /**
427           * Remove a string from the beginning and end of another string const version.
428           * Occurrences of the string \a aString appearing
429           * at the beginning and end of the string \a s are removed.
430           * @param s string to be stripped (modified).
431           * @param aString string to remove.
432           * @param num maximum number of occurrences to remove.
433           * @throw StringException if there's a std::exception thrown.
434           * @return a reference to \a s.
435           */
strip(const std::string & s,const std::string & aString,std::string::size_type num=std::string::npos)436       inline std::string strip(const std::string& s,
437                                const std::string& aString,
438                                std::string::size_type num = std::string::npos)
439       { std::string t(s);  strip(t, aString, num); return t; }
440 
441 
442          /**
443           * Remove a string from the beginning and end of another string.
444           * Occurrences of the string \a pString appearing
445           * at the beginning and end of the string \a s are removed.
446           * @param s string to be stripped (modified).
447           * @param pString string to remove.
448           * @param num maximum number of occurrences to remove.
449           * @throw StringException if there's a std::exception thrown.
450           * @return a reference to \a s.
451           */
strip(std::string & s,const char * pString,std::string::size_type num=std::string::npos)452       inline std::string& strip(std::string& s,
453                                 const char* pString,
454                                 std::string::size_type num = std::string::npos)
455       { return strip(s, std::string(pString), num); }
456 
457          /**
458           * Remove a string from the beginning and end of another string cosnt version.
459           * Occurrences of the string \a pString appearing
460           * at the beginning and end of the string \a s are removed.
461           * @param s string to be stripped (modified).
462           * @param pString string to remove.
463           * @param num maximum number of occurrences to remove.
464           * @throw StringException if there's a std::exception thrown.
465           * @return a reference to \a s.
466           */
strip(const std::string & s,const char * pString,std::string::size_type num=std::string::npos)467       inline std::string strip(const std::string& s,
468                                const char* pString,
469                                std::string::size_type num = std::string::npos)
470       { std::string t(s); strip(t, std::string(pString), num); return t; }
471 
472          /**
473           * Strip character(s) from the beginning and end of a string.
474           * Occurrences of the character \a aCharacter appearing
475           * at the beginning and end of the string \a s are removed.
476           * @param s string to be stripped (modified).
477           * @param aCharacter character to remove.
478           * @param num maximum number of occurrences to remove.
479           * @throw StringException if there's a std::exception thrown.
480           * @return a reference to \a s.
481           */
strip(std::string & s,const char aCharacter,std::string::size_type num=std::string::npos)482       inline std::string& strip(std::string& s,
483                                 const char aCharacter,
484                                 std::string::size_type num = std::string::npos)
485       { return strip(s, std::string(1,aCharacter), num); }
486 
487          /**
488           * Strip character(s) from the beginning and end of a string const version.
489           * Occurrences of the character \a aCharacter appearing
490           * at the beginning and end of the string \a s are removed.
491           * @param s string to be stripped (modified).
492           * @param aCharacter character to remove.
493           * @param num maximum number of occurrences to remove.
494           * @throw StringException if there's a std::exception thrown.
495           * @return a reference to \a s.
496           */
strip(const std::string & s,const char aCharacter,std::string::size_type num=std::string::npos)497       inline std::string strip(const std::string& s,
498                                const char aCharacter,
499                                std::string::size_type num = std::string::npos)
500       { std::string t(s);  strip(t, std::string(1,aCharacter), num); return t;}
501 
502          /**
503           * Strip blanks from the beginning and end of a string.
504           * Occurrences of the space character appearing
505           * at the beginning and end of the string \a s are removed.
506           * @param s string to be stripped (modified).
507           * @param num maximum number of occurrences to remove.
508           * @throw StringException if there's a std::exception thrown.
509           * @return a reference to \a s.
510           */
strip(std::string & s,std::string::size_type num=std::string::npos)511       inline std::string& strip(std::string& s,
512                                 std::string::size_type num = std::string::npos)
513       { return strip(s, std::string(1, ' '), num); }
514 
515          /**
516           * Strip blanks from the beginning and end of a string const version.
517           * Occurrences of the space character appearing
518           * at the beginning and end of the string \a s are removed.
519           * @param s string to be stripped (modified).
520           * @param num maximum number of occurrences to remove.
521           * @throw StringException if there's a std::exception thrown.
522           * @return a reference to \a s.
523           */
strip(const std::string & s,std::string::size_type num=std::string::npos)524       inline std::string strip(const std::string& s,
525                                std::string::size_type num = std::string::npos)
526       { std::string t(s);  strip(t, std::string(1, ' '), num); return t;}
527 
528          /**
529           * Converts all of the receiver's characters that are in the
530           * first specified string to the corresponding character in
531           * the second specified string.
532           * @param aString string to perform translation on.
533           * @param inputChars characters in \a aString to translate from.
534           * @param outputChars characters to translate to.
535           * @param pad pad character in the event inputChars and
536           * outputChars are not equal length.  The pad character will
537           * become the translated character.
538           */
539       inline std::string translate(const std::string& aString,
540                                    const std::string& inputChars,
541                                    const std::string& outputChars,
542                                    const char pad = ' ');
543 
544          /**
545           * Changes occurrences of a specified pattern to a specified
546           * replacement string.  You can specify the number of changes
547           * to perform.  The default is to change all occurrences of
548           * the pattern. You can also specify the position in the
549           * receiver at which to begin.
550           * @param aString string to perform translation on.
551           * @param inputString The pattern string as a reference to an
552           *   object of type string.  The library searches for the
553           *   pattern string within the receiver's data.
554           * @param outputString The replacement string as a reference
555           *   to an object of type string. It replaces the occurrences
556           *   of the pattern string in the receiver's data.
557           * @param startPos The position to start the search at within
558           *   the receiver's data.  The default is 0.
559           * @param numChanges the number of patterns to search for and
560           *   change.  The default is to change all occurrences of the
561           *   pattern.
562           */
563       inline std::string change(const std::string& aString,
564                                 const std::string& inputString,
565                                 const std::string& outputString,
566                                 std::string::size_type startPos = 0,
567                                 unsigned numChanges = (std::numeric_limits<unsigned>::max)());
568 
569          /**
570           * Changes occurrences of a specified pattern to a specified
571           * replacement string.  You can specify the number of changes
572           * to perform.  The default is to change all occurrences of
573           * the pattern. You can also specify the position in the
574           * receiver at which to begin.
575           * @param aString string to perform translation on.
576           * @param inputString The pattern string as a reference to an
577           *   object of type string.  The library searches for the
578           *   pattern string within the receiver's data.
579           * @param outputString The replacement string as a reference
580           *   to an object of type string. It replaces the occurrences
581           *   of the pattern string in the receiver's data.
582           * @param startPos The position to start the search at within
583           *   the receiver's data.  The default is 0.
584           * @param numChanges the number of patterns to search for and
585           *   change.  The default is to change all occurrences of the
586           *   pattern.
587           */
588       inline std::string& change(std::string& aString,
589                                  const std::string& inputString,
590                                  const std::string& outputString,
591                                  std::string::size_type startPos = 0,
592                                  unsigned numChanges = (std::numeric_limits<unsigned>::max)());
593 
594          /**
595           * Right-justifies the receiver in a string of the specified
596           * length. If the receiver's data is shorter than the
597           * requested length (\a length), it is padded on the left with
598           * the pad character (\a pad). The default pad
599           * character is a blank.
600           * @param s string to be modified.
601           * @param length new desired length of string.
602           * @param pad character to pad string with (blank by default).
603           * @throw StringException if there's a std::exception thrown.
604           * @return a reference to \a s.  */
605       inline std::string& rightJustify(std::string& s,
606                                        const std::string::size_type length,
607                                        const char pad = ' ');
608 
609          /**
610           * Right-justifies the receiver in a string of the specified
611           * length (const version). If the receiver's data is shorter than the
612           * requested length (\a length), it is padded on the left with
613           * the pad character (\a pad). The default pad
614           * character is a blank.
615           * @param s string to be modified.
616           * @param length new desired length of string.
617           * @param pad character to pad string with (blank by default).
618           * @throw StringException if there's a std::exception thrown.
619           * @return a reference to \a s.  */
rightJustify(const std::string & s,const std::string::size_type length,const char pad=' ')620       inline std::string rightJustify(const std::string& s,
621                                       const std::string::size_type length,
622                                       const char pad = ' ')
623       { std::string t(s); return rightJustify(t, length, pad); }
624 
625          /**
626           * Left-justifies the receiver in a string of the specified
627           * length. If the new length (\a length) is larger than the
628           * current length, the string is extended by the pad
629           * character (\a pad). The default pad character is a
630           * blank.
631           * @param s string to be modified.
632           * @param length new desired length of string.
633           * @param pad character to pad string with (blank by default).
634           * @throw StringException if there's a std::exception thrown.
635           * @return a reference to \a s.  */
636       inline std::string& leftJustify(std::string& s,
637                                       const std::string::size_type length,
638                                       const char pad = ' ');
639 
640          /**
641           * Left-justifies the receiver in a string of the specified
642           * length (const version). If the new length (\a length) is larger
643           * than the current length, the string is extended by the pad
644           * character (\a pad). The default pad character is a
645           * blank.
646           * @param s string to be modified.
647           * @param length new desired length of string.
648           * @param pad character to pad string with (blank by default).
649           * @throw StringException if there's a std::exception thrown.
650           * @return a reference to \a s.  */
leftJustify(const std::string & s,const std::string::size_type length,const char pad=' ')651       inline std::string leftJustify(const std::string& s,
652                                      const std::string::size_type length,
653                                      const char pad = ' ')
654       { std::string t(s); return leftJustify(t, length, pad); }
655 
656          /**
657           * Change the length of a string by adding to the beginning and end.
658           * The string \a s is modified to the specified
659           * length.  If the string is shorter than
660           * \a length, then the string is truncated with the
661           * left-most \a length characters remaining.
662           * Otherwise, characters are added to the beginning and end of the
663           * string until the string is the specified length, where the
664           * number of characters added to the beginning and the end
665           * does not differ by more than one so the original string
666           * is centered.
667           * @param s string to be modified.
668           * @param length new desired length of string.
669           * @param pad character to pad string with (blank by default).
670           * @throw StringException if there's a std::exception thrown.
671           * @return a reference to \a s.
672           */
673       inline std::string& center(std::string& s,
674                                  const std::string::size_type length,
675                                  const char pad = ' ');
676 
677          /**
678           * Change the length of a string by adding to the beginning and end
679           * (const version).
680           * The string \a s is modified to the specified
681           * length.  If the string is shorter than
682           * \a length, then the string is truncated with the
683           * left-most \a length characters remaining.
684           * Otherwise, characters are added to the beginning and end of the
685           * string until the string is the specified length, where the
686           * number of characters added to the beginning and the end
687           * does not differ by more than one so the original string
688           * is centered.
689           * @param s string to be modified.
690           * @param length new desired length of string.
691           * @param pad character to pad string with (blank by default).
692           * @throw StringException if there's a std::exception thrown.
693           * @return a reference to \a s.
694           */
center(const std::string & s,const std::string::size_type length,const char pad=' ')695       inline std::string center(const std::string& s,
696                                 const std::string::size_type length,
697                                 const char pad = ' ')
698       { std::string t(s); return center(t, length, pad); }
699 
700          /**
701           * Convert a string to a double precision floating point number.
702           * @param s string containing a number.
703           * @return double representation of string.
704           */
asDouble(const std::string & s)705       inline double asDouble(const std::string& s)
706       { return strtod(s.c_str(), 0); }
707 
708          /**
709           * Convert a string to an integer.
710           * @param s string containing a number.
711           * @return long integer representation of string.
712           */
asInt(const std::string & s)713       inline long asInt(const std::string& s)
714       { return strtol(s.c_str(), 0, 10); }
715 
716          /**
717           * Convert a string to an unsigned integer.
718           * @param s string containing a number.
719           * @return unsigned long integer representation of string.
720           */
asUnsigned(const std::string & s)721       inline unsigned long asUnsigned(const std::string& s)
722       { return strtoul(s.c_str(), 0, 10); }
723 
724          /**
725           * Convert a string to a single precision floating point number.
726           * @param s string containing a number.
727           * @return single representation of string.
728           * @throw StringException
729           */
730       inline float asFloat(const std::string& s);
731 
732          /**
733           * Convert a string to a big precision floating point number.
734           * @param s string containing a number.
735           * @return long double representation of string.
736           * @throw StringException
737           */
738       inline long double asLongDouble(const std::string& s);
739 
740          /**
741           * Convert a value in a string to a type specified by the template
742           * class.  The template class type must have stream operators
743           * defined.
744           * @param s object to turn into the templatized type.
745           * @return the template object of \a x.
746           * @throw StringException
747           */
748       template <class X>
749       inline X asData(const std::string& s);
750 
751          /**
752           * Convert a long double to a string in fixed notation.
753           * @param x long double.
754           * @param precision the number of decimal places you want displayed.
755           * @return string representation of \a x.
756           */
757       inline std::string asString(const long double x,
758                                   const std::string::size_type precision = 21);
759 
760          /**
761           * Convert a double to a string in fixed notation.
762           * @param x double.
763           * @param precision the number of decimal places you want displayed.
764           * @return string representation of \a x.
765           */
766       inline std::string asString(const double x,
767                                   const std::string::size_type precision = 17);
768 
769          /**
770           * Convert any old object to a string.
771           * The class must have stream operators defined.
772           * @param x object to turn into a string.
773           * @return string representation of \a x.
774           */
775       template <class X>
776       inline std::string asString(const X x);
777 
778          /**
779           * Convert a decimal string to a hexadecimal string.
780           * Modify the string such that the decimal integer is now
781           * represented as hexadecimal.  Only the first decimal encountered is
782           * changed; the rest of the string is unmodified.
783           * @param s string containing an integer.
784           * @return reference to modified \a s.
785           * @throw StringException
786           */
787       inline std::string& d2x(std::string& s);
788 
789          /**
790           * Convert a decimal string to a hexadecimal string.
791           * Given a string containing a decimal integer, convert the
792           * integer from base 10 to base 16 and return the result.  No
793           * prefix is added.  Only the first decimal encountered is
794           * changed; the rest of the string is unmodified.
795           * @param s string containing an integer.
796           * @return string containing a hexadecimal number.
797           * @throw StringException
798           */
d2x(const std::string & s)799       inline std::string d2x(const std::string& s)
800       { std::string t(s);  return d2x(t); }
801 
802          /**
803           * Convert a hexadecimal string to a decimal string.
804           * Modify the string such that the hexadecimal number is now
805           * represented as decimal.  Only the first hex number encountered
806           * is changed; the rest of the string is unmodified.
807           * @param s string containing an integer.
808           * @return reference to modified \a s.
809           * @throw StringException
810           */
811       inline std::string& x2d(std::string& s);
812 
813          /**
814           * Convert a hexadecimal string to a decimal string.
815           * Given a string containing a hexadecimal number, convert the
816           * integer from base 16 to base 10 and return the result.
817           * Only the first hex number encountered
818           * is changed; the rest of the string is unmodified.
819           * @param s string containing an integer.
820           * @return string containing a hexadecimal number.
821           * @throw StringException
822           */
x2d(const std::string & s)823       inline std::string x2d(const std::string& s)
824       { std::string t(s);  return x2d(t); }
825 
826          /**
827           * Convert a character string to a hexadecimal string.
828           * Modify the string such that the character string is now
829           * represented as series of hexadecimal digits.
830           * @param s string to convert.
831           * @return reference to modified \a s.
832           * @throw StringException
833           */
834       inline std::string& c2x(std::string& s);
835 
836          /**
837           * Convert a character string to a hexadecimal string.
838           * @param s string containing an integer.
839           * @return string containing a sequence of hexadecimal numbers.
840           * @throw StringException
841           */
c2x(const std::string & s)842       inline std::string c2x(const std::string& s)
843       { std::string t(s);  return c2x(t); }
844 
845          /**
846           * Convert a hexadecimal string to an unsigned int.
847           * Only the first hex number encountered is converted.
848           * @param s string containing a hex integer.
849           * @return a long holding the value of \a s.
850           * @throw StringException
851           */
852       inline unsigned int x2uint(const std::string& s);
853 
854          /**
855           * Convert an int to a string.
856           * @param i the integer to convert
857           * @return a string with the hex equivalent of i
858           * @throw StringException
859           */
860       inline std::string int2x(const unsigned int& i);
861 
862          /**
863           * Replace all instances of \a oldString with \a newString in \a s.
864           * @param s the string whose contents will be modified.
865           * @param oldString the string to search for in \a s.
866           * @param newString the string to replace \a oldString in \a s.
867           * @return a reference to the modified string.
868           * @throw StringException
869           */
870       inline std::string& replaceAll(std::string& s,
871                                      const std::string& oldString,
872                                      const std::string& newString );
873 
874          /**
875           * isDigitString is exactly like the C function isDigit
876           * except it checks all the characters of string \a s to see if
877           * they are all digits.
878           * @param s the string to check the digits in.
879           * @return true if \a s is all digits, false otherwise.
880           */
881       inline bool isDigitString(const std::string& s);
882 
883          /**
884           * isDecimalString is like isDigitString() except it allows a
885           * single period ('.') character in the string.
886           * @param s the string to check.
887           * @return true if \a s is a valid fixed-point number.
888           */
889       inline bool isDecimalString(const std::string& s);
890 
891          /**
892           * isScientificString extends isDecimalString() to allow a single
893           * exponent (E,e,D,d) character between a decimal string and
894           * a (possibly empty) digit string.
895           * @param s the string to check.
896           * @return true if \a s is a valid scientific-notation number.
897           */
898       inline bool isScientificString(const std::string& s);
899 
900          /**
901           * isAlphaString is exactly like the C function isAlpha
902           * except it checks all the characters of string \a s to see if
903           * they are all alphabet characters.
904           * @param s the string to check the characters in.
905           * @return true if \a s is all digits, false otherwise.
906           */
907       inline bool isAlphaString(const std::string& s);
908 
909          /**
910           * Perform pattern matching on strings.
911           * Looks for a pattern in a string.  Wildcards are allowed.
912           * Uses POSIX regular expressions.
913           * @param s string to search.
914           * @param aPattern pattern to search for. This is a POSIX
915           * regular expression.
916           * @param zeroOrMore character representing wildcards
917           * matching strings of zero or more characters (default '*').
918           * @param oneOrMore character representing plus sign
919           * matching strings of one or more characters (default '+').
920           * @param anyChar character representing wildcards matching a
921           * single arbitrary character (default '.').
922           * @return string representing the first match of \a aPattern in
923           * \a s.  Returns a null string if no match is found.
924           * @throw StringException
925           */
926       inline std::string matches(const std::string& s,
927                                  const std::string& aPattern,
928                                  const char zeroOrMore = '*',
929                                  const char oneOrMore = '+',
930                                  const char anyChar = '.' );
931 
932          /**
933           * Perform pattern matching on strings.
934           * Looks for a pattern in a string.  Wildcards are allowed.
935           * Uses POSIX regular expressions.
936           * @param s string to search.
937           * @param aPattern pattern to search for. This is a POSIX
938           * regular expression.
939           * @param zeroOrMore character representing wildcards
940           * matching strings of zero or more characters (default '*').
941           * @param oneOrMore character representing plus sign
942           * matching strings of one or more characters (default '+').
943           * @param anyChar character representing wildcards matching a
944           * single arbitrary character (default '.').
945           * @return t if a match is found, f if not.
946           * @throw StringException
947           */
isLike(const std::string & s,const std::string & aPattern,const char zeroOrMore='*',const char oneOrMore='+',const char anyChar='.')948       inline bool isLike(const std::string& s,
949                          const std::string& aPattern,
950                          const char zeroOrMore = '*',
951                          const char oneOrMore = '+',
952                          const char anyChar = '.' )
953       { return matches(s, aPattern, zeroOrMore, oneOrMore, anyChar) !=
954             std::string(); }
955 
956 
957          /**
958           * Perform pattern matching on strings.
959           * Looks for a pattern in a string.  Wildcards are allowed.
960           * Uses POSIX regular expressions.
961           * @param s string to search.
962           * @param pPattern pattern to search for. This is a POSIX
963           * regular expression.
964           * @param zeroOrMore character representing wildcards
965           * matching strings of zero or more characters (default '*').
966           * @param oneOrMore character representing plus sign
967           * matching strings of one or more characters (default '+').
968           * @param anyChar character representing wildcards matching a
969           * single arbitrary character (default '.').
970           * @return t if a match is found, f if not.
971           * @throw StringException
972           */
isLike(const std::string & s,const char * pPattern,const char zeroOrMore='*',const char oneOrMore='+',const char anyChar='.')973       inline bool isLike(const std::string& s,
974                          const char* pPattern,
975                          const char zeroOrMore = '*',
976                          const char oneOrMore = '+',
977                          const char anyChar = '.' )
978       { return matches(s, std::string(pPattern),
979                        zeroOrMore, oneOrMore, anyChar) !=  std::string(); }
980 
981 
982          /**
983           * Work-horse method for printf.  Substitutes patterns
984           * matching \a pat with \a rep.  Use only one pattern/token
985           * at a time!  This used to be DayTime::iprint().
986           * @param fmt format to use for this time.
987           * @param pat regular expression pattern to match.
988           * @param rep sprintf token replacement.  First character is
989           * token character used in fmt, remainder is sprintf token to
990           * use.  For example, with fmt="%15S", pat="%[ 0-]?[[:digit:]]*S",
991           * and rep="Sd", the fmt will be translated to "%15d" before
992           * using it in a sprintf call like printf("%15d"), \a to.
993           * @param to the value to stuff into the string.
994           * @return \a fmt with \a pat replaced by \a to.  If there is no
995           * match, \a fmt is returned unchanged.
996           * @throw StringException
997           */
998       template <class T>
999       std::string formattedPrint(const std::string& fmt,
1000                                  const std::string& pat,
1001                                  const std::string& rep,
1002                                  T to);
1003 
1004          /**
1005           * Get a substring of a string.
1006           * Try to avoid using this, use the stl string's substr
1007           * method instead (and ::leftJustify if needed).
1008           * @throw StringException
1009           */
1010       inline std::string subString(const std::string& s,
1011                                    const std::string::size_type startPos = 0,
1012                                    const std::string::size_type length = std::string::npos,
1013                                    const char pad = ' ' );
1014 
1015          /**
1016           * Change all upper-case letters in a string to lower-case.
1017           * \a s is modified as a result.
1018           * @param s string to change to lower case.
1019           * @return a copy of the original string, all in lower-case.
1020           */
1021       inline std::string& lowerCase(std::string& s);
1022 
1023          /**
1024           * Change all upper-case letters in a string to lower-case.
1025           * Does not modify the original string.
1026           * @param s a string containing upper-case characters.
1027           * @return a copy of the original string, all in lower-case.
1028           */
lowerCase(const std::string & s)1029       inline std::string lowerCase(const std::string& s)
1030       { std::string t(s);  return lowerCase(t); }
1031 
1032          /**
1033           * Change all lower-case letters in a string to upper-case.
1034           * \a s is modified as a result.
1035           * @param s string to change to upper case.
1036           * @return a copy of the original string, all in upper-case.
1037           */
1038       inline std::string& upperCase(std::string& s);
1039 
1040          /**
1041           * Change all lower-case letters in a string to upper-case.
1042           * Does not modify the original string.
1043           * @param s a string containing lower-case characters.
1044           * @return a copy of the original string, all in upper-case.
1045           */
upperCase(const std::string & s)1046       inline std::string upperCase(const std::string& s)
1047       { std::string t(s);  return upperCase(t); }
1048 
1049          /**
1050           * Make a string from a void pointer.
1051           * This function should not be used.  Instead, use the string
1052           * constructor as follows:
1053           * \code string((char*)p, size); \endcode
1054           * @param p pointer to memory.
1055           * @param size length of the data to turn into a string.
1056           * @return string object containing the contents of \a p.
1057           */
1058       inline std::string memToString(const void* p,
1059                                      const std::string::size_type size);
1060 
1061          /**
1062           * Returns the first word in string \a s without modifying the string.
1063           * @param s the string to count the words from.
1064           * @param delimiter the character that marks the start and
1065           * end of a word.
1066           * @return the first word from \a s;
1067           * @throw StringException
1068           */
1069       inline std::string firstWord(const std::string& s,
1070                                    const char delimiter = ' ');
1071 
1072          /**
1073           * Counts the number of words in \a s and returns it.
1074           * @param s the string to count the words from.
1075           * @param delimiter the character that marks the start and
1076           * end of a word.
1077           * @return the number of words in \a s.
1078           * @throw StringException
1079           */
1080       inline int numWords(const std::string& s,
1081                           const char delimiter = ' ');
1082 
1083          /**
1084           * Returns \a numWords words starting with \a firstWord from
1085           * \a s (if any).
1086           * @param s a string with the word you want removed.
1087           * @param firstWord the number of the first word you want from \a s.
1088           * The first word is word 0.
1089           * @param numWords number of words to get from \a s.
1090           * @param delimiter the character that marks the start and
1091           * end of a word.
1092           * @return the first word from \a s or an empty string if there is
1093           * no \a wordNum'th word.
1094           * @throw StringException
1095           */
1096       inline std::string words(const std::string& s,
1097                                const std::string::size_type firstWord = 0,
1098                                const std::string::size_type numWords = std::string::npos,
1099                                const char delimiter = ' ');
1100 
1101          /**
1102           * Returns word number \a wordNum from \a s (if any).
1103           * @param s a string with the word you want removed.
1104           * @param wordNum the number of the word you want from \a s.
1105           * The first word is word 0.
1106           * @param delimiter the character that marks the start and
1107           * end of a word.
1108           * @return the first word from \a s or an empty string if there is
1109           * no \a wordNum'th word.
1110           * @throw StringException
1111           */
word(const std::string & s,const std::string::size_type wordNum=0,const char delimiter=' ')1112       inline std::string word(const std::string& s,
1113                               const std::string::size_type wordNum = 0,
1114                               const char delimiter = ' ')
1115       { return words(s, wordNum, 1, delimiter); }
1116 
1117          /**
1118           * Removes the first word off string \a s and returns it.
1119           * \a s is modified as a result.
1120           * @param s a string with the word you want removed.
1121           * @param delimiter the character that marks the start and
1122           * end of a word.
1123           * @return the first word from \a s
1124           * @throw StringException
1125           */
1126       inline std::string stripFirstWord(std::string& s,
1127                                         const char delimiter = ' ');
1128 
1129          /**
1130           * Split a string \a str into words as defined by \a delimiter.
1131           * @param str string to be parsed.
1132           * @param delimiter character that marks the start and end of a word.
1133           * @return a vector of the words (strings)
1134           * @throw StringException
1135           */
1136       inline std::vector<std::string> split(const std::string& str,
1137                                             const char delimiter = ' ');
1138 
1139          /**
1140           * Remove indicated words from the string \a s.
1141           * \a s is modified as a result.  Removal of a word begins at the
1142           * start of the word and continues to include any delimiters between
1143           * the end of the word and the start of the next word.
1144           * If the last word in \a s is removed, all trailing delimiters
1145           * are removed as well; if all words in \a s are removed, the
1146           * resulting string is empty.
1147           * @param s a string with words to be removed.
1148           * @param first the first word to be removed (the first word is 0).
1149           * @param wordsToReplace the number of words to remove,
1150           *    or std::string::npos to remove all subsequent words
1151           * @param delimiter character that marks the start and end of words
1152           * @return a reference to string \a s with the words removed.
1153           * @throw StringException
1154           */
1155       inline std::string& removeWords(std::string& s,
1156                                       const std::string::size_type first = 0,
1157                                       const std::string::size_type wordsToReplace = std::string::npos,
1158                                       const char delimiter = ' ');
1159 
1160          /**
1161           * Convert a double to a scientific notation number.
1162           * @param d the double to convert
1163           * @param length length (in characters) of output, including exponent
1164           * @param expLen length (in characters) of the exponent, with sign
1165           * @param showSign if true, reserves 1 character for +/- sign
1166           * @param checkSwitch if true, keeps the exponential sanity check for
1167           * exponentials above three characters in length.  If false, it removes
1168           * that check.
1169           * @deprecated This method is being replaced by
1170           * FormattedDouble and is scheduled for removal in the first
1171           * tagged release of Q4 2020.
1172           */
1173       inline std::string doub2sci(const double& d,
1174                                   const std::string::size_type length,
1175                                   const std::string::size_type expLen,
1176                                   const bool showSign = true,
1177                                   const bool checkSwitch = true);
1178 
1179          /** Convert a double to scientific notation; this routine works better,
1180           * on Windows particularly, than doub2sci.
1181           * @param length = total string length,
1182           *                         including 1 for overall sign if showPlus is true.
1183           * @param precision = number of digits after the decimal and before the 'e'
1184           * @param explen = length of exponent, this must = 1, 2 or 3
1185           * NB. length is increased if precision, explen and showPlus require it.
1186           * @deprecated This method is being replaced by
1187           * FormattedDouble and is scheduled for removal in the first
1188           * tagged release of Q4 2020.
1189           */
1190       inline std::string doubleToScientific(const double& d,
1191                                             const std::string::size_type length,
1192                                             const std::string::size_type precision,
1193                                             const std::string::size_type explen,
1194                                             bool showPlus=false);
1195 
1196          /**
1197           * Convert scientific notation to FORTRAN notation.
1198           * As an example, the string "1.5636E5" becomes " .15636D6".
1199           * Note that the first character of the string will be '-' if
1200           * the number is negative or ' ' if the first character is positive.
1201           * @param aStr string with number to convert
1202           * @param startPos start position of number in string
1203           * @param length length (in characters) of number, including exponent.
1204           * @param expLen length (in characters of exponent, not including sign.
1205           * @param checkSwitch will keep the method running as
1206           *   originally programmed when set to true.  If false, the
1207           *   method will always resize exponentials, produce an
1208           *   exponential with an E instead of a D, and always have a
1209           *   leading zero.  For example -> 0.87654E-0004 or
1210           *   -0.1234E00005.
1211           * @throw StringException if the string is not a number in
1212           *   scientific notation
1213           * @deprecated This method is being replaced by
1214           * FormattedDouble and is scheduled for removal in the first
1215           * tagged release of Q4 2020.
1216           */
1217       inline std::string& sci2for(std::string& aStr,
1218                                   const std::string::size_type startPos = 0,
1219                                   const std::string::size_type length = std::string::npos,
1220                                   const std::string::size_type expLen = 3,
1221                                   const bool checkSwitch = true);
1222 
1223          /**
1224           * Convert double precision floating point to a string
1225           * containing the number in FORTRAN notation.
1226           * As an example, the number 156360 becomes ".15636D6".
1227           * @param d number to convert.
1228           * @param length length (in characters) of number, including exponent.
1229           * @param expLen length (in characters of exponent, including sign.
1230           * @param checkSwitch if true, keeps the exponential sanity check for
1231           * exponentials above three characters in length.  If false, it removes
1232           * that check.
1233           * @return a string containing \a d in FORTRAN notation.
1234           * @throw StringException
1235           * @bug The rendered width is not correct if checkSwitch=false
1236           * @deprecated This method is being replaced by
1237           * FormattedDouble and is scheduled for removal in the first
1238           * tagged release of Q4 2020.
1239           */
1240       inline std::string doub2for(const double& d,
1241                                   const std::string::size_type length,
1242                                   const std::string::size_type expLen,
1243                                   const bool checkSwitch = true);
1244 
1245          /**
1246           * Convert FORTRAN representation of a double precision
1247           * floating point in a string to a number.
1248           * As an example, the number ".15636D6" becomes 156360.
1249           * @param aStr string containing FORTRAN representation of number.
1250           * @param startPos beginning of number in string.
1251           * @param length length (in characters) of number, including exponent.
1252           * @return value of the number.
1253           * @deprecated This method is being replaced by
1254           * FormattedDouble and is scheduled for removal in the first
1255           * tagged release of Q4 2020.
1256           */
1257       inline double for2doub(const std::string& aStr,
1258                              const std::string::size_type startPos = 0,
1259                              const std::string::size_type length = std::string::npos);
1260 
1261          /** Format a floating point value according to rules not
1262           * directly supported by C++ stream I/O.
1263           * @see FormattedDouble which should generally be used rather
1264           *   than using this function directly.
1265           * @param[in] d The value that is to be formatted in an ostream.
1266           * @param[in] lead How the lead-in to the value is to be formatted.
1267           * @param[in] mantissa How many digits of precision should be
1268           *   in the mantissa, e.g. mantissa=5 could result in
1269           *   something like 1.2345e+00.
1270           * @param[in] exponent How many digits of precision should be
1271           *   in the exponent, e.g. exponent=3 could result in
1272           *   something like 1.2345e+000.  Exponents will always be at
1273           *   least 2 characters in length.
1274           * @param[in] width The total number of characters in the
1275           *   formatted value.  If the length of the formatted value
1276           *   including mantissa, exponent, sign, etc. is >= width, no
1277           *   additional formatting will take place.  If the length of
1278           *   the formatted value is < width, it will be padded with
1279           *   spaces according to align.
1280           * @param[in] expChar The character used to designate the
1281           *   exponent, e.g. "e" or "E" or "D".
1282           * @param[in] sign How numerical sign is to be handled in formatting.
1283           * @param[in] align How to pad the formatted value according
1284           *   to width.  Left adds space to the end of the formatted
1285           *   value while Right inserts space at the beginning.
1286           */
1287       std::string floatFormat(double d, FFLead lead, unsigned mantissa,
1288                               unsigned exponent, unsigned width = 0,
1289                               char expChar = 'e',
1290                               FFSign sign = FFSign::NegOnly,
1291                               FFAlign align = FFAlign::Left);
1292 
1293          /**
1294           * Change a string into printable characters.  Control
1295           * characters 0, 1, ... 31 are changed to ^@, ^A, ... ^_ ;
1296           * control character 127 is changed to ^? (as per Caret-notation).
1297           * Other non-printable characters are changed to hex sequences
1298           * enclosed in <>.
1299           * @param aStr the string to make printable.
1300           * @throw StringException
1301           */
1302       inline std::string printable(const std::string& aStr);
1303 
1304          /**
1305           * Nicely expands the input string into several lines, non-const
1306           * version.
1307           * @param aStr the string to be modified.
1308           * @param lineDelim a string to put between every line.
1309           * @param indent an indentataion string used on all but the first line
1310           * @param firstIndent is the indentation used on the first line.
1311           * @param len the maximum length of string to put on a line.
1312           * @param wordDelim the character that separates each word.
1313           * @return the string nicely formatted.
1314           * @throw StringException
1315           */
1316       inline std::string& prettyPrint(std::string& aStr,
1317                                       const std::string& lineDelim = "\n",
1318                                       const std::string& indent = "",
1319                                       const std::string& firstIndent = "     ",
1320                                       const std::string::size_type len = 80,
1321                                       const char wordDelim = ' ');
1322 
1323          /**
1324           * Const version of prettyPrint, which nicely expands the
1325           * input string into several lines.
1326           * @param aStr the string to be modified.
1327           * @param lineDelim a string to put between every line.
1328           * @param indent an indentataion string used on all but the first line
1329           * @param firstIndent is the indentation used on the first line.
1330           * @param len the maximum length of string to put on a line.
1331           * @param wordDelim the character that separates each word.
1332           * @return the string nicely formatted.
1333           * @throw StringException
1334           */
prettyPrint(const std::string & aStr,const std::string & lineDelim="\\n",const std::string & indent="",const std::string & firstIndent="     ",const std::string::size_type len=80,const char wordDelim=' ')1335       inline std::string prettyPrint(const std::string& aStr,
1336                                      const std::string& lineDelim = "\n",
1337                                      const std::string& indent = "",
1338                                      const std::string& firstIndent = "     ",
1339                                      const std::string::size_type len = 80,
1340                                      const char wordDelim = ' ')
1341       {
1342          std::string temp(aStr);
1343          prettyPrint(temp, lineDelim, indent, firstIndent, len, wordDelim);
1344          return temp;
1345       }
1346 
1347          /** Split a string by some delimiters
1348           * @param  aStr           the string to be split
1349           * @param  theDelimiters  the delimiters to split the string
1350           * @param  trimWhitespace will trim the token string, default is false
1351           * @param  ignoreEmpty    will ignore the empty tokens, default is true
1352           */
1353       inline std::vector<std::string> split(const std::string& aStr,
1354                                             const std::string& theDelimiters,
1355                                             bool trimWhitespace = false,
1356                                             bool ignoreEmpty = true);
1357 
1358          /// Split a string on the given delimiter, respecting fields enclosed by
1359          /// a pair of either single or double quotes. Quotes are removed in output,
1360          /// and optionally also leading and trailing whitespace.
1361          /// @param  aStr           the string to be split
1362          /// @param  delimiter      character delimiter (not ' or ")
1363          /// @param  trimWhitespace will trim the token string, default is true
1364          /// @param  ignoreEmpty    will ignore the empty tokens, default is true
1365       inline std::vector<std::string> splitWithQuotes(const std::string& aStr,
1366                                                       const char delimiter = ' ',
1367                                                       bool trimWhitespace = true,
1368                                                       bool ignoreEmpty = true);
1369 
1370          /// Split a string on the given delimiter, respecting fields enclosed by
1371          /// a pair of double quotes. Quotes are removed in output, and optionally
1372          /// also leading and trailing whitespace.
1373          /// @param  aStr           the string to be split
1374          /// @param  delimiter      character delimiter (not ")
1375          /// @param  trimWhitespace will trim the token string, default is true
1376          /// @param  ignoreEmpty    will ignore the empty tokens, default is true
1377       inline std::vector<std::string> splitWithDoubleQuotes(const std::string& aStr,
1378                                                             const char delimiter = ' ',
1379                                                             bool trimWhitespace = true,
1380                                                             bool ignoreEmpty = true);
1381    } // namespace StringUtils
1382 
1383 } // namespace gpstk
1384 
1385 // ################################################
1386 //   Implementations of inline functions follow
1387 // ################################################
1388 
1389 namespace gpstk
1390 {
1391 
1392    namespace StringUtils
1393    {
hexDumpData(std::ostream & s,const std::string & data,unsigned indent,const HexDumpDataConfig & cfg)1394       inline void hexDumpData(std::ostream& s, const std::string& data,
1395                               unsigned indent, const HexDumpDataConfig& cfg)
1396       {
1397          std::string instr(indent, ' ');
1398          hexDumpData(s, data, instr, cfg);
1399       }
1400 
hexDumpData(std::ostream & s,const std::string & data,const std::string & tag,HexDumpDataConfig cfg)1401       inline void hexDumpData(std::ostream& s, const std::string& data,
1402                               const std::string& tag,
1403                               HexDumpDataConfig cfg)
1404       {
1405          cfg.prefix = tag;
1406          hexDumpData(data, s, cfg);
1407       }
1408 
1409          // Keep searching for aString at the start of s
1410          // until num == 0 or aString is not found at the start of s
stripLeading(std::string & s,const std::string & aString,std::string::size_type num)1411       inline std::string& stripLeading(std::string& s,
1412                                        const std::string& aString,
1413                                        std::string::size_type num)
1414       {
1415          try
1416          {
1417             if (aString == "")
1418                return s;
1419 
1420             while((num > 0) &&
1421                   (s.find(aString,0) == 0) &&
1422                   (s.length() > 0))
1423             {
1424                s.erase(0,aString.length());
1425                --num;
1426             }
1427             return s;
1428          }
1429          catch(std::exception &e)
1430          {
1431             StringException strexc("Exception thrown: " + std::string(e.what()));
1432             GPSTK_THROW(strexc);
1433          }
1434       }
1435 
1436          // keep searching for aString at the end of s
1437          // until aString isn't found there or num == 0
stripTrailing(std::string & s,const std::string & aString,std::string::size_type num)1438       inline std::string& stripTrailing(std::string& s,
1439                                         const std::string& aString,
1440                                         std::string::size_type num)
1441       {
1442          try
1443          {
1444                // empty string, etc.; cannot let (size_type) pos = negative
1445             if (s.length() < aString.length() || (aString == ""))
1446                return s;
1447 
1448             std::string::size_type pos = s.length() - aString.length();
1449 
1450             while((num > 0) &&
1451                   (s.length() >= aString.length()) &&
1452                   (s.rfind(aString,pos) == pos))
1453             {
1454                s.erase(pos, std::string::npos);
1455                --num;
1456                pos = s.length() - aString.length();
1457             }
1458             return s;
1459          }
1460          catch(std::exception &e)
1461          {
1462             StringException strexc("Exception thrown: " + std::string(e.what()));
1463             GPSTK_THROW(strexc);
1464          }
1465       }
1466 
strip(std::string & s,const std::string & aString,std::string::size_type num)1467       inline std::string& strip(std::string& s,
1468                                 const std::string& aString,
1469                                 std::string::size_type num)
1470       {
1471          stripLeading(s, aString, num);
1472          stripTrailing(s, aString, num);
1473          return s;
1474       }
1475 
translate(const std::string & aString,const std::string & inputChars,const std::string & outputChars,const char pad)1476       inline std::string translate(const std::string& aString,
1477                                    const std::string& inputChars,
1478                                    const std::string& outputChars,
1479                                    const char pad)
1480       {
1481          std::string rv = aString;
1482          std::string::size_type aspos = 0;
1483          std::string::size_type inpos = std::string::npos;
1484          char toc = pad;
1485 
1486             // By starting at the last position, we avoid infinite
1487             // loops in case someone did something dumb, like, for
1488             // example, setting inputChars=outputChars.
1489          while ((aspos = rv.find_first_of(inputChars, aspos))
1490                 != std::string::npos)
1491          {
1492                // figure out which char we found;
1493             inpos = inputChars.find(rv[aspos]);
1494             if (outputChars.length()-1 < inpos)
1495                toc = pad;
1496             else
1497                toc = outputChars[inpos];
1498             rv[aspos] = toc;
1499 
1500             aspos++; // try to guarantee no infinite loops
1501          }
1502 
1503          return rv;
1504       }
1505 
change(const std::string & aString,const std::string & inputString,const std::string & outputString,std::string::size_type startPos,unsigned numChanges)1506       inline std::string change(const std::string& aString,
1507                                 const std::string& inputString,
1508                                 const std::string& outputString,
1509                                 std::string::size_type startPos, unsigned numChanges)
1510       {
1511          std::string rv(aString);
1512          change(rv, inputString, outputString, startPos, numChanges);
1513          return rv;
1514       }
1515 
change(std::string & aString,const std::string & inputString,const std::string & outputString,std::string::size_type startPos,unsigned numChanges)1516       inline std::string& change(std::string& aString, const std::string& inputString,
1517                                  const std::string& outputString,
1518                                  std::string::size_type startPos, unsigned numChanges)
1519       {
1520          if(inputString.empty() || aString.empty()) return aString;
1521          unsigned count = 0;
1522          std::string::size_type opos = startPos;
1523 
1524          while (count < numChanges)
1525          {
1526             std::string::size_type pos = aString.find(inputString, opos);
1527             if (pos != std::string::npos)
1528             {
1529                count++;
1530                aString.replace(pos, inputString.length(), outputString);
1531                opos = pos + outputString.length();
1532             }
1533             else
1534                break;
1535          }
1536 
1537          return aString;
1538       }
1539 
1540          // if the string is bigger than length, truncate it from the left.
1541          // otherwise, add pad characters to it's left.
rightJustify(std::string & s,const std::string::size_type length,const char pad)1542       inline std::string& rightJustify(std::string& s,
1543                                        const std::string::size_type length,
1544                                        const char pad)
1545       {
1546          try
1547          {
1548             if(length < s.length())
1549             {
1550                s = s.substr(s.length()-length, std::string::npos);
1551             }
1552             else
1553             {
1554                s.insert((std::string::size_type)0, length-s.length(), pad);
1555             }
1556             return s;
1557          }
1558          catch(std::exception &e)
1559          {
1560             StringException strexc("Exception thrown: " + std::string(e.what()));
1561             GPSTK_THROW(strexc);
1562          }
1563       }
1564 
1565          // if the string is bigger than length, truncate it from the right.
1566          // otherwise, add pad characters to it's right.
leftJustify(std::string & s,const std::string::size_type length,const char pad)1567       inline std::string& leftJustify(std::string& s,
1568                                       const std::string::size_type length,
1569                                       const char pad)
1570       {
1571          try
1572          {
1573             if(length < s.length())
1574             {
1575                s = s.substr(0, length);
1576             }
1577             else
1578             {
1579                s.append(length-s.length(), pad);
1580             }
1581             return s;
1582          }
1583          catch(std::exception &e)
1584          {
1585             StringException strexc("Exception thrown: " + std::string(e.what()));
1586             GPSTK_THROW(strexc);
1587          }
1588       }
1589 
1590          // leftJustify if s is bigger than length.
1591          // otherwise, add pad to the left and right of s.
center(std::string & s,const std::string::size_type length,const char pad)1592       inline std::string& center(std::string& s,
1593                                  const std::string::size_type length,
1594                                  const char pad)
1595       {
1596          try
1597          {
1598             if(length < s.length())
1599             {
1600                leftJustify(s, length, pad);
1601             }
1602             else {
1603                std::string::size_type leftOff = s.length() + (length - s.length()) / 2;
1604                leftJustify(s, leftOff, pad);
1605                rightJustify(s, length, pad);
1606             }
1607             return s;
1608          }
1609          catch(StringException &e)
1610          {
1611             GPSTK_RETHROW(e);
1612          }
1613          catch(std::exception &e)
1614          {
1615             StringException strexc("Exception thrown: " + std::string(e.what()));
1616             GPSTK_THROW(strexc);
1617          }
1618       }
1619 
1620 
asFloat(const std::string & s)1621       inline float asFloat(const std::string& s)
1622       {
1623          try
1624          {
1625             std::istringstream is(s);
1626             float f;
1627             is >> f;
1628             return f;
1629          }
1630          catch(std::exception &e)
1631          {
1632             StringException strexc("Exception thrown: " + std::string(e.what()));
1633             GPSTK_THROW(strexc);
1634          }
1635       }
1636 
asLongDouble(const std::string & s)1637       inline long double asLongDouble(const std::string& s)
1638       {
1639          try
1640          {
1641             std::istringstream is(s);
1642             long double x;
1643             is >> x;
1644             return x;
1645          }
1646          catch(std::exception &e)
1647          {
1648             StringException strexc("Exception thrown: " + std::string(e.what()));
1649             GPSTK_THROW(strexc);
1650          }
1651       }
1652 
1653       template <class X>
asData(const std::string & s)1654       inline X asData(const std::string& s)
1655       {
1656          try
1657          {
1658             std::istringstream is(s);
1659             X x;
1660             is >> x;
1661             return x;
1662          }
1663          catch(std::exception &e)
1664          {
1665             StringException strexc("Exception thrown: " + std::string(e.what()));
1666             GPSTK_THROW(strexc);
1667          }
1668       }
1669 
asString(const long double x,const std::string::size_type precision)1670       inline std::string asString(const long double x, const std::string::size_type precision)
1671       {
1672          std::ostringstream ss;
1673          ss << std::fixed << std::setprecision(precision) << x ;
1674          return ss.str();
1675       }
1676 
asString(const double x,const std::string::size_type precision)1677       inline std::string asString(const double x, const std::string::size_type precision)
1678       {
1679          std::ostringstream ss;
1680          ss << std::fixed << std::setprecision(precision) << x;
1681          return ss.str();
1682       }
1683 
1684       template<class X>
asString(const X x)1685       inline std::string asString(const X x)
1686       {
1687          std::ostringstream ss;
1688          ss << x;
1689          return ss.str();
1690       }
1691 
1692          // decimal to hex...
d2x(std::string & s)1693       inline std::string& d2x(std::string& s)
1694       {
1695          try
1696          {
1697                // remove the integer from s, including
1698                // leading spaces and 0's
1699             long l = asInt(s);
1700             stripLeading(s);
1701             stripLeading(s, "0");
1702             stripLeading(s, asString<long>(l));
1703 
1704                // put the int in a stringstream to convert it
1705             std::ostringstream st;
1706             st << std::hex << l << std::dec;
1707 
1708                // add the new hex to s
1709             s.insert(0, upperCase(st.str()) );
1710 
1711             return s;
1712          }
1713          catch(StringException &e)
1714          {
1715             GPSTK_RETHROW(e);
1716          }
1717          catch(std::exception &e)
1718          {
1719             StringException strexc("Exception thrown: " + std::string(e.what()));
1720             GPSTK_THROW(strexc);
1721          }
1722       }
1723 
1724          // character to hex...
c2x(std::string & s)1725       inline std::string& c2x(std::string& s)
1726       {
1727          const char hexDigits[] = "0123456789ABCDEF";
1728          try
1729          {
1730             std::string old(s);
1731             const unsigned char *pSource = (unsigned char *)old.c_str();
1732             unsigned n = old.length();
1733 
1734             s.resize(n * 2, 0);
1735 
1736             for (int i = 0; i < (int)n * 2;)
1737             {
1738                unsigned char c = *pSource++;
1739                s[i++] = hexDigits[ c / 16 ];
1740                s[i++] = hexDigits[ c % 16 ];
1741             }
1742 
1743             return s;
1744          }
1745          catch(StringException &e)
1746          {
1747             GPSTK_RETHROW(e);
1748          }
1749          catch(std::exception &e)
1750          {
1751             StringException strexc("Exception thrown: " + std::string(e.what()));
1752             GPSTK_THROW(strexc);
1753          }
1754       }
1755 
1756          /// @todo Need to find a way to combine this with x2d.
1757          // hex to a long.
x2uint(const std::string & s)1758       inline unsigned int x2uint(const std::string& s)
1759       {
1760          try
1761          {
1762                // make the stringstream, get the integer, and
1763                // remove it from the string
1764             std::istringstream iss(s);
1765             unsigned int ui;
1766             iss >> std::hex >> ui;
1767             return ui;
1768          }
1769          catch(StringException &e)
1770          {
1771             GPSTK_RETHROW(e);
1772          }
1773          catch(std::exception &e)
1774          {
1775             StringException strexc("Exception thrown: " + std::string(e.what()));
1776             GPSTK_THROW(strexc);
1777          }
1778       }
1779 
1780          /// @todo detecting 0 isn't quite right...
1781          // hex to decimal
x2d(std::string & s)1782       inline std::string& x2d(std::string& s)
1783       {
1784          try
1785          {
1786                // remove the "0x" part, leading zeros and spaces from the
1787                // string
1788                // ex. ' 0x003' -> '3'
1789             stripLeading(s);
1790             stripLeading(s, "0x", 1);
1791             stripLeading(s, "0");
1792 
1793                // make the stringstream, get the integer, and
1794                // remove it from the string
1795             std::istringstream strstr(s);
1796             int i = 0;
1797             strstr >> std::hex >> i;
1798             stripLeading(s, asString<int>(asInt(s)), 1);
1799 
1800                // append the decimal to the existing string
1801             s.insert(0,asString<int>(i));
1802             return s;
1803          }
1804          catch(StringException &e)
1805          {
1806             GPSTK_RETHROW(e);
1807          }
1808          catch(std::exception &e)
1809          {
1810             StringException strexc("Exception thrown: " + std::string(e.what()));
1811             GPSTK_THROW(strexc);
1812          }
1813       }
1814 
int2x(const unsigned int & i)1815       inline std::string int2x(const unsigned int& i)
1816       {
1817          try
1818          {
1819             std::ostringstream ss;
1820             ss << std::hex << i;
1821             return ss.str();
1822          }
1823          catch(StringException &e)
1824          {
1825             GPSTK_RETHROW(e);
1826          }
1827          catch(std::exception &e)
1828          {
1829             StringException strexc("Exception thrown: " + std::string(e.what()));
1830             GPSTK_THROW(strexc);
1831          }
1832       }
1833 
replaceAll(std::string & s,const std::string & oldString,const std::string & newString)1834       inline std::string& replaceAll(std::string& s,
1835                                      const std::string& oldString,
1836                                      const std::string& newString)
1837       {
1838          try
1839          {
1840             int spot = s.find(oldString, 0);
1841             while (spot != (int)std::string::npos)
1842             {
1843                s.replace(spot, oldString.length(), newString);
1844                spot += newString.length();
1845                spot = s.find(oldString, spot);
1846             }
1847             return s;
1848          }
1849          catch(std::exception &e)
1850          {
1851             StringException strexc("Exception thrown: " + std::string(e.what()));
1852             GPSTK_THROW(strexc);
1853          }
1854       }
1855 
isDigitString(const std::string & s)1856       inline bool isDigitString(const std::string& s)
1857       {
1858          if (s.size() == 0)
1859             return false;
1860 
1861          std::string::size_type index = 0;
1862          if((s[0] == '-') || (s[0] == '+'))
1863             index++;
1864          for( ; index < s.size(); index++)
1865             if (!isdigit(s[index]))
1866                return false;
1867          return true;
1868       }
1869 
isDecimalString(const std::string & s)1870       inline bool isDecimalString(const std::string& s)
1871       {
1872          if (s.size() == 0)
1873             return false;
1874 
1875          std::string::size_type index = 0;
1876          bool sawdot = false;
1877          if((s[0] == '-') || (s[0] == '+'))
1878             index++;
1879          for( ; index < s.size(); index++)
1880          {
1881             if (s[index] == '.')
1882             {
1883                if (sawdot)
1884                   return false;
1885                else sawdot = true;
1886             }
1887             else if (!isdigit(s[index]))
1888                return false;
1889          }
1890          return true;
1891       }
1892 
isScientificString(const std::string & s)1893       inline bool isScientificString(const std::string& s)
1894       {
1895          if(s.size() == 0)
1896             return false;
1897 
1898          std::string::size_type pos = s.find_first_of("EeDd");
1899          if(pos == std::string::npos)
1900             return isDecimalString(s);
1901 
1902          std::string mant=s.substr(0,pos);
1903          std::string exp=s.substr(pos+1);
1904          return (isDecimalString(mant) && (exp.size()==0 || isDigitString(exp)));
1905       }
1906 
isAlphaString(const std::string & s)1907       inline bool isAlphaString(const std::string& s)
1908       {
1909          if (s.size() == 0)
1910             return false;
1911 
1912          std::string::size_type index;
1913          for(index = 0; index < s.size(); index++)
1914             if (!isalpha(s[index]))
1915                return false;
1916          return true;
1917       }
1918 
matches(const std::string & s,const std::string & aPattern,const char zeroOrMore,const char oneOrMore,const char anyChar)1919       inline std::string matches(const std::string& s,
1920                                  const std::string& aPattern,
1921                                  const char zeroOrMore,
1922                                  const char oneOrMore,
1923                                  const char anyChar)
1924       {
1925          std::string thisPattern(aPattern);
1926          std::string thisStr(s);
1927 
1928             // check if something other than the regex standard
1929             // characters (*,+,.) is used for those variables
1930          if (zeroOrMore != '*')
1931          {
1932             replaceAll(thisPattern, "*", "\\*");
1933             replaceAll(thisPattern, std::string(1, zeroOrMore), "*");
1934          }
1935          if (oneOrMore != '+')
1936          {
1937             replaceAll(thisPattern, "+", "\\+");
1938             replaceAll(thisPattern, std::string(1, oneOrMore), "+");
1939          }
1940          if (anyChar != '.')
1941          {
1942             replaceAll(thisPattern, ".", "\\.");
1943             replaceAll(thisPattern, std::string(1, anyChar), ".");
1944          }
1945 
1946 #if defined(_WIN32) && _MSC_VER >= 1700
1947          try
1948          {
1949             std::regex reg (thisPattern);
1950 
1951             std::smatch sm;
1952             if(std::regex_search(thisStr,sm,reg,
1953                                  std::regex_constants::match_not_bol|
1954                                  std::regex_constants::match_not_eol))
1955             {
1956                return sm.str();
1957             }
1958             else
1959             {
1960                return std::string();
1961             }
1962          }
1963          catch(std::regex_error& e)
1964          {
1965             Exception E(std::string("std::regex_error: ") + e.what() );
1966             GPSTK_THROW(E);
1967          }
1968 
1969 #else
1970 
1971          const std::string::size_type regErrorBufSize = 512;
1972 
1973 
1974          regmatch_t matches;
1975          regex_t regExp;
1976          char errorMsg[regErrorBufSize];
1977          int rc = regcomp(&regExp, thisPattern.c_str(), REG_EXTENDED);
1978 
1979          if (rc != 0)
1980          {
1981             regerror(rc, NULL, errorMsg, regErrorBufSize - 1);
1982             regfree(&regExp);
1983             StringException strerr("Regexp error: " + std::string(errorMsg));
1984             GPSTK_THROW(strerr);
1985          }
1986          rc = regexec(&regExp, thisStr.c_str(), 1, &matches,
1987                       REG_NOTBOL | REG_NOTEOL);
1988          if ( (rc != 0) && (rc != REG_NOMATCH) )
1989          {
1990             regerror(rc, &regExp, errorMsg, regErrorBufSize - 1);
1991             regfree(&regExp);
1992             StringException strerr("Regexp error: " + std::string(errorMsg));
1993             GPSTK_THROW(strerr);
1994          }
1995 
1996          regfree(&regExp);
1997          if (rc == REG_NOMATCH)
1998             return std::string();
1999          else
2000             return thisStr.substr(matches.rm_so, matches.rm_eo - matches.rm_so);
2001 #endif
2002       }
2003 
2004       template <class T>
formattedPrint(const std::string & fmt,const std::string & pat,const std::string & rep,T to)2005       inline std::string formattedPrint(const std::string& fmt,
2006                                         const std::string& pat,
2007                                         const std::string& rep,
2008                                         T to)
2009       {
2010 #if defined(_WIN32) && _MSC_VER >= 1700
2011 
2012          std::string rv(fmt);
2013 
2014          try
2015          {
2016             std::regex reg(pat);
2017 
2018             std::smatch m;
2019             while (std::regex_search (rv,m,reg))
2020             {
2021                std::string mac = m.str();
2022                mac = StringUtils::replaceAll(mac, rep.substr(0,1), rep.substr(1));
2023 
2024                char buffer[1024];
2025                sprintf(buffer, mac.c_str(), to);
2026 
2027                rv.replace(m.position(), m.length(), std::string(buffer));
2028             }
2029          }
2030          catch(std::regex_error& e)
2031          {
2032             Exception E(std::string("std::regex_error:")+e.what());
2033             GPSTK_THROW(E);
2034          }
2035 
2036          return rv;
2037 #else
2038          regex_t re;
2039          const size_t bufferSize = 513;
2040          char buffer[bufferSize];
2041          int rc = regcomp(&re, pat.c_str(), REG_EXTENDED);
2042             // if the regex doesnt compile, toast =)
2043          if ( rc != 0)
2044          {
2045             regerror(rc, NULL, buffer, bufferSize - 1);
2046             regfree(&re);
2047             StringException se("Regexp error: " + std::string(buffer));
2048             GPSTK_THROW(se);
2049          }
2050 
2051          regmatch_t r;
2052          std::string rv = fmt;
2053 
2054          while ( regexec(&re, rv.c_str(), 1, &r, 0) == 0 )
2055          {
2056             size_t len = r.rm_eo - r.rm_so;
2057             std::string mac = rv.substr(r.rm_so, len);
2058             mac = replaceAll(mac, rep.substr(0,1), rep.substr(1));
2059             sprintf(buffer, mac.c_str(), to);
2060             rv.replace(r.rm_so, len, std::string(buffer));
2061          }
2062 
2063          regfree(&re);
2064          return rv;
2065 #endif
2066       }
2067 
subString(const std::string & s,const std::string::size_type startPos,const std::string::size_type length,const char pad)2068       inline std::string subString(const std::string& s,
2069                                    const std::string::size_type startPos,
2070                                    const std::string::size_type length,
2071                                    const char pad)
2072       {
2073          try
2074          {
2075             if(startPos >= s.length())
2076             {
2077                return std::string(length, pad);
2078             }
2079             std::string temp = s.substr(startPos, length);
2080             return leftJustify(temp, length, pad);
2081          }
2082          catch(StringException &e)
2083          {
2084             GPSTK_RETHROW(e);
2085          }
2086          catch(std::exception &e)
2087          {
2088             StringException strexc("Exception thrown: " + std::string(e.what()));
2089             GPSTK_THROW(strexc);
2090          }
2091       }
2092 
lowerCase(std::string & s)2093       inline std::string& lowerCase(std::string& s)
2094       {
2095          for(std::string::size_type i = 0; i < s.length(); i++)
2096          {
2097             s[i] = tolower(s[i]);
2098          }
2099          return s;
2100       }
2101 
upperCase(std::string & s)2102       inline std::string& upperCase(std::string& s)
2103       {
2104          for(std::string::size_type i = 0; i < s.length(); i++)
2105          {
2106             s[i] = toupper(s[i]);
2107          }
2108          return s;
2109       }
2110 
memToString(const void * p,const std::string::size_type size)2111       inline std::string memToString(const void* p,
2112                                      const std::string::size_type size)
2113       {
2114          unsigned char* q = (unsigned char*)p;
2115          std::string s(size,'\0');
2116          for (int i=0; i<(int)size; i++)
2117          {
2118             s[i] = (unsigned char)(*q++);
2119          }
2120          return s;
2121       }
2122 
firstWord(const std::string & s,const char delimiter)2123       inline std::string firstWord(const std::string& s,
2124                                    const char delimiter)
2125       {
2126          try
2127          {
2128                // return s if there are no delimiters
2129             std::string::size_type pos = s.find_first_not_of(delimiter);
2130             if (pos == std::string::npos)
2131             {
2132                return s;
2133             }
2134                // find the end delimiter (if any) and return the string
2135             std::string::size_type endPos = s.find(delimiter, pos);
2136             if (endPos == std::string::npos)
2137             {
2138                return std::string(s, pos, endPos);
2139             }
2140             else
2141             {
2142                return std::string(s, pos, endPos - pos);
2143             }
2144          }
2145          catch(StringException &e)
2146          {
2147             GPSTK_RETHROW(e);
2148          }
2149          catch(std::exception &e)
2150          {
2151             StringException strexc("Exception thrown: " + std::string(e.what()));
2152             GPSTK_THROW(strexc);
2153          }
2154       }
2155 
numWords(const std::string & s,const char delimiter)2156       inline int numWords(const std::string& s,
2157                           const char delimiter)
2158       {
2159          try
2160          {
2161             std::string t(s);
2162             stripTrailing(t, delimiter);
2163 
2164             int words = 0;
2165             while(t.length())
2166             {
2167                stripLeading(t, delimiter);
2168                stripLeading(t, firstWord(t, delimiter));
2169                words++;
2170             }
2171             return words;
2172          }
2173          catch(StringException &e)
2174          {
2175             GPSTK_RETHROW(e);
2176          }
2177          catch(std::exception &e)
2178          {
2179             StringException strexc("Exception thrown: " + std::string(e.what()));
2180             GPSTK_THROW(strexc);
2181          }
2182       }
2183 
words(const std::string & s,const std::string::size_type firstWord,const std::string::size_type numWords,const char delimiter)2184       inline std::string words(const std::string& s,
2185                                const std::string::size_type firstWord,
2186                                const std::string::size_type numWords,
2187                                const char delimiter)
2188       {
2189          try
2190          {
2191             if ((firstWord == 0) && (numWords == 1))
2192                return StringUtils::firstWord(s, delimiter);
2193 
2194             if (numWords == 0)
2195                return "";
2196 
2197             std::string::size_type wordNum = 0;
2198             std::string::size_type pos = 0, startPos = 0;
2199 
2200             std::string toReturn;
2201 
2202                // get position of word wordNum
2203             pos = s.find_first_not_of(delimiter, pos);
2204             while ((pos != std::string::npos) && (pos <= s.length()))
2205             {
2206                if (wordNum == firstWord)
2207                   startPos = pos;
2208 
2209                   // get first delimter after word wordNum
2210                pos = s.find(delimiter, pos);
2211                if (((int)numWords != -1)
2212                    && ((int)wordNum == (int)(firstWord + (numWords-1))))
2213                   break;
2214 
2215                pos = s.find_first_not_of(delimiter, pos);
2216                wordNum++;
2217             }
2218 
2219             if (pos == std::string::npos)
2220                return ((wordNum >= firstWord) ? s.substr(startPos) : "");
2221 
2222             return s.substr(startPos, pos-startPos);
2223          }
2224          catch(StringException &e)
2225          {
2226             GPSTK_RETHROW(e);
2227          }
2228          catch(std::exception &e)
2229          {
2230             StringException strexc("Exception thrown: " + std::string(e.what()));
2231             GPSTK_THROW(strexc);
2232          }
2233       }
2234 
stripFirstWord(std::string & s,const char delimiter)2235       inline std::string stripFirstWord(std::string& s,
2236                                         const char delimiter)
2237       {
2238          try
2239          {
2240             stripLeading(s, delimiter);
2241             std::string toReturn = firstWord(s, delimiter);
2242             stripLeading(s, toReturn);
2243             stripLeading(s, delimiter);
2244             return toReturn;
2245          }
2246          catch(StringException &e)
2247          {
2248             GPSTK_RETHROW(e);
2249          }
2250          catch(std::exception &e)
2251          {
2252             StringException strexc("Exception thrown: " + std::string(e.what()));
2253             GPSTK_THROW(strexc);
2254          }
2255       }
2256 
split(const std::string & str,const char delimiter)2257       inline std::vector<std::string> split(const std::string& str,
2258                                             const char delimiter)
2259       {
2260          try {
2261             std::vector<std::string> rvec;   // vector to return
2262             std::string tempStr(str);        // copy the input string
2263             stripLeading(tempStr,delimiter); // remove leading delimiters
2264             while(tempStr.size() > 0)
2265                rvec.push_back(stripFirstWord(tempStr,delimiter));
2266             return rvec;
2267          }
2268          catch(StringException &e)
2269          {
2270             GPSTK_RETHROW(e);
2271          }
2272          catch(std::exception &e)
2273          {
2274             StringException strexc("Exception thrown: " + std::string(e.what()));
2275             GPSTK_THROW(strexc);
2276          }
2277       }
2278 
split(const std::string & aStr,const std::string & theDelimiters,bool trimWhitespace,bool ignoreEmpty)2279       inline std::vector<std::string> split(const std::string& aStr,
2280                                             const std::string& theDelimiters,
2281                                             bool trimWhitespace,
2282                                             bool ignoreEmpty)
2283       {
2284          std::vector<std::string> toReturn;
2285 
2286          std::string::size_type lastPos = aStr.find_first_not_of(theDelimiters, 0);
2287          std::string::size_type pos     = aStr.find_first_of(theDelimiters, lastPos);
2288 
2289          while (std::string::npos != pos || std::string::npos != lastPos)
2290          {
2291             std::string token = aStr.substr(lastPos, pos - lastPos);
2292 
2293             if(trimWhitespace) token = StringUtils::strip(token);
2294 
2295             if(!token.empty() || !ignoreEmpty) toReturn.push_back(token);
2296 
2297             lastPos = aStr.find_first_not_of(theDelimiters, pos);
2298             pos = aStr.find_first_of(theDelimiters, lastPos);
2299          }
2300 
2301          return toReturn;
2302       }
2303 
splitWithQuotes(const std::string & aStr,const char delimiter,bool trimWhitespace,bool ignoreEmpty)2304       inline std::vector<std::string> splitWithQuotes(const std::string& aStr,
2305                                                       const char delimiter,
2306                                                       bool trimWhitespace,
2307                                                       bool ignoreEmpty)
2308       {
2309          std::vector<std::string> toReturn;
2310          std::string::size_type begPos = 0;
2311          std::string::size_type endPos = 0;
2312          std::string::size_type tokenLength;
2313          char currentDelimiter;
2314 
2315          if(delimiter == '\'' || delimiter == '"')
2316             GPSTK_THROW(StringException("Delimiter must not be quote"));
2317 
2318          while(endPos != std::string::npos && endPos <= aStr.length())
2319          {
2320             currentDelimiter = delimiter;
2321 
2322                // if first character is a quote, set current delimiter to it
2323             if(aStr.compare(begPos,1,"\"") == 0) currentDelimiter = '"';
2324             if(aStr.compare(begPos,1,"\'") == 0) currentDelimiter = '\'';
2325 
2326                // find next delimiter
2327             endPos = aStr.find_first_of(currentDelimiter,
2328                                         begPos + (currentDelimiter == delimiter ? 0 : 1));
2329 
2330                // if this token is quoted, make sure to capture the trailing quote
2331                // if(delimiter is a quote and second quote is found) include it
2332             if(currentDelimiter != delimiter && std::string::npos != endPos) endPos++;
2333 
2334                // length of new field
2335             tokenLength = endPos - begPos;
2336                // copy out the field
2337             std::string token = aStr.substr(begPos, tokenLength);
2338 
2339                // if quoted, remove the quotes
2340             if(currentDelimiter != delimiter)
2341                token = gpstk::StringUtils::strip(token,currentDelimiter);
2342 
2343                // remove whitespace at beginning and end
2344             if(trimWhitespace) token = gpstk::StringUtils::strip(token);
2345 
2346                // save it
2347             if(!token.empty() || !ignoreEmpty) toReturn.push_back(token);
2348 
2349                // find the next token, and go back to delimiter
2350             begPos = endPos;
2351             if(begPos != std::string::npos) begPos++;
2352             endPos = begPos;
2353          }
2354 
2355          return toReturn;
2356       }
2357 
splitWithDoubleQuotes(const std::string & aStr,const char delimiter,bool trimWhitespace,bool ignoreEmpty)2358       inline std::vector<std::string> splitWithDoubleQuotes(const std::string& aStr,
2359                                                             const char delimiter,
2360                                                             bool trimWhitespace,
2361                                                             bool ignoreEmpty)
2362       {
2363          std::vector<std::string> toReturn;
2364          std::string::size_type begPos = 0;
2365          std::string::size_type endPos = 0;
2366          std::string::size_type tokenLength;
2367          char currentDelimiter;
2368 
2369          if(delimiter == '"')
2370             GPSTK_THROW(StringException("Delimiter must not be quote"));
2371 
2372          while(endPos != std::string::npos && endPos <= aStr.length())
2373          {
2374             currentDelimiter = delimiter;
2375 
2376                // if first character is a quote, set current delimiter to it
2377             if(aStr.compare(begPos,1,"\"") == 0) currentDelimiter = '"';
2378 
2379                // find next delimiter
2380             endPos = aStr.find_first_of(currentDelimiter,
2381                                         begPos + (currentDelimiter == delimiter ? 0 : 1));
2382 
2383                // if this token is quoted, make sure to capture the trailing quote
2384                // if(delimiter is a quote and second quote is found) include it
2385             if(currentDelimiter != delimiter && std::string::npos != endPos) endPos++;
2386 
2387                // length of new field
2388             tokenLength = endPos - begPos;
2389                // copy out the field
2390             std::string token = aStr.substr(begPos, tokenLength);
2391 
2392                // if quoted, remove the quotes
2393             if(currentDelimiter != delimiter)
2394                token = gpstk::StringUtils::strip(token,currentDelimiter);
2395 
2396                // remove whitespace at beginning and end
2397             if(trimWhitespace) token = gpstk::StringUtils::strip(token);
2398 
2399                // save it
2400             if(!token.empty() || !ignoreEmpty) toReturn.push_back(token);
2401 
2402                // find the next token, and go back to delimiter
2403             begPos = endPos;
2404             if(begPos != std::string::npos) begPos++;
2405             endPos = begPos;
2406          }
2407 
2408          return toReturn;
2409       }
2410 
removeWords(std::string & s,const std::string::size_type first,const std::string::size_type wordsToReplace,const char delimiter)2411       inline std::string& removeWords(std::string& s,
2412                                       const std::string::size_type first,
2413                                       const std::string::size_type wordsToReplace,
2414                                       const char delimiter)
2415       {
2416          try
2417          {
2418             std::string::size_type rmStart = std::string::npos;
2419             std::string::size_type rmCount = std::string::npos;
2420 
2421                // Find start of word 0
2422             std::string::size_type sPos = s.find_first_not_of(delimiter);
2423 
2424             for (std::string::size_type thisWord = 0;
2425                  sPos != std::string::npos;
2426                  thisWord++)
2427             {
2428                   // Check for start and end of range to remove
2429                if (thisWord == first)
2430                {
2431                   rmStart = sPos;
2432                   if (wordsToReplace == std::string::npos)
2433                   {
2434                      break;
2435                   }
2436                }
2437                else if (  (wordsToReplace != std::string::npos)
2438                        && (thisWord >= first + wordsToReplace))
2439                {
2440                   rmCount = sPos - rmStart;
2441                   break;
2442                }
2443                   // Find the end of the current word
2444                sPos = s.find(delimiter, sPos);
2445                if (sPos != std::string::npos)
2446                {
2447                      // Find start of next word
2448                   sPos = s.find_first_not_of(delimiter, sPos);
2449                }
2450             }
2451                // Remove the words if they were present
2452             if (rmStart != std::string::npos)
2453             {
2454                s.erase(rmStart, rmCount);
2455                if (rmCount == std::string::npos)
2456                {
2457                   stripTrailing(s, delimiter);
2458                }
2459             }
2460             return s;
2461          }
2462          catch(StringException &e)
2463          {
2464             GPSTK_RETHROW(e);
2465          }
2466          catch(std::exception &e)
2467          {
2468             StringException strexc("Exception thrown: " + std::string(e.what()));
2469             GPSTK_THROW(strexc);
2470          }
2471       }
2472 
doub2sci(const double & d,const std::string::size_type length,const std::string::size_type expLen,const bool showSign,const bool checkSwitch)2473       inline std::string doub2sci(const double& d,
2474                                   const std::string::size_type length,
2475                                   const std::string::size_type expLen,
2476                                   const bool showSign,
2477                                   const bool checkSwitch)
2478       {
2479          std::string toReturn;
2480          short exponentLength = expLen;
2481 
2482             /* Validate the assumptions regarding the input arguments */
2483          if (exponentLength < 0) exponentLength = 1;
2484          if (exponentLength > 3 && checkSwitch) exponentLength = 3;
2485 
2486          std::stringstream c;
2487          c.setf(std::ios::scientific, std::ios::floatfield);
2488 
2489             // length - 3 for special characters ('.', 'e', '+' or '-')
2490             // - exponentlength (e04)
2491             // - 1 for the digit before the decimal (2.)
2492             // and if showSign == true,
2493             //    an extra -1 for '-' or ' ' if it's positive or negative
2494          int expSize = 0;
2495          if (showSign)
2496             expSize = 1;
2497          c.precision(length - 3 - exponentLength - 1 - expSize);
2498 
2499 
2500          c << d;
2501 
2502          c >> toReturn;
2503 
2504          return toReturn;
2505       }
2506 
doubleToScientific(const double & d,const std::string::size_type length,const std::string::size_type precision,const std::string::size_type explen,bool showPlus)2507       inline std::string doubleToScientific(const double& d,
2508                                             const std::string::size_type length,
2509                                             const std::string::size_type precision,
2510                                             const std::string::size_type explen,
2511                                             bool showPlus)
2512       {
2513             // get final exp length, precision and total length
2514          std::string::size_type elen = (explen > 0 ? (explen < 3 ? explen : 3) : 1);
2515          std::string::size_type prec = (precision > 0 ? precision : 1);
2516          std::string::size_type leng = (length > 0 ? length : 1);
2517 
2518             // i will be minimum length required with prec==1: force leng if necessary
2519          size_t i = (int(leng) - int(elen) - 4);
2520          if(showPlus) i--;
2521          if(i > 0 && leng < i) leng = std::string::size_type(i);
2522 
2523             // set up the stream for writing
2524          std::stringstream ss;
2525          ss << std::scientific << std::setprecision(prec);
2526          if(showPlus) ss << std::showpos;
2527 
2528             // write d to a string with precision, sign and in scientific notation
2529          ss << d;
2530 
2531             // now read that string
2532          std::string str1,str2;
2533          ss >> str1;
2534          std::string::size_type pos = str1.find_first_of("EDed");    // find exponent
2535          str2 = str1.substr(0,pos+2);        // str2 = +123.2345e+
2536          str1 = str1.substr(pos+2);          // str1 = exponent only
2537 
2538             // make the exponent length elen
2539          str2 += StringUtils::rightJustify(
2540             StringUtils::asString(StringUtils::asInt(str1)),elen,'0');
2541 
2542             // pad if necessary
2543          if(str2.length() < leng) str2 = StringUtils::rightJustify(str2,leng);
2544 
2545          return str2;
2546       }
2547 
sci2for(std::string & aStr,const std::string::size_type startPos,const std::string::size_type length,const std::string::size_type expLen,const bool checkSwitch)2548       inline std::string& sci2for(std::string& aStr,
2549                                   const std::string::size_type startPos,
2550                                   const std::string::size_type length,
2551                                   const std::string::size_type expLen,
2552                                   const bool checkSwitch)
2553       {
2554          try
2555          {
2556             std::string::size_type idx = aStr.find('.', startPos);
2557             int expAdd = 0;
2558             std::string exp;
2559             long iexp;
2560                //If checkSwitch is false, always redo the exponential. Otherwise,
2561                //set it to false.
2562             bool redoexp=!checkSwitch;
2563 
2564                // Check for decimal place within specified boundaries
2565             if ((idx == 0) || (idx >= (startPos + length - expLen - 1)))
2566             {
2567                StringException e("sci2for: no decimal point in string");
2568                GPSTK_THROW(e);
2569             }
2570 
2571                // Here, account for the possibility that there are
2572                // no numbers to the left of the decimal, but do not
2573                // account for the possibility of non-scientific
2574                // notation (more than one digit to the left of the
2575                // decimal)
2576             if (idx > startPos)
2577             {
2578                redoexp = true;
2579                   // Swap digit and decimal.
2580                aStr[idx] = aStr[idx-1];
2581                aStr[idx-1] = '.';
2582                   // Only add one to the exponent if the number is non-zero
2583                if (asDouble(aStr.substr(startPos, length)) != 0.0)
2584                   expAdd = 1;
2585             }
2586 
2587             idx = aStr.find('e', startPos);
2588             if (idx == std::string::npos)
2589             {
2590                idx = aStr.find('E', startPos);
2591                if (idx == std::string::npos)
2592                {
2593                   StringException e("sci2for:no 'e' or 'E' in string");
2594                   GPSTK_THROW(e);
2595                }
2596             }
2597                // Change the exponent character to D normally, or E of checkSwitch is false.
2598             if (checkSwitch)
2599                aStr[idx] = 'D';
2600             else
2601                aStr[idx] = 'E';
2602 
2603                // Change the exponent itself
2604             if (redoexp)
2605             {
2606                exp = aStr.substr(idx + 1, std::string::npos);
2607                iexp = asInt(exp);
2608                iexp += expAdd;
2609 
2610                aStr.erase(idx + 1);
2611                if (iexp < 0)
2612                {
2613                   aStr += "-";
2614                   iexp -= iexp*2;
2615                }
2616                else
2617                   aStr += "+";
2618                aStr += rightJustify(asString(iexp),expLen,'0');
2619 
2620             }
2621 
2622                // if the number is positive, append a space
2623                // (if it's negative, there's a leading '-'
2624             if (aStr[0] == '.')
2625             {
2626                aStr.insert((std::string::size_type)0, 1, ' ');
2627             }
2628 
2629                //If checkSwitch is false, add on one leading zero to the string
2630             if (!checkSwitch)
2631             {
2632                aStr.insert((std::string::size_type)1, 1, '0');
2633             }
2634 
2635 
2636             return aStr;
2637          }
2638          catch(StringException &e)
2639          {
2640             GPSTK_RETHROW(e);
2641          }
2642          catch(std::exception &e)
2643          {
2644             StringException strexc("Exception thrown: " + std::string(e.what()));
2645             GPSTK_THROW(strexc);
2646          }
2647       }  // end sci2for
2648 
2649 
doub2for(const double & d,const std::string::size_type length,const std::string::size_type expLen,const bool checkSwitch)2650       inline std::string doub2for(const double& d,
2651                                   const std::string::size_type length,
2652                                   const std::string::size_type expLen,
2653                                   const bool checkSwitch)
2654       {
2655          try
2656          {
2657             short exponentLength = expLen;
2658 
2659                /* Validate the assumptions regarding the input arguments */
2660             if (exponentLength < 0) exponentLength = 1;
2661             if (exponentLength > 3 && checkSwitch) exponentLength = 3;
2662 
2663             std::string toReturn = doub2sci(d, length, exponentLength, true, checkSwitch);
2664             sci2for(toReturn, 0, length, exponentLength, checkSwitch);
2665 
2666             return toReturn;
2667          }
2668          catch(StringException &e)
2669          {
2670             GPSTK_RETHROW(e);
2671          }
2672          catch(std::exception &e)
2673          {
2674             StringException strexc("Exception thrown: " + std::string(e.what()));
2675             GPSTK_THROW(strexc);
2676          }
2677       }
2678 
2679 
for2doub(const std::string & aStr,const std::string::size_type startPos,const std::string::size_type length)2680       inline double for2doub(const std::string& aStr,
2681                              const std::string::size_type startPos,
2682                              const std::string::size_type length)
2683       {
2684          std::string s(aStr, startPos, length);
2685          strip(s);
2686 
2687             // you can blame Rinex for these special checks
2688          if (s.empty())
2689          {
2690             return 0;
2691          }
2692 
2693          std::string::size_type pos = s.find_first_of("EDd");
2694          if (pos != std::string::npos)
2695          {
2696             s[pos] = 'e';
2697          }
2698          else
2699          {
2700                // just treat it like a double
2701             return asDouble(aStr.substr(startPos, length));
2702          }
2703 
2704          std::stringstream st;
2705          st << s;
2706 
2707          double d;
2708          st >> d;
2709 
2710          return d;
2711       }
2712 
printable(const std::string & aStr)2713       inline std::string printable(const std::string& aStr)
2714       {
2715          try
2716          {
2717             std::string rv;
2718             size_t len = aStr.length();
2719             rv.reserve(len);
2720 
2721             for (int i = 0; i < len; i++)
2722             {
2723                char c = aStr[i];
2724                if (c > 31 && c < 127)  // Handle printable ASCII characters
2725                {
2726                   rv.append(1,c);
2727                }
2728                else
2729                {
2730                   if (c < 0 || c > 127)  // Handle non-ASCII characters
2731                   {
2732                      rv.append("<" + c2x(aStr.substr(i,1)) + ">");
2733                   }
2734                   else  // Handle control characters
2735                   {
2736                      char ctrl = (int)c ^ 0x40;  // Flip the 7th bit
2737                      rv.append(1,'^');
2738                      rv.append(1,ctrl);
2739                   }
2740                }
2741             }
2742 
2743             return rv;
2744          }
2745          catch(StringException &e)
2746          {
2747             GPSTK_RETHROW(e);
2748          }
2749          catch(std::exception &e)
2750          {
2751             StringException strexc("Exception thrown: " + std::string(e.what()));
2752             GPSTK_THROW(strexc);
2753          }
2754       }
2755 
prettyPrint(std::string & aStr,const std::string & lineDelim,const std::string & indent,const std::string & firstIndent,const std::string::size_type len,const char wordDelim)2756       inline std::string& prettyPrint(std::string& aStr,
2757                                       const std::string& lineDelim,
2758                                       const std::string& indent,
2759                                       const std::string& firstIndent,
2760                                       const std::string::size_type len,
2761                                       const char wordDelim)
2762       {
2763          try
2764          {
2765             std::string newStr(firstIndent);
2766                // positions of word and line delimiters in aStr
2767             std::string::size_type wordPos = 0, linePos = 0, curPos = 0,
2768                curLineLen = newStr.length(), minPos = 0, wordLen = 0;
2769             bool wordDelimited = false;
2770             while (curPos != std::string::npos)
2771             {
2772                wordPos = aStr.find(wordDelim, curPos);
2773                if (wordPos == curPos)
2774                {
2775                      // ignore the word delimeter
2776                   curPos++;
2777                   continue;
2778                }
2779                   // no longer processing a word delimiter
2780                wordDelimited = false;
2781                linePos = aStr.find(lineDelim, curPos);
2782                if (linePos == curPos)
2783                {
2784                   curPos += lineDelim.length();
2785                      // line delimiters are NOT compressed.
2786                   newStr += lineDelim + indent;
2787                   curLineLen = indent.length();
2788                   continue;
2789                }
2790                   // add a word
2791                minPos = std::min(wordPos, linePos);
2792                if (minPos != std::string::npos)
2793                   wordLen = minPos - curPos;
2794                else
2795                   wordLen = aStr.length() - curPos;
2796                if ((curLineLen + wordLen + 1) > len)
2797                {
2798                   newStr += lineDelim + indent;
2799                   curLineLen = indent.length();
2800                }
2801                newStr += wordDelim;
2802                newStr += aStr.substr(curPos, wordLen);
2803                curLineLen += wordLen + 1;
2804                curPos = minPos;
2805             }
2806             aStr = newStr + lineDelim;
2807             return aStr;
2808          }
2809          catch (std::exception &e)
2810          {
2811             StringException strexc("Exception thrown: " +
2812                                    std::string(e.what()));
2813             GPSTK_THROW(strexc);
2814          }
2815       }
2816 
2817          //@}
2818 
2819    } // namespace StringUtils
2820 
2821 } // namespace gpstk
2822 #endif // GPSTK_STRINGUTILS_HPP
2823