Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

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

Last change on this file since 12012 was 11114, checked in by landauf, 9 years ago

unfortunately std::put_time and std::get_time are only available in GCC 5+, so I have to revert this change.

  • Property svn:eol-style set to native
File size: 17.0 KB
RevLine 
[1505]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
[1791]29/**
[2171]30    @file
[1791]31    @brief Implementation of several string manipulation functions.
32*/
33
[3250]34#include "StringUtils.h"
[1505]35
[11111]36#include <algorithm>
[1505]37#include <cctype>
[8232]38#include <ctime>
[11097]39#include <memory>
[2087]40#include "Convert.h"
41#include "Math.h"
[1625]42
[2171]43namespace orxonox
[2087]44{
[7401]45    /// A blank string (""). Used to return a blank string by reference.
[6417]46    std::string BLANKSTRING;
[2087]47
[7401]48    /// Returns a string of a unique number. This function is guaranteed to never return the same string twice.
[2171]49    std::string getUniqueNumberString()
50    {
[3280]51        return multi_cast<std::string>(getUniqueNumber());
[2171]52    }
[1505]53
[7401]54    /// Removes all whitespaces from a string.
[2171]55    void strip(std::string* str)
56    {
[11111]57        str->erase(std::remove_if(str->begin(), str->end(), [](char val) { return std::isspace(val) != 0; }), str->end());
[2171]58    }
[1505]59
[7401]60    /// Returns a copy of a string without whitespaces.
[2171]61    std::string getStripped(const std::string& str)
62    {
[6417]63        std::string output(str);
[2171]64        strip(&output);
65        return output;
66    }
[1505]67
[7401]68    /// Returns a copy of a string without trailing whitespaces.
[2171]69    std::string removeTrailingWhitespaces(const std::string& str)
70    {
[11111]71        auto pos1 = std::find_if(str.begin(), str.end(), [](char val) { return std::isspace(val) == 0; });
72        auto pos2 = std::find_if(str.rbegin(), str.rend(), [](char val) { return std::isspace(val) == 0; });
73        if (pos1 == str.end() && pos2 == str.rend())
74        {
75            // String doesn't have non-whitespace characters
76            return "";
77        }
78        return std::string(pos1, pos2.base());
[2171]79    }
[1505]80
[9550]81    /// Splits a given string by a delimiter and stores it in an output vector. See @ref SubString for a more sophisticated implementation.
[8858]82    void vectorize(const std::string& str, char delimiter, std::vector<std::string>* output)
83    {
[9550]84        output->clear();
[8858]85        for (size_t start = 0, end = 0; end != std::string::npos; start = end + 1)
86        {
87            end = str.find_first_of(delimiter, start);
88            output->push_back(str.substr(start, end - start));
89        }
90    }
91
[2171]92    /**
[9550]93        @brief Returns the position of the next quotation mark in the string, starting with start. Escaped quotation marks (with \ in front) are not considered.
[2171]94        @param str The string
[7401]95        @param start The first position to look at
96        @return The position of the next quotation mark (@c std::string::npos if there is none)
[2171]97    */
98    size_t getNextQuote(const std::string& str, size_t start)
[1505]99    {
[2171]100        size_t quote = start - 1;
101
[6417]102        while ((quote = str.find('"', quote + 1)) != std::string::npos)
[2171]103        {
104            size_t backslash = quote;
105            size_t numbackslashes = 0;
106            for (; backslash > 0; backslash--, numbackslashes++)
107                if (str[backslash - 1] != '\\')
108                    break;
109
110            if (numbackslashes % 2 == 0)
[1505]111                break;
[2171]112        }
[1505]113
[2171]114        return quote;
[1505]115    }
116
[2171]117    /**
[7401]118        @brief Returns true if pos is between two quotation marks.
[2171]119        @param str The string
120        @param pos The position to check
[7401]121        @return True if pos is between two quotation marks
[2171]122    */
123    bool isBetweenQuotes(const std::string& str, size_t pos)
[1505]124    {
[2171]125        if (pos == std::string::npos)
[1830]126            return false;
[1505]127
[2171]128        size_t quotecount = 0;
[3301]129        size_t quote = static_cast<size_t>(-1);
[2171]130        while ((quote = getNextQuote(str, quote + 1)) < pos)
131            quotecount++;
[1505]132
[9550]133        if (quote == pos)
134            return false;
[2171]135        if (quote == std::string::npos)
136            return false;
[1505]137
[2171]138        return ((quotecount % 2) == 1);
139    }
[1505]140
[7401]141    /// Returns true if the string contains something like '..."between quotaton marks"...'.
[2171]142    bool hasStringBetweenQuotes(const std::string& str)
143    {
144        size_t pos1 = getNextQuote(str, 0);
145        size_t pos2 = getNextQuote(str, pos1 + 1);
146        return (pos1 != std::string::npos && pos2 != std::string::npos && pos2 > pos1 + 1);
147    }
[1505]148
[7401]149    /// If the string contains something like '..."between quotaton marks"...' then 'between quotaton marks' gets returned, otherwise "".
[2171]150    std::string getStringBetweenQuotes(const std::string& str)
151    {
152        size_t pos1 = getNextQuote(str, 0);
153        size_t pos2 = getNextQuote(str, pos1 + 1);
154        if (pos1 != std::string::npos && pos2 != std::string::npos)
[9550]155            return str.substr(pos1 + 1, pos2 - pos1 - 1);
[2171]156        else
157            return "";
158    }
[1505]159
[2171]160    /**
[7401]161        @brief Removes enclosing quotation marks if available (including whitespaces at the outside of the quotation marks).
[11111]162        @return The stripped string without quotation marks
[2171]163    */
164    std::string stripEnclosingQuotes(const std::string& str)
[1505]165    {
[11111]166        auto start = str.begin();
167        auto end = str.rbegin();
[2171]168
[11111]169        for (; start != str.end() && *start != '"'; ++start)
[1505]170        {
[11111]171            if (std::isspace(*start) == 0)
[2171]172                return str;
[1505]173        }
174
[11111]175        for (; end != str.rend() && *end != '"'; ++end)
[2171]176        {
[11111]177            if (std::isspace(*end) == 0)
[2171]178                return str;
[1505]179        }
180
[11111]181        if (start != str.end() && end != str.rend())
182        {
183            ++start; // Skip "
184            ++end; // Skip "
185            return std::string(start, end.base());
186        }
[2171]187        else
[1505]188            return str;
189    }
190
[2171]191    /**
[7401]192        @brief Removes enclosing braces '{' and '}' (the braces must be exactly on the beginning and the end of the string).
[11111]193        @return The stripped string without braces
[2171]194    */
195    std::string stripEnclosingBraces(const std::string& str)
196    {
[11111]197        if (str.empty())
198        {
199            return "";
200        }
[1505]201
[11111]202        auto start = str.begin();
203        auto end = str.rbegin();
[1505]204
[11111]205        while (*start == '{' && *end == '}')
206        {
207            ++start;
208            ++end;
209        }
210
211        return std::string(start, end.base());
[2171]212    }
[1505]213
[2171]214    /**
215        @brief Determines if a string is a comment (starts with a comment-symbol).
[1505]216
[2171]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
[6417]222        const std::string& teststring = getStripped(str);
[1505]223
[2171]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        }
[1505]239
[2171]240        return false;
[1505]241    }
[2171]242
[7401]243    /// Determines if a string is empty (contains only whitespaces).
[2171]244    bool isEmpty(const std::string& str)
[1505]245    {
[11111]246        return std::all_of(str.begin(), str.end(), [](char val) { return std::isspace(val) != 0; });
[1505]247    }
248
[2171]249    /**
250        @brief Adds backslashes to the given string which makes special chars visible. Existing slashes will be doubled.
[7401]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
[2171]259    */
260    std::string addSlashes(const std::string& str)
261    {
[6424]262        std::string output(str.size() * 2, ' ');
263        size_t i = 0;
[11071]264        for (const char& character : str)
[6424]265        {
[11071]266            switch (character)
[6424]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;
[11071]278            default  : output[i] = character; ++i; continue;
[6424]279            }
280            i += 2;
281        }
282        output.resize(i);
[1505]283
[2171]284        return output;
285    }
[1505]286
[2171]287    /**
288        @brief Removes backslashes from the given string. Double backslashes are interpreted as one backslash.
[7401]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
[2171]296    */
297    std::string removeSlashes(const std::string& str)
298    {
299        if (str.size() <= 1)
300            return str;
[1505]301
[6424]302        std::string output(str.size(), ' ');
303        size_t i = 0;
304        size_t pos = 0;
305        while (pos < str.size() - 1)
[2171]306        {
307            if (str[pos] == '\\')
308            {
[6424]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;
[2171]324            }
[6424]325            else
326                output[i++] = str[pos++];
[2171]327        }
[6424]328        if (pos < str.size())
329            output[i++] = str[pos];
330        output.resize(i);
[1505]331
[2171]332        return output;
333    }
334
[7401]335    /// Replaces each char between A and Z with its lowercase equivalent.
[2171]336    void lowercase(std::string* str)
[1505]337    {
[11111]338        std::transform(str->begin(), str->end(), str->begin(), [](char val) { return std::tolower(val); });
[1505]339    }
340
[7401]341    /// Returns a copy of the given string where all chars are converted to lowercase.
[2171]342    std::string getLowercase(const std::string& str)
343    {
[6417]344        std::string output(str);
[2171]345        lowercase(&output);
346        return output;
347    }
[1505]348
[7401]349    /// Replaces each char between a and z with its uppercase equivalent.
[2171]350    void uppercase(std::string* str)
[1505]351    {
[11111]352        std::transform(str->begin(), str->end(), str->begin(), [](char val) { return std::toupper(val); });
[1505]353    }
354
[7401]355    /// Returns a copy of the given string where all chars are converted to uppercase.
[2171]356    std::string getUppercase(const std::string& str)
357    {
[6417]358        std::string output(str);
[2171]359        uppercase(&output);
360        return output;
361    }
[1505]362
[2171]363    /**
364        @brief Compares two strings ignoring different casing.
[11111]365        @return s1 == s2 -> returns 0 / s1 < s2 -> returns -1 / s1 >= s2 -> returns 1
[2171]366    */
367    int nocaseCmp(const std::string& s1, const std::string& s2)
[1505]368    {
[11111]369        size_t size1 = s1.size(), size2 = s2.size(); // cache lengths
[2171]370
[11111]371        int res = nocaseCmp(s1, s2, std::min(size1, size2));
372
373        if (res != 0)
[2171]374        {
[11111]375            return res;
[2171]376        }
[11111]377
[2171]378        //return -1,0 or 1 according to strings' lengths
[11111]379        if (size1 == size2)
[2171]380            return 0;
[11111]381        return (size1 < size2) ? -1 : 1;
382
[1505]383    }
384
385
[2171]386    /**
[7401]387        @brief Compares the first @a len chars of two strings ignoring different casing.
[2171]388        @param s1 First string
389        @param s2 Second string
390        @param len Maximal number of chars to compare
391    */
392    int nocaseCmp(const std::string& s1, const std::string& s2, size_t len)
[1505]393    {
[2171]394        if (len == 0)
395            return 0;
[11111]396        std::string::const_iterator it1 = s1.begin();
397        std::string::const_iterator it2 = s2.begin();
[1505]398
[2171]399        //stop when either string's end has been reached
[11111]400        while ( (it1 != s1.end()) && (it2 != s2.end()) && len-- > 0)
[2171]401        {
[11111]402            if(std::toupper(*it1) != std::toupper(*it2)) //letters differ?
[2171]403                // return -1 to indicate smaller than, 1 otherwise
[11111]404                return (std::toupper(*it1)  < std::toupper(*it2)) ? -1 : 1;
[2171]405            //proceed to the next character in each string
406            ++it1;
407            ++it2;
408        }
[1505]409        return 0;
[2171]410    }
[1505]411
[7401]412    /// Returns true if the string contains a comment, introduced by #, %, ; or //.
[2171]413    bool hasComment(const std::string& str)
[1505]414    {
[9550]415        return (getNextCommentPosition(str) != std::string::npos);
[1505]416    }
417
[9550]418    /// 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.
[2171]419    std::string getComment(const std::string& str)
420    {
[9550]421        size_t pos = getNextCommentPosition(str);
422        if (pos == std::string::npos)
423            return "";
424        else
425            return str.substr(pos);
[2171]426    }
[1505]427
[2171]428    /**
[9550]429        @brief Returns the beginning of the next comment including whitespaces in front of the comment symbol.
[2171]430        @param str The string
[7401]431        @param start The first position to look at
[2171]432    */
433    size_t getNextCommentPosition(const std::string& str, size_t start)
434    {
435        for (size_t i = start; i < str.size(); i++)
436            if (isComment(str.substr(i)))
437                return i;
[1505]438
[2171]439        return std::string::npos;
440    }
[3327]441
442    /**
443        @brief Replaces individual charaters
444        @param str String to be manipulated
445        @param target Character to be replaced
446        @param replacement Replacement character
447        @return Number of replacements
448    */
[7284]449    size_t replaceCharacters(std::string& str, char target, char replacement)
[3327]450    {
451        size_t j = 0;
[11071]452        for (char& character : str)
[3327]453        {
[11071]454            if (character == target)
[3327]455            {
[11071]456                character = replacement;
[3327]457                ++j;
458            }
459        }
460        return j;
461    }
[7284]462
463    /**
464        @brief Calculates the Levenshtein distance between two strings.
465
466        The Levenshtein distance is defined by the number of transformations needed to convert str1
467        into str2. Possible transformations are substituted, added, or removed characters.
468    */
469    unsigned int getLevenshteinDistance(const std::string& str1, const std::string& str2)
470    {
471        size_t cols = str1.size() + 1;
472        size_t rows = str2.size() + 1;
[11071]473        const std::unique_ptr<int[]> matrix(new int[rows * cols]);
[7284]474
475        for (size_t r = 0; r < rows; ++r)
476            for (size_t c = 0; c < cols; ++c)
477                matrix[r*cols + c] = 0;
478
479        for (size_t i = 1; i < cols; ++i)
480            matrix[0*cols + i] = i;
481        for (size_t i = 1; i < rows; ++i)
482            matrix[i*cols + 0] = i;
483
484        for (size_t r = 1; r < rows; ++r)
485            for (size_t c = 1; c < cols; ++c)
486                matrix[r*cols + c] = (str1[c-1] != str2[r-1]);
487
488        for (size_t r = 1; r < rows; ++r)
489            for (size_t c = 1; c < cols; ++c)
490                matrix[r*cols + c] = std::min(std::min(matrix[(r-1)*cols + c] + 1,
491                                                       matrix[r*cols + c-1] + 1),
492                                              matrix[(r-1)*cols + c-1] + (str1[c-1] != str2[r-1]));
493
494        return matrix[(rows-1)*cols + cols-1];
495    }
[8858]496
[8232]497    /**
498    @brief
[11111]499        Get a timestamp for the current time instant.
[8232]500    @return
501        Returns a string with the timestamp.
502    */
503    std::string getTimestamp(void)
504    {
505        time_t ctTime; std::time(&ctTime);
[11111]506        tm* pTime = std::localtime(&ctTime);
[8232]507        std::ostringstream oss;
[11114]508//        oss << std::put_time(pTime, "%m%d%Y_%H%M%S"); // <-- std::put_time is only available in GCC 5+
509        oss << std::setw(2) << std::setfill('0') << (pTime->tm_mon + 1)
510            << std::setw(2) << std::setfill('0') << pTime->tm_mday
511            << std::setw(2) << std::setfill('0') << (pTime->tm_year + 1900)
512            << "_" << std::setw(2) << std::setfill('0') << pTime->tm_hour
513            << std::setw(2) << std::setfill('0') << pTime->tm_min
514            << std::setw(2) << std::setfill('0') << pTime->tm_sec;
[8232]515        return oss.str();
516    }
[1505]517}
Note: See TracBrowser for help on using the repository browser.