Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: code/branches/doc/src/libraries/util/SubString.cc @ 7332

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

Fixed bug in SubString. "a (),b" was split in "a (" and "b" instead of "a ()" and "b".
For the sake of performance, I just inserted a few lines instead of a generic solution at the end of SL_NORMAL.
(And please don't cite Donald Knuth for that optimisation…).

  • Property svn:eol-style set to native
File size: 19.4 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 *      Christian Meyer
24 *   Co-authors:
25 *      Benjamin Grauer
26 *
27
28//
29//  splitLine
30//  STL string tokenizer
31//
32//  Created by Clemens Wacha.
33//  Version 1.0
34//  Copyright (c) 2005 Clemens Wacha. All rights reserved.
35//
36
37 *   Extended by Fabian 'x3n' Landau by the SL_PARENTHESES mode.
38 */
39
40#include "SubString.h"
41#include <cstdio>
42#include "Debug.h"
43
44namespace orxonox
45{
46    const std::string SubString::WhiteSpaces          = " \n\t";
47    const std::string SubString::WhiteSpacesWithComma = " \n\t,";
48    const SubString SubString::NullSubString          = SubString();
49
50    /**
51        @brief Default constructor.
52    */
53    SubString::SubString()
54    {
55    }
56
57    /**
58        @brief Splits a string into multiple tokens.
59        @param line The line to split
60        @param delimiters Multiple characters at which to split the line
61        @param delimiterNeighbours Neighbours of the delimiters that will be erased as well (for example white-spaces)
62        @param bAllowEmptyEntries If true, empty tokens are also added to the SubString (if there are two delimiters without a char in between)
63        @param escapeChar The escape character that is used to escape safemode chars (for example if you want to use a quotation mark between two other quotation marks).
64        @param bRemoveEscapeChar If true, the escape char is removed from the tokens
65        @param safemodeChar Within these characters splitting won't happen (usually the quotation marks)
66        @param bRemoveSafemodeChar Removes the safemodeChar from the beginning and the ending of a token
67        @param openparenthesisChar The beginning of a safemode is marked with this (usually an opening brace)
68        @param closeparenthesisChar The ending of a safemode is marked with this (usually a closing brace)
69        @param bRemoveParenthesisChars Removes the parenthesis chars from the beginning and the ending of a token
70        @param commentChar The comment character (used to ignore the part of the line after the comment char).
71    */
72    SubString::SubString(const std::string& line,
73                         const std::string& delimiters, const std::string& delimiterNeighbours, bool bAllowEmptyEntries,
74                         char escapeChar, bool bRemoveEscapeChar, char safemodeChar, bool bRemoveSafemodeChar,
75                         char openparenthesisChar, char closeparenthesisChar, bool bRemoveParenthesisChars, char commentChar)
76    {
77        SubString::splitLine(this->tokens_, this->bTokenInSafemode_, line, delimiters, delimiterNeighbours, bAllowEmptyEntries, escapeChar, bRemoveEscapeChar, safemodeChar, bRemoveSafemodeChar, openparenthesisChar, closeparenthesisChar, bRemoveParenthesisChars, commentChar);
78    }
79
80    /**
81        @brief creates a new SubString based on a subset of an other SubString.
82        @param other The other SubString
83        @param begin The beginning of the subset
84
85        The subset ranges from the token with index @a begin to the end of the tokens.
86        If @a begin is greater than the greatest index, the new SubString will be empty.
87    */
88    SubString::SubString(const SubString& other, unsigned int begin)
89    {
90        for (unsigned int i = begin; i < other.size(); ++i)
91        {
92            this->tokens_.push_back(other[i]);
93            this->bTokenInSafemode_.push_back(other.isInSafemode(i));
94        }
95    }
96
97    /**
98        @brief creates a new SubString based on a subset of an other SubString.
99        @param other The other SubString
100        @param begin The beginning of the subset
101        @param end The end of the subset
102
103        The subset ranges from the token with index @a begin until (but not including) the token with index @a end.
104        If @a begin or @a end are beyond the allowed index, the resulting SubString will be empty.
105    */
106    SubString::SubString(const SubString& other, unsigned int begin, unsigned int end)
107    {
108        for (unsigned int i = begin; i < std::min(other.size(), end); ++i)
109        {
110            this->tokens_.push_back(other[i]);
111            this->bTokenInSafemode_.push_back(other.isInSafemode(i));
112        }
113    }
114
115    /**
116        @brief Creates a SubString from a count and values set.
117        @param argc The number of arguments
118        @param argv An array of pointers to the arguments
119    */
120    SubString::SubString(unsigned int argc, const char** argv)
121    {
122        for(unsigned int i = 0; i < argc; ++i)
123        {
124            this->tokens_.push_back(std::string(argv[i]));
125            this->bTokenInSafemode_.push_back(false);
126        }
127    }
128
129    /**
130        @brief Destructor
131    */
132    SubString::~SubString()
133    { }
134
135    /**
136        @brief Stores the tokens of @a other in this SubString
137        @return This SubString.
138    */
139    SubString& SubString::operator=(const SubString& other)
140    {
141        this->tokens_ = other.tokens_;
142        this->bTokenInSafemode_ = other.bTokenInSafemode_;
143        return *this;
144    }
145
146    /**
147        @brief Compares this SubString to another SubString and returns true if they contain the same values.
148    */
149    bool SubString::operator==(const SubString& other) const
150    {
151        return ((this->tokens_ == other.tokens_) && (this->bTokenInSafemode_ == other.bTokenInSafemode_));
152    }
153
154    /**
155        @copydoc operator==
156    */
157    bool SubString::compare(const SubString& other) const
158    {
159        return (*this == other);
160    }
161
162    /**
163        @brief Compares this SubString to another SubString and returns true if the first @a length values match.
164        @param other The other SubString
165        @param length How many tokens to compare
166    */
167    bool SubString::compare(const SubString& other, unsigned int length) const
168    {
169        if (length > this->size() || length > other.size())
170            return false;
171
172        for (unsigned int i = 0; i < length; ++i)
173            if ((this->tokens_[i] != other.tokens_[i]) || (this->bTokenInSafemode_[i] != other.bTokenInSafemode_[i]))
174                return false;
175        return true;
176    }
177
178    /**
179        @brief Concatenates the tokens of two SubStrings and returns the resulting new SubString
180        @return A new SubString that contains the tokens of this and the other SubString
181    */
182    SubString SubString::operator+(const SubString& other) const
183    {
184        return SubString(*this) += other;
185    }
186
187    /**
188        @brief Appends the tokens of @a other to this SubString
189        @return This SubString
190    */
191    SubString& SubString::operator+=(const SubString& other)
192    {
193        for (unsigned int i = 0; i < other.size(); ++i)
194        {
195            this->tokens_.push_back(other[i]);
196            this->bTokenInSafemode_.push_back(other.isInSafemode(i));
197        }
198        return *this;
199    }
200
201    /**
202        @copydoc SubString(const std::string&,const std::string&,const std::string&,bool,char,bool,char,bool,char,char,bool,char)
203    */
204    unsigned int SubString::split(const std::string& line,
205                                  const std::string& delimiters, const std::string& delimiterNeighbours, bool bAllowEmptyEntries,
206                                  char escapeChar, bool bRemoveEscapeChar, char safemodeChar, bool bRemoveSafemodeChar,
207                                  char openparenthesisChar, char closeparenthesisChar, bool bRemoveParenthesisChars, char commentChar)
208    {
209        this->tokens_.clear();
210        this->bTokenInSafemode_.clear();
211        SubString::splitLine(this->tokens_, this->bTokenInSafemode_, line, delimiters, delimiterNeighbours, bAllowEmptyEntries, escapeChar, bRemoveEscapeChar, safemodeChar, bRemoveSafemodeChar, openparenthesisChar, closeparenthesisChar, bRemoveParenthesisChars, commentChar);
212        return this->tokens_.size();
213    }
214
215    /**
216        @brief Joins the tokens of this SubString using the given delimiter and returns a string.
217        @param delimiter This delimiter will be placed between each two tokens
218        @return The joined string.
219    */
220    std::string SubString::join(const std::string& delimiter) const
221    {
222        if (!this->tokens_.empty())
223        {
224            std::string retVal = this->tokens_[0];
225            for (unsigned int i = 1; i < this->tokens_.size(); ++i)
226                retVal += delimiter + this->tokens_[i];
227            return retVal;
228        }
229        else
230            return "";
231    }
232
233    /**
234        @brief Creates a subset of this SubString.
235        @param begin The beginning of the subset
236        @return A new SubString containing the defined subset.
237
238        The subset ranges from the token with index @a begin to the end of the tokens.
239        If @a begin is greater than the greatest index, the new SubString will be empty.
240
241        This function is added for your convenience, and does the same as
242        SubString::SubString(const SubString& other, unsigned int begin)
243    */
244    SubString SubString::subSet(unsigned int begin) const
245    {
246        return SubString(*this, begin);
247    }
248
249    /**
250        @brief Creates a subset of this SubString.
251        @param begin The beginning of the subset
252        @param end The ending of the subset
253        @return A new SubString containing the defined subset.
254
255        The subset ranges from the token with index @a begin until (but not including) the token with index @a end.
256        If @a begin or @a end are beyond the allowed index, the resulting SubString will be empty.
257
258        This function is added for your convenience, and does the same as
259        SubString::SubString(const SubString& other, unsigned int begin, unsigned int end)
260    */
261    SubString SubString::subSet(unsigned int begin, unsigned int end) const
262    {
263        return SubString(*this, begin, end);
264    }
265
266    /**
267        @copydoc SubString(const std::string&,const std::string&,const std::string&,bool,char,bool,char,bool,char,char,bool,char)
268        @param tokens The array, where the splitted strings will be stored in
269        @param bTokenInSafemode A vector wich stores for each character of the string if it is in safemode or not
270        @param start_state The internal state of the parser
271
272        This is the actual splitting algorithm from Clemens Wacha.
273        Supports delimiters, escape characters, ignores special characters between safemodeChar and between commentChar and line end "\n".
274
275        Extended by Orxonox to support parenthesis as additional safe-mode.
276    */
277    SubString::SPLIT_LINE_STATE
278    SubString::splitLine(std::vector<std::string>& tokens,
279                         std::vector<bool>& bTokenInSafemode,
280                         const std::string& line,
281                         const std::string& delimiters,
282                         const std::string& delimiterNeighbours,
283                         bool bAllowEmptyEntries,
284                         char escapeChar,
285                         bool bRemoveEscapeChar,
286                         char safemodeChar,
287                         bool bRemoveSafemodeChar,
288                         char openparenthesisChar,
289                         char closeparenthesisChar,
290                         bool bRemoveParenthesisChars,
291                         char commentChar,
292                         SPLIT_LINE_STATE start_state)
293    {
294        SPLIT_LINE_STATE state = start_state;
295        unsigned int i = 0;
296        unsigned int fallBackNeighbours = 0;
297
298        std::string token;
299        bool inSafemode = false;
300
301        if(start_state != SL_NORMAL && tokens.size() > 0)
302        {
303            token = tokens[tokens.size()-1];
304            tokens.pop_back();
305        }
306        if(start_state != SL_NORMAL && bTokenInSafemode.size() > 0)
307        {
308            inSafemode = bTokenInSafemode[bTokenInSafemode.size()-1];
309            bTokenInSafemode.pop_back();
310        }
311
312        while(i < line.size())
313        {
314            switch(state)
315            {
316            case SL_NORMAL:
317                if(line[i] == escapeChar)
318                {
319                    state = SL_ESCAPE;
320                    if (!bRemoveEscapeChar)
321                        token += line[i];
322                    fallBackNeighbours = 0;
323                }
324                else if(line[i] == safemodeChar)
325                {
326                    state = SL_SAFEMODE;
327                    inSafemode = true;
328                    if (!bRemoveSafemodeChar)
329                        token += line[i];
330                    fallBackNeighbours = 0;
331                }
332                else if(line[i] == openparenthesisChar)
333                {
334                    state = SL_PARENTHESES;
335                    inSafemode = true;
336                    if (!bRemoveParenthesisChars)
337                        token += line[i];
338                    fallBackNeighbours = 0;
339                }
340                else if(line[i] == commentChar)
341                {
342                    if (fallBackNeighbours > 0)
343                        token = token.substr(0, token.size() - fallBackNeighbours);
344                    fallBackNeighbours = 0;
345                    // FINISH
346                    if(bAllowEmptyEntries || token.size() > 0)
347                    {
348                        tokens.push_back(token);
349                        token.clear();
350                        bTokenInSafemode.push_back(inSafemode);
351                        inSafemode = false;
352                    }
353                    token += line[i];       // EAT
354                    state = SL_COMMENT;
355                }
356                else if(delimiters.find(line[i]) != std::string::npos)
357                {
358                    // line[i] is a delimiter
359                    if (fallBackNeighbours > 0)
360                        token = token.substr(0, token.size() - fallBackNeighbours);
361                    fallBackNeighbours = 0;
362                    // FINISH
363                    if(bAllowEmptyEntries || token.size() > 0)
364                    {
365                        tokens.push_back(token);
366                        token.clear();
367                        bTokenInSafemode.push_back(inSafemode);
368                        inSafemode = false;
369                    }
370                    state = SL_NORMAL;
371                }
372                else
373                {
374                    if (delimiterNeighbours.find(line[i]) != std::string::npos)
375                    {
376                        if (token.size() > 0)
377                            ++fallBackNeighbours;
378                        else
379                        {
380                            ++i;
381                            continue;
382                        }
383                    }
384                    else
385                        fallBackNeighbours = 0;
386                    token += line[i];       // EAT
387                }
388                break;
389            case SL_ESCAPE:
390                if (!bRemoveSafemodeChar)
391                    token += line[i];
392                else
393                {
394                    if(line[i] == 'n') token += '\n';
395                    else if(line[i] == 't') token += '\t';
396                    else if(line[i] == 'v') token += '\v';
397                    else if(line[i] == 'b') token += '\b';
398                    else if(line[i] == 'r') token += '\r';
399                    else if(line[i] == 'f') token += '\f';
400                    else if(line[i] == 'a') token += '\a';
401                    else if(line[i] == '?') token += '\?';
402                    else token += line[i];  // EAT
403                }
404                state = SL_NORMAL;
405                break;
406            case SL_SAFEMODE:
407                if(line[i] == safemodeChar)
408                {
409                    state = SL_NORMAL;
410                    if (!bRemoveSafemodeChar)
411                        token += line[i];
412                }
413                else if(line[i] == escapeChar)
414                {
415                    state = SL_SAFEESCAPE;
416                }
417                else
418                {
419                    token += line[i];       // EAT
420                }
421                break;
422
423            case SL_SAFEESCAPE:
424                if(line[i] == 'n') token += '\n';
425                else if(line[i] == 't') token += '\t';
426                else if(line[i] == 'v') token += '\v';
427                else if(line[i] == 'b') token += '\b';
428                else if(line[i] == 'r') token += '\r';
429                else if(line[i] == 'f') token += '\f';
430                else if(line[i] == 'a') token += '\a';
431                else if(line[i] == '?') token += '\?';
432                else token += line[i];  // EAT
433                state = SL_SAFEMODE;
434                break;
435
436            case SL_PARENTHESES:
437                if(line[i] == closeparenthesisChar)
438                {
439                    state = SL_NORMAL;
440                    if (!bRemoveParenthesisChars)
441                        token += line[i];
442                }
443                else if(line[i] == escapeChar)
444                {
445                    state = SL_PARENTHESESESCAPE;
446                }
447                else
448                {
449                    token += line[i];       // EAT
450                }
451                break;
452
453            case SL_PARENTHESESESCAPE:
454                if(line[i] == 'n') token += '\n';
455                else if(line[i] == 't') token += '\t';
456                else if(line[i] == 'v') token += '\v';
457                else if(line[i] == 'b') token += '\b';
458                else if(line[i] == 'r') token += '\r';
459                else if(line[i] == 'f') token += '\f';
460                else if(line[i] == 'a') token += '\a';
461                else if(line[i] == '?') token += '\?';
462                else token += line[i];  // EAT
463                state = SL_PARENTHESES;
464                break;
465
466            case SL_COMMENT:
467                if(line[i] == '\n')
468                {
469                    // FINISH
470                    if(token.size() > 0)
471                    {
472                        tokens.push_back(token);
473                        token.clear();
474                        bTokenInSafemode.push_back(inSafemode);
475                        inSafemode = false;
476                    }
477                    state = SL_NORMAL;
478                }
479                else
480                {
481                    token += line[i];       // EAT
482                }
483                break;
484
485            default:
486                // nothing
487                break;
488            }
489            ++i;
490        }
491
492        // FINISH
493        if (fallBackNeighbours > 0)
494            token = token.substr(0, token.size() - fallBackNeighbours);
495        if(bAllowEmptyEntries || token.size() > 0)
496        {
497            tokens.push_back(token);
498            token.clear();
499            bTokenInSafemode.push_back(inSafemode);
500            inSafemode = false;
501        }
502        return(state);
503    }
504
505    /**
506        @brief Some nice debug information about this SubString.
507    */
508    void SubString::debug() const
509    {
510        COUT(0) << "Substring-information::count=" << this->tokens_.size() << " ::";
511        for (unsigned int i = 0; i < this->tokens_.size(); ++i)
512            COUT(0) << "s" << i << "='" << this->tokens_[i].c_str() << "'::";
513        COUT(0) << std::endl;
514    }
515}
Note: See TracBrowser for help on using the repository browser.