Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/forks/sandbox_qt/src/libraries/util/StringUtils.cc @ 8280

Last change on this file since 8280 was 7434, checked in by rgrieder, 14 years ago

Build fix.

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