Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/trunk/src/libraries/util/StringUtils.cc @ 9550

Last change on this file since 9550 was 9550, checked in by landauf, 11 years ago

merged testing branch back to trunk. unbelievable it took me 13 months to finish this chore…

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