Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/consolecommands3/src/libraries/util/StringUtils.cc @ 7830

Last change on this file since 7830 was 7241, checked in by rgrieder, 14 years ago

This is a little prettier.

  • Property svn:eol-style set to native
File size: 17.4 KB
Line 
1/*
2 *   ORXONOX - the hottest 3D action shooter ever to exist
3 *                    > www.orxonox.net <
4 *
5 *
6 *   License notice:
7 *
8 *   This program is free software; you can redistribute it and/or
9 *   modify it under the terms of the GNU General Public License
10 *   as published by the Free Software Foundation; either version 2
11 *   of the License, or (at your option) any later version.
12 *
13 *   This program is distributed in the hope that it will be useful,
14 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
15 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 *   GNU General Public License for more details.
17 *
18 *   You should have received a copy of the GNU General Public License
19 *   along with this program; if not, write to the Free Software
20 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
21 *
22 *   Author:
23 *      Fabian 'x3n' Landau
24 *   Co-authors:
25 *      Benjamin Grauer
26 *
27 */
28
29/**
30    @file
31    @brief Implementation of several string manipulation functions.
32*/
33
34#include "StringUtils.h"
35
36#include <cctype>
37#include <boost/scoped_array.hpp>
38#include "Convert.h"
39#include "Math.h"
40
41namespace orxonox
42{
43    std::string BLANKSTRING;
44
45    std::string getUniqueNumberString()
46    {
47        return multi_cast<std::string>(getUniqueNumber());
48    }
49
50    /**
51        @brief Removes all whitespaces from a string.
52        @param str The string to strip
53    */
54    void strip(std::string* str)
55    {
56        size_t pos;
57        while ((pos = str->find(' ')) < str->length())
58            str->erase(pos, 1);
59        while ((pos = str->find('\t')) < str->length())
60            str->erase(pos, 1);
61        while ((pos = str->find('\n')) < str->length())
62            str->erase(pos, 1);
63    }
64
65    /**
66        @brief Returns a copy of a string without whitespaces.
67        @param str The string to strip
68        @return The stripped line
69    */
70    std::string getStripped(const std::string& str)
71    {
72        std::string output(str);
73        strip(&output);
74        return output;
75    }
76
77    /**
78        @brief Returns a copy of a string without trailing whitespaces.
79        @param str The string
80        @return The modified copy
81    */
82    std::string removeTrailingWhitespaces(const std::string& str)
83    {
84        size_t pos1 = 0;
85        int pos2 = static_cast<int>(str.size() - 1);
86        for (; pos1 < str.size() && (str[pos1] == ' ' || str[pos1] == '\t' || str[pos1] == '\n'); pos1++);
87        for (; pos2 > 0         && (str[pos2] == ' ' || str[pos2] == '\t' || str[pos2] == '\n'); pos2--);
88        return str.substr(pos1, pos2 - pos1 + 1);
89    }
90
91    /**
92        @brief Returns the position of the next quote in the string, starting with start.
93        @param str The string
94        @param start The startposition
95        @return The position of the next quote (std::string::npos if there is no next quote)
96    */
97    size_t getNextQuote(const std::string& str, size_t start)
98    {
99        size_t quote = start - 1;
100
101        while ((quote = str.find('"', quote + 1)) != std::string::npos)
102        {
103            size_t backslash = quote;
104            size_t numbackslashes = 0;
105            for (; backslash > 0; backslash--, numbackslashes++)
106                if (str[backslash - 1] != '\\')
107                    break;
108
109            if (numbackslashes % 2 == 0)
110                break;
111        }
112
113        return quote;
114    }
115
116    /**
117        @brief Returns true if pos is between two quotes.
118        @param str The string
119        @param pos The position to check
120        @return True if pos is between two quotes
121    */
122    bool isBetweenQuotes(const std::string& str, size_t pos)
123    {
124        if (pos == std::string::npos)
125            return false;
126
127        size_t quotecount = 0;
128        size_t quote = static_cast<size_t>(-1);
129        while ((quote = getNextQuote(str, quote + 1)) < pos)
130        {
131            if (quote == pos)
132                return false;
133            quotecount++;
134        }
135
136        if (quote == std::string::npos)
137            return false;
138
139        return ((quotecount % 2) == 1);
140    }
141
142    /**
143        @brief Returns true if the string contains something like '..."between quotes"...'.
144        @param The string
145        @return True if there is something between quotes
146    */
147    bool hasStringBetweenQuotes(const std::string& str)
148    {
149        size_t pos1 = getNextQuote(str, 0);
150        size_t pos2 = getNextQuote(str, pos1 + 1);
151        return (pos1 != std::string::npos && pos2 != std::string::npos && pos2 > pos1 + 1);
152    }
153
154    /**
155        @brief If the string contains something like '..."between quotes"...' then 'between quotes' gets returned (without quotes).
156        @param The string
157        @param The string between the quotes
158    */
159    std::string getStringBetweenQuotes(const std::string& str)
160    {
161        size_t pos1 = getNextQuote(str, 0);
162        size_t pos2 = getNextQuote(str, pos1 + 1);
163        if (pos1 != std::string::npos && pos2 != std::string::npos)
164            return str.substr(pos1, pos2 - pos1 + 1);
165        else
166            return "";
167    }
168
169    /**
170        @brief Removes enclosing quotes if available (including whitespaces at the outside of the quotes).
171        @brief str The string to strip
172        @return The string with removed quotes
173    */
174    std::string stripEnclosingQuotes(const std::string& str)
175    {
176        size_t start = std::string::npos;
177        size_t end = 0;
178
179        for (size_t pos = 0; (pos < str.size()) && (pos < std::string::npos); pos++)
180        {
181            if (str[pos] == '"')
182            {
183                start = pos;
184                break;
185            }
186
187            if ((str[pos] != ' ') && (str[pos] != '\t') && (str[pos] != '\n'))
188                return str;
189        }
190
191        for (size_t pos = str.size() - 1; pos < std::string::npos; pos--)
192        {
193            if (str[pos] == '"')
194            {
195                end = pos;
196                break;
197            }
198
199            if ((str[pos] != ' ') && (str[pos] != '\t') && (str[pos] != '\n'))
200                return str;
201        }
202
203        if ((start != std::string::npos) && (end != 0))
204            return str.substr(start + 1, end - start - 1);
205        else
206            return str;
207    }
208
209    /**
210        @brief Removes enclosing {braces} (braces must be exactly on the beginning and the end of the string).
211        @param str The string to strip
212        @return The striped string
213    */
214    std::string stripEnclosingBraces(const std::string& str)
215    {
216        std::string output = str;
217
218        while (output.size() >= 2 && output[0] == '{' && output[output.size() - 1] == '}')
219            output = output.substr(1, output.size() - 2);
220
221        return output;
222    }
223
224    /**
225        @brief Determines if a string is a comment (starts with a comment-symbol).
226        @param str The string to check
227        @return True = it's a comment
228
229        A comment is defined by a leading '#', '%', ';' or '//'.
230    */
231    bool isComment(const std::string& str)
232    {
233        // Strip the line, whitespaces are disturbing
234        const std::string& teststring = getStripped(str);
235
236        // There are four possible comment-symbols:
237        //  1) #comment in script-language style
238        //  2) %comment in matlab style
239        //  3) ;comment in unreal tournament config-file style
240        //  4) //comment in code style
241        if (teststring.size() >= 2)
242        {
243            if (teststring[0] == '#' || teststring[0] == '%' || teststring[0] == ';' || (teststring[0] == '/' && teststring[1] == '/'))
244                return true;
245        }
246        else if (teststring.size() == 1)
247        {
248            if (teststring[0] == '#' || teststring[0] == '%' || teststring[0] == ';')
249                return true;
250        }
251
252        return false;
253    }
254
255    /**
256        @brief Determines if a string is empty (contains only whitespaces).
257        @param str The string to check
258        @return True = it's empty
259    */
260    bool isEmpty(const std::string& str)
261    {
262        return getStripped(str).empty();
263    }
264
265    /**
266        @brief Determines if a string contains only numbers and maximal one '.'.
267        @param str The string to check
268        @return True = it's a number
269    */
270    bool isNumeric(const std::string& str)
271    {
272        bool foundPoint = false;
273
274        for (std::string::const_iterator it = str.begin(); it != str.end(); ++it)
275        {
276            if (((*it) < '0' || (*it) > '9'))
277            {
278                if ((*it) != '.' && !foundPoint)
279                    foundPoint = true;
280                else
281                    return false;
282            }
283        }
284
285        return true;
286    }
287
288    /**
289        @brief Adds backslashes to the given string which makes special chars visible. Existing slashes will be doubled.
290        @param str The string to manipulate
291        @return The string with added slashes
292    */
293    std::string addSlashes(const std::string& str)
294    {
295        std::string output(str.size() * 2, ' ');
296        size_t i = 0;
297        for (size_t pos = 0; pos < str.size(); ++pos)
298        {
299            switch (str[pos])
300            {
301            case '\\': output[i] = '\\'; output[i + 1] = '\\'; break;
302            case '\n': output[i] = '\\'; output[i + 1] =  'n'; break;
303            case '\t': output[i] = '\\'; output[i + 1] =  't'; break;
304            case '\v': output[i] = '\\'; output[i + 1] =  'v'; break;
305            case '\b': output[i] = '\\'; output[i + 1] =  'b'; break;
306            case '\r': output[i] = '\\'; output[i + 1] =  'r'; break;
307            case '\f': output[i] = '\\'; output[i + 1] =  'f'; break;
308            case '\a': output[i] = '\\'; output[i + 1] =  'a'; break;
309            case  '"': output[i] = '\\'; output[i + 1] =  '"'; break;
310            case '\0': output[i] = '\\'; output[i + 1] =  '0'; break;
311            default  : output[i] = str[pos]; ++i; continue;
312            }
313            i += 2;
314        }
315        output.resize(i);
316
317        return output;
318    }
319
320    /**
321        @brief Removes backslashes from the given string. Double backslashes are interpreted as one backslash.
322        @param str The string to manipulate
323        @return The string with removed slashes
324    */
325    std::string removeSlashes(const std::string& str)
326    {
327        if (str.size() <= 1)
328            return str;
329
330        std::string output(str.size(), ' ');
331        size_t i = 0;
332        size_t pos = 0;
333        while (pos < str.size() - 1)
334        {
335            if (str[pos] == '\\')
336            {
337                switch (str[pos + 1])
338                {
339                case '\\': output[i] = '\\'; break;
340                case  'n': output[i] = '\n'; break;
341                case  't': output[i] = '\t'; break;
342                case  'v': output[i] = '\v'; break;
343                case  'b': output[i] = '\b'; break;
344                case  'r': output[i] = '\r'; break;
345                case  'f': output[i] = '\f'; break;
346                case  'a': output[i] = '\a'; break;
347                case  '"': output[i] =  '"'; break;
348                case  '0': output[i] = '\0'; break;
349                default: ++pos; continue;
350                }
351                pos += 2; ++i;
352            }
353            else
354                output[i++] = str[pos++];
355        }
356        if (pos < str.size())
357            output[i++] = str[pos];
358        output.resize(i);
359
360        return output;
361    }
362
363    /**
364        @brief Replaces each char between A and Z with its lowercase equivalent.
365        @param str The string to convert
366    */
367    void lowercase(std::string* str)
368    {
369        for (size_t i = 0; i < str->size(); ++i)
370        {
371            (*str)[i] = static_cast<char>(tolower((*str)[i]));
372        }
373    }
374
375    /**
376        @brief Returns a copy of the given string without uppercase chars.
377        @param str The string
378        @return The copy
379    */
380    std::string getLowercase(const std::string& str)
381    {
382        std::string output(str);
383        lowercase(&output);
384        return output;
385    }
386
387    /**
388        @brief Replaces each char between a and z with its uppercase equivalent.
389        @param str The string to convert
390    */
391    void uppercase(std::string* str)
392    {
393        for (size_t i = 0; i < str->size(); ++i)
394        {
395            (*str)[i] = static_cast<char>(toupper((*str)[i]));
396        }
397    }
398
399    /**
400        @brief Returns a copy of the given string without lowercase chars.
401        @param str The string
402        @return The copy
403    */
404    std::string getUppercase(const std::string& str)
405    {
406        std::string output(str);
407        uppercase(&output);
408        return output;
409    }
410
411    /**
412        @brief Compares two strings ignoring different casing.
413        @param s1 First string
414        @param s2 Second string
415    */
416    int nocaseCmp(const std::string& s1, const std::string& s2)
417    {
418        std::string::const_iterator it1=s1.begin();
419        std::string::const_iterator it2=s2.begin();
420
421        //stop when either string's end has been reached
422        while ( (it1!=s1.end()) && (it2!=s2.end()) )
423        {
424            if(::toupper(*it1) != ::toupper(*it2)) //letters differ?
425                // return -1 to indicate smaller than, 1 otherwise
426                return (::toupper(*it1)  < ::toupper(*it2)) ? -1 : 1;
427            //proceed to the next character in each string
428            ++it1;
429            ++it2;
430        }
431        size_t size1=s1.size(), size2=s2.size();// cache lengths
432        //return -1,0 or 1 according to strings' lengths
433        if (size1==size2)
434            return 0;
435        return (size1<size2) ? -1 : 1;
436    }
437
438
439    /**
440        @brief Compares the first 'len' chars of two strings ignoring different casing.
441        @param s1 First string
442        @param s2 Second string
443        @param len Maximal number of chars to compare
444    */
445    int nocaseCmp(const std::string& s1, const std::string& s2, size_t len)
446    {
447        if (len == 0)
448            return 0;
449        std::string::const_iterator it1=s1.begin();
450        std::string::const_iterator it2=s2.begin();
451
452        //stop when either string's end has been reached
453        while ( (it1!=s1.end()) && (it2!=s2.end()) && len-- > 0)
454        {
455            if(::toupper(*it1) != ::toupper(*it2)) //letters differ?
456                // return -1 to indicate smaller than, 1 otherwise
457                return (::toupper(*it1)  < ::toupper(*it2)) ? -1 : 1;
458            //proceed to the next character in each string
459            ++it1;
460            ++it2;
461        }
462        return 0;
463    }
464
465    /**
466        @brief Returns true if the string contains a comment, introduced by #, %, ; or //.
467        @param str The string
468        @return True if the string contains a comment
469    */
470    bool hasComment(const std::string& str)
471    {
472        return (getCommentPosition(str) != std::string::npos);
473    }
474
475    /**
476        @brief If the string contains a comment, the comment gets returned (including the comment symbol), an empty string otherwise.
477        @param str The string
478        @return The comment
479    */
480    std::string getComment(const std::string& str)
481    {
482        return str.substr(getCommentPosition(str));
483    }
484
485    /**
486        @brief If the string contains a comment, the position of the comment-symbol gets returned, std::string::npos otherwise.
487        @param str The string
488        @return The position
489    */
490    size_t getCommentPosition(const std::string& str)
491    {
492        return getNextCommentPosition(str, 0);
493    }
494
495    /**
496        @brief Returns the position of the next comment-symbol, starting with start.
497        @param str The string
498        @param start The startposition
499        @return The position
500    */
501    size_t getNextCommentPosition(const std::string& str, size_t start)
502    {
503        for (size_t i = start; i < str.size(); i++)
504            if (isComment(str.substr(i)))
505                return i;
506
507        return std::string::npos;
508    }
509
510    /**
511        @brief Replaces individual charaters
512        @param str String to be manipulated
513        @param target Character to be replaced
514        @param replacement Replacement character
515        @return Number of replacements
516    */
517    size_t replaceCharacters(std::string& str, char target, char replacement)
518    {
519        size_t j = 0;
520        for (size_t i = 0; i < str.size(); ++i)
521        {
522            if (str[i] == target)
523            {
524                str[i] = replacement;
525                ++j;
526            }
527        }
528        return j;
529    }
530
531    /**
532        @brief Calculates the Levenshtein distance between two strings.
533
534        The Levenshtein distance is defined by the number of transformations needed to convert str1
535        into str2. Possible transformations are substituted, added, or removed characters.
536    */
537    unsigned int getLevenshteinDistance(const std::string& str1, const std::string& str2)
538    {
539        size_t cols = str1.size() + 1;
540        size_t rows = str2.size() + 1;
541        boost::scoped_array<int> matrix(new int[rows * cols]);
542
543        for (size_t r = 0; r < rows; ++r)
544            for (size_t c = 0; c < cols; ++c)
545                matrix[r*cols + c] = 0;
546
547        for (size_t i = 1; i < cols; ++i)
548            matrix[0*cols + i] = i;
549        for (size_t i = 1; i < rows; ++i)
550            matrix[i*cols + 0] = i;
551
552        for (size_t r = 1; r < rows; ++r)
553            for (size_t c = 1; c < cols; ++c)
554                matrix[r*cols + c] = (str1[c-1] != str2[r-1]);
555
556        for (size_t r = 1; r < rows; ++r)
557            for (size_t c = 1; c < cols; ++c)
558                matrix[r*cols + c] = std::min(std::min(matrix[(r-1)*cols + c] + 1,
559                                                       matrix[r*cols + c-1] + 1),
560                                              matrix[(r-1)*cols + c-1] + (str1[c-1] != str2[r-1]));
561
562        return matrix[(rows-1)*cols + cols-1];
563    }
564}
Note: See TracBrowser for help on using the repository browser.