Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/output/src/libraries/util/StringUtils.cc @ 8807

Last change on this file since 8807 was 8801, checked in by landauf, 13 years ago

fixed bug with multiple lines in shell.
adjusted colors in IOConsole and InGameConsole a bit

  • Property svn:eol-style set to native
File size: 18.0 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 <ctime>
38#include <boost/scoped_array.hpp>
39#include "Convert.h"
40#include "Math.h"
41
42namespace orxonox
43{
44    /// A blank string (""). Used to return a blank string by reference.
45    std::string BLANKSTRING;
46
47    /// Returns a string of a unique number. This function is guaranteed to never return the same string twice.
48    std::string getUniqueNumberString()
49    {
50        return multi_cast<std::string>(getUniqueNumber());
51    }
52
53    /// Removes all whitespaces from a string.
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    /// Returns a copy of a string without whitespaces.
66    std::string getStripped(const std::string& str)
67    {
68        std::string output(str);
69        strip(&output);
70        return output;
71    }
72
73    /// Returns a copy of a string without trailing whitespaces.
74    std::string removeTrailingWhitespaces(const std::string& str)
75    {
76        size_t pos1 = 0;
77        int pos2 = static_cast<int>(str.size() - 1);
78        for (; pos1 < str.size() && (str[pos1] == ' ' || str[pos1] == '\t' || str[pos1] == '\n'); pos1++);
79        for (; pos2 > 0         && (str[pos2] == ' ' || str[pos2] == '\t' || str[pos2] == '\n'); pos2--);
80        return str.substr(pos1, pos2 - pos1 + 1);
81    }
82
83    /// Splits a given string by a delimiter and stores it in an output vector
84    void vectorize(const std::string& str, char delimiter, std::vector<std::string>* output)
85    {
86        for (size_t start = 0, end = 0; end != std::string::npos; start = end + 1)
87        {
88            end = str.find_first_of(delimiter, start);
89            output->push_back(str.substr(start, end - start));
90        }
91    }
92
93    /**
94        @brief Returns the position of the next quotation mark in the string, starting with start.
95        @param str The string
96        @param start The first position to look at
97        @return The position of the next quotation mark (@c std::string::npos if there is none)
98    */
99    size_t getNextQuote(const std::string& str, size_t start)
100    {
101        size_t quote = start - 1;
102
103        while ((quote = str.find('"', quote + 1)) != std::string::npos)
104        {
105            size_t backslash = quote;
106            size_t numbackslashes = 0;
107            for (; backslash > 0; backslash--, numbackslashes++)
108                if (str[backslash - 1] != '\\')
109                    break;
110
111            if (numbackslashes % 2 == 0)
112                break;
113        }
114
115        return quote;
116    }
117
118    /**
119        @brief Returns true if pos is between two quotation marks.
120        @param str The string
121        @param pos The position to check
122        @return True if pos is between two quotation marks
123    */
124    bool isBetweenQuotes(const std::string& str, size_t pos)
125    {
126        if (pos == std::string::npos)
127            return false;
128
129        size_t quotecount = 0;
130        size_t quote = static_cast<size_t>(-1);
131        while ((quote = getNextQuote(str, quote + 1)) < pos)
132        {
133            if (quote == pos)
134                return false;
135            quotecount++;
136        }
137
138        if (quote == std::string::npos)
139            return false;
140
141        return ((quotecount % 2) == 1);
142    }
143
144    /// Returns true if the string contains something like '..."between quotaton marks"...'.
145    bool hasStringBetweenQuotes(const std::string& str)
146    {
147        size_t pos1 = getNextQuote(str, 0);
148        size_t pos2 = getNextQuote(str, pos1 + 1);
149        return (pos1 != std::string::npos && pos2 != std::string::npos && pos2 > pos1 + 1);
150    }
151
152    /// If the string contains something like '..."between quotaton marks"...' then 'between quotaton marks' gets returned, otherwise "".
153    std::string getStringBetweenQuotes(const std::string& str)
154    {
155        size_t pos1 = getNextQuote(str, 0);
156        size_t pos2 = getNextQuote(str, pos1 + 1);
157        if (pos1 != std::string::npos && pos2 != std::string::npos)
158            return str.substr(pos1, pos2 - pos1 + 1);
159        else
160            return "";
161    }
162
163    /**
164        @brief Removes enclosing quotation marks if available (including whitespaces at the outside of the quotation marks).
165        @return The striped string without quotation marks
166    */
167    std::string stripEnclosingQuotes(const std::string& str)
168    {
169        size_t start = std::string::npos;
170        size_t end = 0;
171
172        for (size_t pos = 0; (pos < str.size()) && (pos < std::string::npos); pos++)
173        {
174            if (str[pos] == '"')
175            {
176                start = pos;
177                break;
178            }
179
180            if ((str[pos] != ' ') && (str[pos] != '\t') && (str[pos] != '\n'))
181                return str;
182        }
183
184        for (size_t pos = str.size() - 1; pos < std::string::npos; pos--)
185        {
186            if (str[pos] == '"')
187            {
188                end = pos;
189                break;
190            }
191
192            if ((str[pos] != ' ') && (str[pos] != '\t') && (str[pos] != '\n'))
193                return str;
194        }
195
196        if ((start != std::string::npos) && (end != 0))
197            return str.substr(start + 1, end - start - 1);
198        else
199            return str;
200    }
201
202    /**
203        @brief Removes enclosing braces '{' and '}' (the braces must be exactly on the beginning and the end of the string).
204        @return The striped string without braces
205    */
206    std::string stripEnclosingBraces(const std::string& str)
207    {
208        std::string output = str;
209
210        while (output.size() >= 2 && output[0] == '{' && output[output.size() - 1] == '}')
211            output = output.substr(1, output.size() - 2);
212
213        return output;
214    }
215
216    /**
217        @brief Determines if a string is a comment (starts with a comment-symbol).
218
219        A comment is defined by a leading '#', '%', ';' or '//'.
220    */
221    bool isComment(const std::string& str)
222    {
223        // Strip the line, whitespaces are disturbing
224        const std::string& teststring = getStripped(str);
225
226        // There are four possible comment-symbols:
227        //  1) #comment in script-language style
228        //  2) %comment in matlab style
229        //  3) ;comment in unreal tournament config-file style
230        //  4) //comment in code style
231        if (teststring.size() >= 2)
232        {
233            if (teststring[0] == '#' || teststring[0] == '%' || teststring[0] == ';' || (teststring[0] == '/' && teststring[1] == '/'))
234                return true;
235        }
236        else if (teststring.size() == 1)
237        {
238            if (teststring[0] == '#' || teststring[0] == '%' || teststring[0] == ';')
239                return true;
240        }
241
242        return false;
243    }
244
245    /// Determines if a string is empty (contains only whitespaces).
246    bool isEmpty(const std::string& str)
247    {
248        return getStripped(str).empty();
249    }
250
251    /// Determines if a string contains only numbers and maximal one '.'.
252    bool isNumeric(const std::string& str)
253    {
254        bool foundPoint = false;
255
256        for (std::string::const_iterator it = str.begin(); it != str.end(); ++it)
257        {
258            if (((*it) < '0' || (*it) > '9'))
259            {
260                if ((*it) != '.' && !foundPoint)
261                    foundPoint = true;
262                else
263                    return false;
264            }
265        }
266
267        return true;
268    }
269
270    /**
271        @brief Adds backslashes to the given string which makes special chars visible. Existing slashes will be doubled.
272
273        This function converts all special chars like line breaks, tabs, quotation marks etc. into
274        a human readable format by adding a backslash. So for example "\n" will be converted to
275        "\\" + "n".
276
277        This is usually used when a string is written to a file.
278
279        @see removeSlashes
280    */
281    std::string addSlashes(const std::string& str)
282    {
283        std::string output(str.size() * 2, ' ');
284        size_t i = 0;
285        for (size_t pos = 0; pos < str.size(); ++pos)
286        {
287            switch (str[pos])
288            {
289            case '\\': output[i] = '\\'; output[i + 1] = '\\'; break;
290            case '\n': output[i] = '\\'; output[i + 1] =  'n'; break;
291            case '\t': output[i] = '\\'; output[i + 1] =  't'; break;
292            case '\v': output[i] = '\\'; output[i + 1] =  'v'; break;
293            case '\b': output[i] = '\\'; output[i + 1] =  'b'; break;
294            case '\r': output[i] = '\\'; output[i + 1] =  'r'; break;
295            case '\f': output[i] = '\\'; output[i + 1] =  'f'; break;
296            case '\a': output[i] = '\\'; output[i + 1] =  'a'; break;
297            case  '"': output[i] = '\\'; output[i + 1] =  '"'; break;
298            case '\0': output[i] = '\\'; output[i + 1] =  '0'; break;
299            default  : output[i] = str[pos]; ++i; continue;
300            }
301            i += 2;
302        }
303        output.resize(i);
304
305        return output;
306    }
307
308    /**
309        @brief Removes backslashes from the given string. Double backslashes are interpreted as one backslash.
310
311        This function removes all backslashes and converts the human readable equivalents of
312        special chars like "\\" + "n" into their real meaning (in this case a line break or "\n").
313
314        This is usually used when reading a string from a file.
315
316        @see addSlashes
317    */
318    std::string removeSlashes(const std::string& str)
319    {
320        if (str.size() <= 1)
321            return str;
322
323        std::string output(str.size(), ' ');
324        size_t i = 0;
325        size_t pos = 0;
326        while (pos < str.size() - 1)
327        {
328            if (str[pos] == '\\')
329            {
330                switch (str[pos + 1])
331                {
332                case '\\': output[i] = '\\'; break;
333                case  'n': output[i] = '\n'; break;
334                case  't': output[i] = '\t'; break;
335                case  'v': output[i] = '\v'; break;
336                case  'b': output[i] = '\b'; break;
337                case  'r': output[i] = '\r'; break;
338                case  'f': output[i] = '\f'; break;
339                case  'a': output[i] = '\a'; break;
340                case  '"': output[i] =  '"'; break;
341                case  '0': output[i] = '\0'; break;
342                default: ++pos; continue;
343                }
344                pos += 2; ++i;
345            }
346            else
347                output[i++] = str[pos++];
348        }
349        if (pos < str.size())
350            output[i++] = str[pos];
351        output.resize(i);
352
353        return output;
354    }
355
356    /// Replaces each char between A and Z with its lowercase equivalent.
357    void lowercase(std::string* str)
358    {
359        for (size_t i = 0; i < str->size(); ++i)
360        {
361            (*str)[i] = static_cast<char>(tolower((*str)[i]));
362        }
363    }
364
365    /// Returns a copy of the given string where all chars are converted to lowercase.
366    std::string getLowercase(const std::string& str)
367    {
368        std::string output(str);
369        lowercase(&output);
370        return output;
371    }
372
373    /// Replaces each char between a and z with its uppercase equivalent.
374    void uppercase(std::string* str)
375    {
376        for (size_t i = 0; i < str->size(); ++i)
377        {
378            (*str)[i] = static_cast<char>(toupper((*str)[i]));
379        }
380    }
381
382    /// Returns a copy of the given string where all chars are converted to uppercase.
383    std::string getUppercase(const std::string& str)
384    {
385        std::string output(str);
386        uppercase(&output);
387        return output;
388    }
389
390    /**
391        @brief Compares two strings ignoring different casing.
392        @return s1 == s1 -> returns 0 / s1 < s2 -> returns -1 / s1 >= s2 -> returns 1
393    */
394    int nocaseCmp(const std::string& s1, const std::string& s2)
395    {
396        std::string::const_iterator it1=s1.begin();
397        std::string::const_iterator it2=s2.begin();
398
399        //stop when either string's end has been reached
400        while ( (it1!=s1.end()) && (it2!=s2.end()) )
401        {
402            if(::toupper(*it1) != ::toupper(*it2)) //letters differ?
403                // return -1 to indicate smaller than, 1 otherwise
404                return (::toupper(*it1)  < ::toupper(*it2)) ? -1 : 1;
405            //proceed to the next character in each string
406            ++it1;
407            ++it2;
408        }
409        size_t size1=s1.size(), size2=s2.size();// cache lengths
410        //return -1,0 or 1 according to strings' lengths
411        if (size1==size2)
412            return 0;
413        return (size1<size2) ? -1 : 1;
414    }
415
416
417    /**
418        @brief Compares the first @a len chars of two strings ignoring different casing.
419        @param s1 First string
420        @param s2 Second string
421        @param len Maximal number of chars to compare
422    */
423    int nocaseCmp(const std::string& s1, const std::string& s2, size_t len)
424    {
425        if (len == 0)
426            return 0;
427        std::string::const_iterator it1=s1.begin();
428        std::string::const_iterator it2=s2.begin();
429
430        //stop when either string's end has been reached
431        while ( (it1!=s1.end()) && (it2!=s2.end()) && len-- > 0)
432        {
433            if(::toupper(*it1) != ::toupper(*it2)) //letters differ?
434                // return -1 to indicate smaller than, 1 otherwise
435                return (::toupper(*it1)  < ::toupper(*it2)) ? -1 : 1;
436            //proceed to the next character in each string
437            ++it1;
438            ++it2;
439        }
440        return 0;
441    }
442
443    /// Returns true if the string contains a comment, introduced by #, %, ; or //.
444    bool hasComment(const std::string& str)
445    {
446        return (getCommentPosition(str) != std::string::npos);
447    }
448
449    /// If the string contains a comment, the comment gets returned (including the comment symbol), an empty string otherwise.
450    std::string getComment(const std::string& str)
451    {
452        return str.substr(getCommentPosition(str));
453    }
454
455    /// If the string contains a comment, the position of the comment-symbol gets returned, @c std::string::npos otherwise.
456    size_t getCommentPosition(const std::string& str)
457    {
458        return getNextCommentPosition(str, 0);
459    }
460
461    /**
462        @brief Returns the position of the next comment-symbol, starting with @a start.
463        @param str The string
464        @param start The first position to look at
465    */
466    size_t getNextCommentPosition(const std::string& str, size_t start)
467    {
468        for (size_t i = start; i < str.size(); i++)
469            if (isComment(str.substr(i)))
470                return i;
471
472        return std::string::npos;
473    }
474
475    /**
476        @brief Replaces individual charaters
477        @param str String to be manipulated
478        @param target Character to be replaced
479        @param replacement Replacement character
480        @return Number of replacements
481    */
482    size_t replaceCharacters(std::string& str, char target, char replacement)
483    {
484        size_t j = 0;
485        for (size_t i = 0; i < str.size(); ++i)
486        {
487            if (str[i] == target)
488            {
489                str[i] = replacement;
490                ++j;
491            }
492        }
493        return j;
494    }
495
496    /**
497        @brief Calculates the Levenshtein distance between two strings.
498
499        The Levenshtein distance is defined by the number of transformations needed to convert str1
500        into str2. Possible transformations are substituted, added, or removed characters.
501    */
502    unsigned int getLevenshteinDistance(const std::string& str1, const std::string& str2)
503    {
504        size_t cols = str1.size() + 1;
505        size_t rows = str2.size() + 1;
506        boost::scoped_array<int> matrix(new int[rows * cols]);
507
508        for (size_t r = 0; r < rows; ++r)
509            for (size_t c = 0; c < cols; ++c)
510                matrix[r*cols + c] = 0;
511
512        for (size_t i = 1; i < cols; ++i)
513            matrix[0*cols + i] = i;
514        for (size_t i = 1; i < rows; ++i)
515            matrix[i*cols + 0] = i;
516
517        for (size_t r = 1; r < rows; ++r)
518            for (size_t c = 1; c < cols; ++c)
519                matrix[r*cols + c] = (str1[c-1] != str2[r-1]);
520
521        for (size_t r = 1; r < rows; ++r)
522            for (size_t c = 1; c < cols; ++c)
523                matrix[r*cols + c] = std::min(std::min(matrix[(r-1)*cols + c] + 1,
524                                                       matrix[r*cols + c-1] + 1),
525                                              matrix[(r-1)*cols + c-1] + (str1[c-1] != str2[r-1]));
526
527        return matrix[(rows-1)*cols + cols-1];
528    }
529
530    /**
531    @brief
532        Get a timestamp for the curent time instant.
533    @return
534        Returns a string with the timestamp.
535    */
536    std::string getTimestamp(void)
537    {
538        struct tm *pTime;
539        time_t ctTime; std::time(&ctTime);
540        pTime = std::localtime( &ctTime );
541        std::ostringstream oss;
542        oss << std::setw(2) << std::setfill('0') << (pTime->tm_mon + 1)
543            << std::setw(2) << std::setfill('0') << pTime->tm_mday
544            << std::setw(2) << std::setfill('0') << (pTime->tm_year + 1900)
545            << "_" << std::setw(2) << std::setfill('0') << pTime->tm_hour
546            << std::setw(2) << std::setfill('0') << pTime->tm_min
547            << std::setw(2) << std::setfill('0') << pTime->tm_sec;
548        return oss.str();
549    }
550}
Note: See TracBrowser for help on using the repository browser.