Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/cpp11_v2/src/libraries/util/StringUtils.cc @ 10983

Last change on this file since 10983 was 10916, checked in by landauf, 9 years ago

use actual types instead of 'auto'. only exception is for complicated template types, e.g. when iterating over a map

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