1 /*=========================================================================
2  *
3  *  Copyright Insight Software Consortium
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *         http://www.apache.org/licenses/LICENSE-2.0.txt
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  *=========================================================================*/
18 
19 #include "itkStringTools.h"
20 
21 #include <algorithm> // std::transform()
22 #include <cctype> // std::toupper(), std::tolower()
23 
24 namespace itk
25 {
26 
27 /////////////////////////////////////////////////////////////////////////////
28 // helper functions for string manipulations
29 /////////////////////////////////////////////////////////////////////////////
30 
31 /** Method to trim the spaces or user-specified characters on both ends of a string. */
32 std::string&
Trim(std::string & str,const std::string & dislike)33 StringTools::Trim( std::string& str, const std::string& dislike )
34 {
35   return StringTools::TrimRight(StringTools::TrimLeft(str, dislike), dislike);
36 }
37 
38 /** Method to trim the spaces or user-specified characters on left end of a string. */
39 std::string&
TrimLeft(std::string & str,const std::string & dislike)40 StringTools::TrimLeft( std::string& str, const std::string& dislike )
41 {
42   if ( !str.empty() )
43     {
44     std::string::size_type pos = str.find_first_not_of( dislike );
45     if ( pos != std::string::npos )
46       {
47       if ( pos > 0 )
48         {
49         str.erase( 0, pos );
50         }
51       }
52     else
53       {
54       str.clear();
55       }
56     }
57 
58   return str;
59 }
60 
61 /** Method to trim the spaces or user-specified characters on right end of a string. */
62 std::string&
TrimRight(std::string & str,const std::string & dislike)63 StringTools::TrimRight( std::string& str, const std::string& dislike )
64 {
65   if ( !str.empty() )
66     {
67     std::string::size_type pos = str.find_last_not_of( dislike );
68     if ( pos != std::string::npos )
69       {
70       if ( (++pos) < str.size() )
71         {
72         str.erase( pos );
73         }
74       }
75     else
76       {
77       str.clear();
78       }
79     }
80 
81   return str;
82 }
83 
84 /** Method to covert lower-case characters to upper cases in a string. */
85 std::string&
ToUpperCase(std::string & str)86 StringTools::ToUpperCase( std::string& str )
87 {
88   // explicit cast needed to resolve ambiguity
89   std::transform( str.begin(), str.end(), str.begin(), (int(*)(int))std::toupper );
90   return str;
91 }
92 
93 /** Method to covert upper-case characters to lower cases in a string. */
94 std::string&
ToLowerCase(std::string & str)95 StringTools::ToLowerCase( std::string& str )
96 {
97   // explicit cast needed to resolve ambiguity
98   std::transform( str.begin(), str.end(), str.begin(), (int(*)(int))std::tolower );
99   return str;
100 }
101 
102 /** Method to split a string into two parts with user-defined delimiters. */
103 void
Split(const std::string & s,std::string & lpart,std::string & rpart,const std::string & delims)104 StringTools::Split( const std::string& s, std::string& lpart, std::string& rpart, const std::string& delims )
105 {
106   std::string::size_type pos = s.find_first_of( delims );
107   if ( pos != std::string::npos )
108     {
109     lpart = s.substr( 0, pos );
110     StringTools::Trim( lpart );
111     rpart = s.substr( pos + 1 );
112     StringTools::Trim( rpart );
113     }
114   else
115     {
116     lpart = s;
117     StringTools::Trim( lpart );
118     rpart = "";
119     }
120 }
121 
122 /** Method to split a string into a sequence of strings with user-defined delimiters. */
123 void
Split(const std::string & s,std::vector<std::string> & result,const std::string & delims)124 StringTools::Split( const std::string& s, std::vector<std::string>& result, const std::string& delims )
125 {
126   std::string str = s;
127   while ( !str.empty() )
128     {
129     std::string::size_type pos = str.find_first_of( delims );
130     if ( pos != std::string::npos )
131       {
132       std::string front = str.substr( 0, pos );
133       StringTools::Trim( front );
134       result.push_back( front );
135       str = str.substr( pos+1 );
136       }
137     else
138       {
139       StringTools::Trim( str );
140       result.push_back( str );
141       str = ""; // no more text to split
142       }
143     }
144 }
145 
146 /**
147  * Method to split a string into a sequence of sub-strings with user-defined delimiters,
148  * then each sub-string is further splitted into a <key,value> pair with separators "=:".
149  */
150 void
Split(const std::string & s,std::map<std::string,std::string> & result,const std::string & delims)151 StringTools::Split( const std::string& s, std::map<std::string,std::string>& result, const std::string& delims )
152 {
153   std::vector<std::string> items;
154   Split( s, items, delims );
155 
156   for (const auto & item : items)
157     {
158     std::string value;
159     std::string key;
160     StringTools::Split( item, key, value );
161     result[key] = value;
162     }
163 }
164 
165 /** Method to test whether one string matches with another. */
166 bool
MatchWith(const std::string & s1,const std::string & s2,bool ignoreCase)167 StringTools::MatchWith( const std::string& s1, const std::string& s2, bool ignoreCase )
168 {
169   // compare ignoring case
170   if ( ignoreCase )
171     {
172     // s1 to lower case
173     std::string ls1 = s1;
174     ToLowerCase( ls1 );
175     // s2 to lower case
176     std::string ls2 = s2;
177     ToLowerCase( ls2 );
178     // compare the two
179     if ( ls1 == ls2 )
180       {
181       return true;
182       }
183     else
184       {
185       return false;
186       }
187     }
188 
189   // compare considering case
190   if ( s1 == s2 )
191     {
192     return true;
193     }
194   else
195     {
196     return false;
197     }
198 }
199 
200 /** Method to test whether a string starts with a user-given sub-string. */
201 bool
StartWith(const std::string & s1,const std::string & s2,bool ignoreCase)202 StringTools::StartWith( const std::string& s1, const std::string& s2, bool ignoreCase )
203 {
204   // if case is not important
205   if ( ignoreCase )
206     {
207     // s1 to lower case
208     std::string ls1 = s1;
209     ToLowerCase( ls1 );
210     // s2 to lower case
211     std::string ls2 = s2;
212     ToLowerCase( ls2 );
213     // do the check
214     if ( ls1.find(ls2) == 0 )
215       {
216       return true;
217       }
218     else
219       {
220       return false;
221       }
222     }
223 
224   // if case must be considered
225   if ( s1.find(s2) == 0 )
226     {
227     return true;
228     }
229   else
230     {
231     return false;
232     }
233 }
234 
235 /** Method to test whether a string ends with a user-given sub-string. */
236 bool
EndWith(const std::string & s1,const std::string & s2,bool ignoreCase)237 StringTools::EndWith( const std::string& s1, const std::string& s2, bool ignoreCase )
238 {
239   std::string::size_type pos = s1.size() - s2.size();
240 
241   // if case is not important
242   if ( ignoreCase )
243     {
244     // s1 to lower case
245     std::string ls1 = s1;
246     ToLowerCase( ls1 );
247     // s2 to lower case
248     std::string ls2 = s2;
249     ToLowerCase( ls2 );
250     // do the check
251     if ( ls1.rfind(ls2) == pos )
252       {
253       return true;
254       }
255     else
256       {
257       return false;
258       }
259     }
260 
261   // if case must be considered
262   if ( s1.rfind(s2) == pos )
263     {
264     return true;
265     }
266   else
267     {
268     return false;
269     }
270 }
271 
272 /** Method to test whether a string contains a user-given sub-string. */
273 bool
ContainSub(const std::string & s1,const std::string & s2,bool ignoreCase)274 StringTools::ContainSub( const std::string& s1, const std::string& s2, bool ignoreCase )
275 {
276   // if case is not important
277   if ( ignoreCase )
278     {
279     // s1 to lower case
280     std::string ls1 = s1;
281     ToLowerCase( ls1 );
282     // s2 to lower case
283     std::string ls2 = s2;
284     ToLowerCase( ls2 );
285     // to the check
286     if ( ls1.find(ls2) != std::string::npos )
287       {
288       return true;
289       }
290     else
291       {
292       return false;
293       }
294     }
295 
296   // if case must be considered
297   if ( s1.find(s2) != std::string::npos )
298     {
299     return true;
300     }
301   else
302     {
303     return false;
304     }
305 }
306 
307 } // namespace itk
308