1 // Copyright (C) 2002-2012 Nikolaus Gebhardt
\r
2 // This file is part of the "Irrlicht Engine" and the "irrXML" project.
\r
3 // For conditions of distribution and use, see copyright notice in irrlicht.h and irrXML.h
\r
5 #ifndef __IRR_STRING_H_INCLUDED__
\r
6 #define __IRR_STRING_H_INCLUDED__
\r
8 #include "irrTypes.h"
\r
9 #include "irrAllocator.h"
\r
10 #include "irrMath.h"
\r
20 //! Very simple string class with some useful features.
\r
21 /** string<c8> and string<wchar_t> both accept Unicode AND ASCII/Latin-1,
\r
22 so you can assign Unicode to string<c8> and ASCII/Latin-1 to string<wchar_t>
\r
23 (and the other way round) if you want to.
\r
25 However, note that the conversation between both is not done using any encoding.
\r
26 This means that c8 strings are treated as ASCII/Latin-1, not UTF-8, and
\r
27 are simply expanded to the equivalent wchar_t, while Unicode/wchar_t
\r
28 characters are truncated to 8-bit ASCII/Latin-1 characters, discarding all
\r
29 other information in the wchar_t.
\r
31 Helper functions for converting between UTF-8 and wchar_t are provided
\r
32 outside the string class for explicit use.
\r
35 // forward declarations
\r
36 template <typename T, typename TAlloc = irrAllocator<T> >
\r
38 static size_t multibyteToWString(string<wchar_t>& destination, const char* source, u32 sourceSize);
\r
39 inline s32 isdigit(s32 c);
\r
43 IRR_LOCALE_ANSI = 0,
\r
44 IRR_LOCALE_GERMAN = 1
\r
47 static eLocaleID locale_current = IRR_LOCALE_ANSI;
\r
48 static inline void locale_set ( eLocaleID id )
\r
50 locale_current = id;
\r
53 //! Returns a character converted to lower case
\r
54 static inline u32 locale_lower ( u32 x )
\r
56 switch ( locale_current )
\r
58 case IRR_LOCALE_GERMAN:
\r
59 case IRR_LOCALE_ANSI:
\r
63 return x >= 'A' && x <= 'Z' ? x + 0x20 : x;
\r
66 //! Returns a character converted to upper case
\r
67 static inline u32 locale_upper ( u32 x )
\r
69 switch ( locale_current )
\r
71 case IRR_LOCALE_GERMAN:
\r
72 case IRR_LOCALE_ANSI:
\r
77 return x >= 'a' && x <= 'z' ? x + ( 'A' - 'a' ) : x;
\r
80 //! Convert this utf-8-encoded string to the platform's wchar.
\r
81 /** The resulting string is always NULL-terminated and well-formed.
\r
82 \param len The size of the output buffer in bytes.
\r
84 IRRLICHT_API void utf8ToWchar(const char *in, wchar_t *out, const u64 len);
\r
86 //! Convert this wchar string to utf-8.
\r
87 /** The resulting string is always NULL-terminated and well-formed.
\r
88 \param len The size of the output buffer in bytes.
\r
90 IRRLICHT_API void wcharToUtf8(const wchar_t *in, char *out, const u64 len);
\r
93 template <typename T, typename TAlloc>
\r
98 typedef T char_type;
\r
100 //! Default constructor
\r
102 : array(0), allocated(1), used(1)
\r
104 array = allocator.allocate(1); // new T[1];
\r
110 string(const string<T,TAlloc>& other)
\r
111 : array(0), allocated(0), used(0)
\r
116 //! Constructor from other string types
\r
117 template <class B, class A>
\r
118 string(const string<B, A>& other)
\r
119 : array(0), allocated(0), used(0)
\r
125 //! Constructs a string from a float
\r
126 explicit string(const double number)
\r
127 : array(0), allocated(0), used(0)
\r
130 snprintf_irr(tmpbuf, 255, "%0.6f", number);
\r
135 //! Constructs a string from an int
\r
136 explicit string(int number)
\r
137 : array(0), allocated(0), used(0)
\r
139 // store if negative and make positive
\r
141 bool negative = false;
\r
148 // temporary buffer for 16 numbers
\r
153 // special case '0'
\r
158 *this = &tmpbuf[14];
\r
164 while(number && idx)
\r
167 tmpbuf[idx] = (c8)('0' + (number % 10));
\r
179 *this = &tmpbuf[idx];
\r
183 //! Constructs a string from an unsigned int
\r
184 explicit string(unsigned int number)
\r
185 : array(0), allocated(0), used(0)
\r
187 // temporary buffer for 16 numbers
\r
192 // special case '0'
\r
197 *this = &tmpbuf[14];
\r
203 while(number && idx)
\r
206 tmpbuf[idx] = (c8)('0' + (number % 10));
\r
210 *this = &tmpbuf[idx];
\r
214 //! Constructs a string from a long
\r
215 explicit string(long number)
\r
216 : array(0), allocated(0), used(0)
\r
218 // store if negative and make positive
\r
220 bool negative = false;
\r
227 // temporary buffer for 16 numbers
\r
232 // special case '0'
\r
237 *this = &tmpbuf[14];
\r
243 while(number && idx)
\r
246 tmpbuf[idx] = (c8)('0' + (number % 10));
\r
258 *this = &tmpbuf[idx];
\r
262 //! Constructs a string from an unsigned long
\r
263 explicit string(unsigned long number)
\r
264 : array(0), allocated(0), used(0)
\r
266 // temporary buffer for 16 numbers
\r
271 // special case '0'
\r
276 *this = &tmpbuf[14];
\r
282 while(number && idx)
\r
285 tmpbuf[idx] = (c8)('0' + (number % 10));
\r
289 *this = &tmpbuf[idx];
\r
293 //! Constructor for copying a string from a pointer with a given length
\r
295 string(const B* const c, u32 length)
\r
296 : array(0), allocated(0), used(0)
\r
300 // correctly init the string to an empty one
\r
305 allocated = used = length+1;
\r
306 array = allocator.allocate(used); // new T[used];
\r
308 for (u32 l = 0; l<length; ++l)
\r
309 array[l] = (T)c[l];
\r
315 //! Constructor for Unicode and ASCII strings
\r
317 string(const B* const c)
\r
318 : array(0), allocated(0), used(0)
\r
327 allocator.deallocate(array); // delete [] array;
\r
331 //! Assignment operator
\r
332 string<T,TAlloc>& operator=(const string<T,TAlloc>& other)
\r
334 if (this == &other)
\r
337 used = other.size()+1;
\r
338 if (used>allocated)
\r
340 allocator.deallocate(array); // delete [] array;
\r
342 array = allocator.allocate(used); //new T[used];
\r
345 const T* p = other.c_str();
\r
346 for (u32 i=0; i<used; ++i, ++p)
\r
352 //! Assignment operator for other string types
\r
353 template <class B, class A>
\r
354 string<T,TAlloc>& operator=(const string<B,A>& other)
\r
356 *this = other.c_str();
\r
361 //! Assignment operator for strings, ASCII and Unicode
\r
363 string<T,TAlloc>& operator=(const B* const c)
\r
369 array = allocator.allocate(1); //new T[1];
\r
377 if ((void*)c == (void*)array)
\r
387 // we'll keep the old string for a while, because the new
\r
388 // string could be a part of the current string.
\r
389 T* oldArray = array;
\r
392 if (used>allocated)
\r
395 array = allocator.allocate(used); //new T[used];
\r
398 for (u32 l = 0; l<len; ++l)
\r
399 array[l] = (T)c[l];
\r
401 if (oldArray != array)
\r
402 allocator.deallocate(oldArray); // delete [] oldArray;
\r
408 //! Append operator for other strings
\r
409 string<T,TAlloc> operator+(const string<T,TAlloc>& other) const
\r
411 string<T,TAlloc> str(*this);
\r
418 //! Append operator for strings, ASCII and Unicode
\r
420 string<T,TAlloc> operator+(const B* const c) const
\r
422 string<T,TAlloc> str(*this);
\r
429 //! Direct access operator
\r
430 T& operator [](const u32 index)
\r
432 _IRR_DEBUG_BREAK_IF(index>=used) // bad index
\r
433 return array[index];
\r
437 //! Direct access operator
\r
438 const T& operator [](const u32 index) const
\r
440 _IRR_DEBUG_BREAK_IF(index>=used) // bad index
\r
441 return array[index];
\r
445 //! Equality operator
\r
446 bool operator==(const T* const str) const
\r
452 for (i=0; array[i] && str[i]; ++i)
\r
453 if (array[i] != str[i])
\r
456 return (!array[i] && !str[i]);
\r
460 //! Equality operator
\r
461 bool operator==(const string<T,TAlloc>& other) const
\r
463 for (u32 i=0; array[i] && other.array[i]; ++i)
\r
464 if (array[i] != other.array[i])
\r
467 return used == other.used;
\r
471 //! Is smaller comparator
\r
472 bool operator<(const string<T,TAlloc>& other) const
\r
474 for (u32 i=0; array[i] && other.array[i]; ++i)
\r
476 const s32 diff = array[i] - other.array[i];
\r
481 return (used < other.used);
\r
485 //! Inequality operator
\r
486 bool operator!=(const T* const str) const
\r
488 return !(*this == str);
\r
492 //! Inequality operator
\r
493 bool operator!=(const string<T,TAlloc>& other) const
\r
495 return !(*this == other);
\r
499 //! Returns length of the string's content
\r
500 /** \return Length of the string's content in characters, excluding
\r
501 the trailing NUL. */
\r
507 //! Informs if the string is empty or not.
\r
508 //! \return True if the string is empty, false if not.
\r
511 return (size() == 0);
\r
514 void clear(bool releaseMemory=true)
\r
516 if ( releaseMemory )
\r
524 //! Returns character string
\r
525 /** \return pointer to C-style NUL terminated string. */
\r
526 const T* c_str() const
\r
532 //! Makes the string lower case.
\r
533 string<T,TAlloc>& make_lower()
\r
535 for (u32 i=0; array[i]; ++i)
\r
536 array[i] = locale_lower ( array[i] );
\r
541 //! Makes the string upper case.
\r
542 string<T,TAlloc>& make_upper()
\r
544 for (u32 i=0; array[i]; ++i)
\r
545 array[i] = locale_upper ( array[i] );
\r
550 //! Compares the strings ignoring case.
\r
551 /** \param other: Other string to compare.
\r
552 \return True if the strings are equal ignoring case. */
\r
553 bool equals_ignore_case(const string<T,TAlloc>& other) const
\r
555 for(u32 i=0; array[i] && other[i]; ++i)
\r
556 if (locale_lower( array[i]) != locale_lower(other[i]))
\r
559 return used == other.used;
\r
562 //! Compares the strings ignoring case.
\r
563 /** \param other: Other string to compare.
\r
564 \param sourcePos: where to start to compare in the string
\r
565 \return True if the strings are equal ignoring case. */
\r
566 bool equals_substring_ignore_case(const string<T,TAlloc>&other, const s32 sourcePos = 0 ) const
\r
568 if ( (u32) sourcePos >= used )
\r
572 for( i=0; array[sourcePos + i] && other[i]; ++i)
\r
573 if (locale_lower( array[sourcePos + i]) != locale_lower(other[i]))
\r
576 return array[sourcePos + i] == 0 && other[i] == 0;
\r
580 //! Compares the strings ignoring case.
\r
581 /** \param other: Other string to compare.
\r
582 \return True if this string is smaller ignoring case. */
\r
583 bool lower_ignore_case(const string<T,TAlloc>& other) const
\r
585 for(u32 i=0; array[i] && other.array[i]; ++i)
\r
587 s32 diff = (s32) locale_lower ( array[i] ) - (s32) locale_lower ( other.array[i] );
\r
592 return used < other.used;
\r
596 //! compares the first n characters of the strings
\r
597 /** \param other Other string to compare.
\r
598 \param n Number of characters to compare
\r
599 \return True if the n first characters of both strings are equal. */
\r
600 bool equalsn(const string<T,TAlloc>& other, u32 n) const
\r
603 for(i=0; i < n && array[i] && other[i]; ++i)
\r
604 if (array[i] != other[i])
\r
607 // if one (or both) of the strings was smaller then they
\r
608 // are only equal if they have the same length
\r
609 return (i == n) || (used == other.used);
\r
613 //! compares the first n characters of the strings
\r
614 /** \param str Other string to compare.
\r
615 \param n Number of characters to compare
\r
616 \return True if the n first characters of both strings are equal. */
\r
617 bool equalsn(const T* const str, u32 n) const
\r
622 for(i=0; i < n && array[i] && str[i]; ++i)
\r
623 if (array[i] != str[i])
\r
626 // if one (or both) of the strings was smaller then they
\r
627 // are only equal if they have the same length
\r
628 return (i == n) || (array[i] == 0 && str[i] == 0);
\r
632 //! Appends a character to this string
\r
633 /** \param character: Character to append. */
\r
634 string<T,TAlloc>& append(T character)
\r
636 if (used + 1 > allocated)
\r
637 reallocate(used + 1);
\r
641 array[used-2] = character;
\r
648 //! Appends a char string to this string
\r
649 /** \param other: Char string to append. */
\r
650 /** \param length: The length of the string to append. */
\r
651 string<T,TAlloc>& append(const T* const other, u32 length=0xffffffff)
\r
657 const T* p = other;
\r
666 if (used + len > allocated)
\r
667 reallocate(used + len);
\r
672 for (u32 l=0; l<len; ++l)
\r
673 array[l+used] = *(other+l);
\r
681 //! Appends a string to this string
\r
682 /** \param other: String to append. */
\r
683 string<T,TAlloc>& append(const string<T,TAlloc>& other)
\r
685 if (other.size() == 0)
\r
689 const u32 len = other.size()+1;
\r
691 if (used + len > allocated)
\r
692 reallocate(used + len);
\r
694 for (u32 l=0; l<len; ++l)
\r
695 array[used+l] = other[l];
\r
703 //! Appends a string of the length l to this string.
\r
704 /** \param other: other String to append to this string.
\r
705 \param length: How much characters of the other string to add to this one. */
\r
706 string<T,TAlloc>& append(const string<T,TAlloc>& other, u32 length)
\r
708 if (other.size() == 0)
\r
711 if (other.size() < length)
\r
717 if (used + length > allocated)
\r
718 reallocate(used + length);
\r
722 for (u32 l=0; l<length; ++l)
\r
723 array[l+used] = other[l];
\r
726 // ensure proper termination
\r
733 //! Insert a certain amount of characters into the string before the given index
\r
734 //\param pos Insert the characters before this index
\r
735 //\param s String to insert. Must be at least of size n
\r
736 //\param n Number of characters from string s to use.
\r
737 string<T,TAlloc>& insert(u32 pos, const char* s, u32 n)
\r
743 // move stuff behind insert point
\r
744 const u32 end = used+n-1;
\r
745 for (u32 i=0; i<used-pos; ++i)
\r
747 array[end-i] = array[end-(i+n)];
\r
751 for (u32 i=0; i<n; ++i)
\r
753 array[pos+i] = s[i];
\r
760 //! Reserves some memory.
\r
761 /** \param count: Amount of characters to reserve. */
\r
762 void reserve(u32 count)
\r
764 if (count < allocated)
\r
771 //! finds first occurrence of character in string
\r
772 /** \param c: Character to search for.
\r
773 \return Position where the character has been found,
\r
774 or -1 if not found. */
\r
775 s32 findFirst(T c) const
\r
777 for (u32 i=0; i<used-1; ++i)
\r
784 //! finds first occurrence of a character of a list in string
\r
785 /** \param c: List of characters to find. For example if the method
\r
786 should find the first occurrence of 'a' or 'b', this parameter should be "ab".
\r
787 \param count: Amount of characters in the list. Usually,
\r
788 this should be strlen(c)
\r
789 \return Position where one of the characters has been found,
\r
790 or -1 if not found. */
\r
791 s32 findFirstChar(const T* const c, u32 count=1) const
\r
796 for (u32 i=0; i<used-1; ++i)
\r
797 for (u32 j=0; j<count; ++j)
\r
798 if (array[i] == c[j])
\r
805 //! Finds first position of a character not in a given list.
\r
806 /** \param c: List of characters not to find. For example if the method
\r
807 should find the first occurrence of a character not 'a' or 'b', this parameter should be "ab".
\r
808 \param count: Amount of characters in the list. Usually,
\r
809 this should be strlen(c)
\r
810 \return Position where the character has been found,
\r
811 or -1 if not found. */
\r
813 s32 findFirstCharNotInList(const B* const c, u32 count=1) const
\r
818 for (u32 i=0; i<used-1; ++i)
\r
821 for (j=0; j<count; ++j)
\r
822 if (array[i] == c[j])
\r
832 //! Finds last position of a character not in a given list.
\r
833 /** \param c: List of characters not to find. For example if the method
\r
834 should find the first occurrence of a character not 'a' or 'b', this parameter should be "ab".
\r
835 \param count: Amount of characters in the list. Usually,
\r
836 this should be strlen(c)
\r
837 \return Position where the character has been found,
\r
838 or -1 if not found. */
\r
840 s32 findLastCharNotInList(const B* const c, u32 count=1) const
\r
845 for (s32 i=(s32)(used-2); i>=0; --i)
\r
848 for (j=0; j<count; ++j)
\r
849 if (array[i] == c[j])
\r
859 //! finds next occurrence of character in string
\r
860 /** \param c: Character to search for.
\r
861 \param startPos: Position in string to start searching.
\r
862 \return Position where the character has been found,
\r
863 or -1 if not found. */
\r
864 s32 findNext(T c, u32 startPos) const
\r
866 for (u32 i=startPos; i<used-1; ++i)
\r
874 //! finds last occurrence of character in string
\r
875 /** \param c: Character to search for.
\r
876 \param start: start to search reverse ( default = -1, on end )
\r
877 \return Position where the character has been found,
\r
878 or -1 if not found. */
\r
879 s32 findLast(T c, s32 start = -1) const
\r
881 start = core::clamp ( start < 0 ? (s32)(used) - 2 : start, 0, (s32)(used) - 2 );
\r
882 for (s32 i=start; i>=0; --i)
\r
889 //! finds last occurrence of a character of a list in string
\r
890 /** \param c: List of strings to find. For example if the method
\r
891 should find the last occurrence of 'a' or 'b', this parameter should be "ab".
\r
892 \param count: Amount of characters in the list. Usually,
\r
893 this should be strlen(c)
\r
894 \return Position where one of the characters has been found,
\r
895 or -1 if not found. */
\r
896 s32 findLastChar(const T* const c, u32 count=1) const
\r
901 for (s32 i=(s32)used-2; i>=0; --i)
\r
902 for (u32 j=0; j<count; ++j)
\r
903 if (array[i] == c[j])
\r
910 //! finds another string in this string
\r
911 /** \param str: Another string
\r
912 \param start: Start position of the search
\r
913 \return Positions where the string has been found,
\r
914 or -1 if not found. */
\r
916 s32 find(const B* const str, const u32 start = 0) const
\r
928 for (u32 i=start; i<used-len; ++i)
\r
932 while(str[j] && array[i+j] == str[j])
\r
944 //! Returns a substring
\r
945 /** \param begin Start of substring.
\r
946 \param length Length of substring.
\r
947 \param make_lower copy only lower case */
\r
948 string<T> subString(u32 begin, s32 length, bool make_lower = false ) const
\r
950 // if start after string
\r
951 // or no proper substring length
\r
952 if ((length <= 0) || (begin>=size()))
\r
953 return string<T>("");
\r
954 // clamp length to maximal value
\r
955 if ((length+begin) > size())
\r
956 length = size()-begin;
\r
959 o.reserve(length+1);
\r
963 for (s32 i=0; i<length; ++i)
\r
964 o.array[i] = array[i+begin];
\r
968 for (s32 i=0; i<length; ++i)
\r
969 o.array[i] = locale_lower ( array[i+begin] );
\r
972 o.array[length] = 0;
\r
973 o.used = length + 1;
\r
979 //! Appends a character to this string
\r
980 /** \param c Character to append. */
\r
981 string<T,TAlloc>& operator += (T c)
\r
988 //! Appends a char string to this string
\r
989 /** \param c Char string to append. */
\r
990 string<T,TAlloc>& operator += (const T* const c)
\r
997 //! Appends a string to this string
\r
998 /** \param other String to append. */
\r
999 string<T,TAlloc>& operator += (const string<T,TAlloc>& other)
\r
1006 //! Appends a string representation of a number to this string
\r
1007 /** \param i Number to append. */
\r
1008 string<T,TAlloc>& operator += (const int i)
\r
1010 append(string<T,TAlloc>(i));
\r
1015 //! Appends a string representation of a number to this string
\r
1016 /** \param i Number to append. */
\r
1017 string<T,TAlloc>& operator += (const unsigned int i)
\r
1019 append(string<T,TAlloc>(i));
\r
1024 //! Appends a string representation of a number to this string
\r
1025 /** \param i Number to append. */
\r
1026 string<T,TAlloc>& operator += (const long i)
\r
1028 append(string<T,TAlloc>(i));
\r
1033 //! Appends a string representation of a number to this string
\r
1034 /** \param i Number to append. */
\r
1035 string<T,TAlloc>& operator += (const unsigned long i)
\r
1037 append(string<T,TAlloc>(i));
\r
1042 //! Appends a string representation of a number to this string
\r
1043 /** \param i Number to append. */
\r
1044 string<T,TAlloc>& operator += (const double i)
\r
1046 append(string<T,TAlloc>(i));
\r
1051 //! Appends a string representation of a number to this string
\r
1052 /** \param i Number to append. */
\r
1053 string<T,TAlloc>& operator += (const float i)
\r
1055 append(string<T,TAlloc>(i));
\r
1060 //! Replaces all characters of a special type with another one
\r
1061 /** \param toReplace Character to replace.
\r
1062 \param replaceWith Character replacing the old one. */
\r
1063 string<T,TAlloc>& replace(T toReplace, T replaceWith)
\r
1065 for (u32 i=0; i<used-1; ++i)
\r
1066 if (array[i] == toReplace)
\r
1067 array[i] = replaceWith;
\r
1072 //! Replaces all instances of a string with another one.
\r
1073 /** \param toReplace The string to replace.
\r
1074 \param replaceWith The string replacing the old one. */
\r
1075 string<T,TAlloc>& replace(const string<T,TAlloc>& toReplace, const string<T,TAlloc>& replaceWith)
\r
1077 if (toReplace.size() == 0)
\r
1080 const T* other = toReplace.c_str();
\r
1081 const T* replace = replaceWith.c_str();
\r
1082 const u32 other_size = toReplace.size();
\r
1083 const u32 replace_size = replaceWith.size();
\r
1085 // Determine the delta. The algorithm will change depending on the delta.
\r
1086 s32 delta = replace_size - other_size;
\r
1088 // A character for character replace. The string will not shrink or grow.
\r
1092 while ((pos = find(other, pos)) != -1)
\r
1094 for (u32 i = 0; i < replace_size; ++i)
\r
1095 array[pos + i] = replace[i];
\r
1101 // We are going to be removing some characters. The string will shrink.
\r
1105 for (u32 pos = 0; pos < used; ++i, ++pos)
\r
1107 // Is this potentially a match?
\r
1108 if (array[pos] == *other)
\r
1110 // Check to see if we have a match.
\r
1112 for (j = 0; j < other_size; ++j)
\r
1114 if (array[pos + j] != other[j])
\r
1118 // If we have a match, replace characters.
\r
1119 if (j == other_size)
\r
1121 for (j = 0; j < replace_size; ++j)
\r
1122 array[i + j] = replace[j];
\r
1123 i += replace_size - 1;
\r
1124 pos += other_size - 1;
\r
1129 // No match found, just copy characters.
\r
1130 array[i] = array[pos];
\r
1138 // We are going to be adding characters, so the string size will increase.
\r
1139 // Count the number of times toReplace exists in the string so we can allocate the new size.
\r
1140 u32 find_count = 0;
\r
1142 while ((pos = find(other, pos)) != -1)
\r
1148 // Re-allocate the string now, if needed.
\r
1149 u32 len = delta * find_count;
\r
1150 if (used + len > allocated)
\r
1151 reallocate(used + len);
\r
1153 // Start replacing.
\r
1155 while ((pos = find(other, pos)) != -1)
\r
1157 T* start = array + pos + other_size - 1;
\r
1158 T* ptr = array + used - 1;
\r
1159 T* end = array + delta + used -1;
\r
1161 // Shift characters to make room for the string.
\r
1162 while (ptr != start)
\r
1169 // Add the new string now.
\r
1170 for (u32 i = 0; i < replace_size; ++i)
\r
1171 array[pos + i] = replace[i];
\r
1173 pos += replace_size;
\r
1181 //! Removes characters from a string.
\r
1182 /** \param c: Character to remove. */
\r
1183 string<T,TAlloc>& remove(T c)
\r
1187 for (u32 i=0; i<used-1; ++i)
\r
1189 if (array[i] == c)
\r
1195 array[pos++] = array[i];
\r
1198 array[used-1] = 0;
\r
1203 //! Removes a string from the string.
\r
1204 /** \param toRemove: String to remove. */
\r
1205 string<T,TAlloc>& remove(const string<T,TAlloc>& toRemove)
\r
1207 u32 size = toRemove.size();
\r
1212 for (u32 i=0; i<used-1; ++i)
\r
1217 if (array[i + j] != toRemove[j])
\r
1228 array[pos++] = array[i];
\r
1231 array[used-1] = 0;
\r
1236 //! Removes characters from a string.
\r
1237 /** \param characters: Characters to remove. */
\r
1238 string<T,TAlloc>& removeChars(const string<T,TAlloc> & characters)
\r
1240 if (characters.size() == 0)
\r
1245 for (u32 i=0; i<used-1; ++i)
\r
1247 // Don't use characters.findFirst as it finds the \0,
\r
1248 // causing used to become incorrect.
\r
1249 bool docontinue = false;
\r
1250 for (u32 j=0; j<characters.size(); ++j)
\r
1252 if (characters[j] == array[i])
\r
1255 docontinue = true;
\r
1262 array[pos++] = array[i];
\r
1265 array[used-1] = 0;
\r
1271 //! Trims the string.
\r
1272 /** Removes the specified characters (by default, Latin-1 whitespace)
\r
1273 from the beginning and the end of the string. */
\r
1274 string<T,TAlloc>& trim(const string<T,TAlloc> & whitespace = " \t\n\r")
\r
1276 // find start and end of the substring without the specified characters
\r
1277 const s32 begin = findFirstCharNotInList(whitespace.c_str(), whitespace.used);
\r
1279 return (*this="");
\r
1281 const s32 end = findLastCharNotInList(whitespace.c_str(), whitespace.used);
\r
1283 return (*this = subString(begin, (end +1) - begin));
\r
1286 //! Erase 0's at the end when a string ends with a floating point number
\r
1287 /** After generating strings from floats we often end up with strings
\r
1288 ending up with lots of zeros which don't add any value. Erase 'em all.
\r
1289 Examples: "0.100000" becomes "0.1"
\r
1290 "10.000000" becomes "10"
\r
1291 "foo 3.140000" becomes "foo 3.14"
\r
1292 "no_num.000" stays "no_num.000"
\r
1295 string<T,TAlloc>& eraseTrailingFloatZeros(char decimalPoint='.')
\r
1297 s32 i=findLastCharNotInList("0", 1);
\r
1298 if ( i > 0 && (u32)i < used-2 ) // non 0 must be found and not last char (also used is at least 2 when i > 0)
\r
1300 u32 eraseStart=i+1;
\r
1302 if( core::isdigit(array[i]) )
\r
1304 while( --i>0 && core::isdigit(array[i]) );
\r
1305 if ( array[i] == decimalPoint )
\r
1308 else if ( array[i] == decimalPoint )
\r
1313 if ( dot > 0 && core::isdigit(array[dot-1]) )
\r
1315 array[eraseStart] = 0;
\r
1316 used = eraseStart+1;
\r
1322 //! Erases a character from the string.
\r
1323 /** May be slow, because all elements
\r
1324 following after the erased element have to be copied.
\r
1325 \param index: Index of element to be erased. */
\r
1326 string<T,TAlloc>& erase(u32 index)
\r
1328 _IRR_DEBUG_BREAK_IF(index>=used) // access violation
\r
1330 for (u32 i=index+1; i<used; ++i)
\r
1331 array[i-1] = array[i];
\r
1337 //! verify the existing string.
\r
1338 string<T,TAlloc>& validate()
\r
1340 // terminate on existing null
\r
1341 for (u32 i=0; i<allocated; ++i)
\r
1343 if (array[i] == 0)
\r
1351 if ( allocated > 0 )
\r
1354 array[used-1] = 0;
\r
1364 //! gets the last char of a string or null
\r
1365 T lastChar() const
\r
1367 return used > 1 ? array[used-2] : 0;
\r
1370 //! Split string into parts (tokens).
\r
1371 /** This method will split a string at certain delimiter characters
\r
1372 into the container passed in as reference. The type of the container
\r
1373 has to be given as template parameter. It must provide a push_back and
\r
1375 \param ret The result container. Tokens are added, the container is not cleared.
\r
1376 \param delimiter C-style string of delimiter characters
\r
1377 \param countDelimiters Number of delimiter characters
\r
1378 \param ignoreEmptyTokens Flag to avoid empty substrings in the result
\r
1379 container. If two delimiters occur without a character in between or an
\r
1380 empty substring would be placed in the result. Or if a delimiter is the last
\r
1381 character an empty substring would be added at the end. If this flag is set,
\r
1382 only non-empty strings are stored.
\r
1383 \param keepSeparators Flag which allows to add the separator to the
\r
1384 result string. If this flag is true, the concatenation of the
\r
1385 substrings results in the original string. Otherwise, only the
\r
1386 characters between the delimiters are returned.
\r
1387 \return The number of resulting substrings
\r
1389 template<class container>
\r
1390 u32 split(container& ret, const T* const delimiter, u32 countDelimiters=1, bool ignoreEmptyTokens=true, bool keepSeparators=false) const
\r
1395 const u32 oldSize=ret.size();
\r
1397 u32 tokenStartIdx = 0;
\r
1398 for (u32 i=0; i<used; ++i)
\r
1400 for (u32 j=0; j<countDelimiters; ++j)
\r
1402 if (array[i] == delimiter[j])
\r
1404 if (i - tokenStartIdx > 0)
\r
1405 ret.push_back(string<T,TAlloc>(&array[tokenStartIdx], i - tokenStartIdx));
\r
1406 else if ( !ignoreEmptyTokens )
\r
1407 ret.push_back(string<T,TAlloc>());
\r
1408 if ( keepSeparators )
\r
1410 ret.push_back(string<T,TAlloc>(&array[i], 1));
\r
1413 tokenStartIdx = i+1;
\r
1418 if ((used - 1) > tokenStartIdx)
\r
1419 ret.push_back(string<T,TAlloc>(&array[tokenStartIdx], (used - 1) - tokenStartIdx));
\r
1420 else if ( !ignoreEmptyTokens )
\r
1421 ret.push_back(string<T,TAlloc>());
\r
1423 return ret.size()-oldSize;
\r
1426 friend size_t multibyteToWString(string<wchar_t>& destination, const char* source, u32 sourceSize);
\r
1430 //! Reallocate the array, make it bigger or smaller
\r
1431 void reallocate(u32 new_size)
\r
1433 T* old_array = array;
\r
1435 array = allocator.allocate(new_size); //new T[new_size];
\r
1436 allocated = new_size;
\r
1438 const u32 amount = used < new_size ? used : new_size;
\r
1439 for (u32 i=0; i<amount; ++i)
\r
1440 array[i] = old_array[i];
\r
1442 if (allocated < used)
\r
1445 allocator.deallocate(old_array); // delete [] old_array;
\r
1448 //--- member variables
\r
1457 //! Typedef for character strings
\r
1458 typedef string<c8> stringc;
\r
1460 //! Typedef for wide character strings
\r
1461 typedef string<wchar_t> stringw;
\r
1463 //! Convert multibyte string to wide-character string
\r
1464 /** Wrapper around mbstowcs from standard library, but directly using Irrlicht string class.
\r
1465 What the function does exactly depends on the LC_CTYPE of the current c locale.
\r
1466 \param destination Wide-character string receiving the converted source
\r
1467 \param source multibyte string
\r
1468 \return The number of wide characters written to destination, not including the eventual terminating null character or -1 when conversion failed */
\r
1469 static inline size_t multibyteToWString(string<wchar_t>& destination, const core::string<c8>& source)
\r
1471 return multibyteToWString(destination, source.c_str(), (u32)source.size());
\r
1474 //! Convert multibyte string to wide-character string
\r
1475 /** Wrapper around mbstowcs from standard library, but directly writing to Irrlicht string class.
\r
1476 What the function does exactly depends on the LC_CTYPE of the current c locale.
\r
1477 \param destination Wide-character string receiving the converted source
\r
1478 \param source multibyte string
\r
1479 \return The number of wide characters written to destination, not including the eventual terminating null character or -1 when conversion failed. */
\r
1480 static inline size_t multibyteToWString(string<wchar_t>& destination, const char* source)
\r
1482 const u32 s = source ? (u32)strlen(source) : 0;
\r
1483 return multibyteToWString(destination, source, s);
\r
1486 //! Internally used by the other multibyteToWString functions
\r
1487 static size_t multibyteToWString(string<wchar_t>& destination, const char* source, u32 sourceSize)
\r
1491 destination.reserve(sourceSize+1);
\r
1492 #if defined(_MSC_VER)
\r
1493 #pragma warning(push)
\r
1494 #pragma warning(disable: 4996) // 'mbstowcs': This function or variable may be unsafe. Consider using mbstowcs_s instead.
\r
1496 const size_t written = mbstowcs(destination.array, source, (size_t)sourceSize);
\r
1497 #if defined(_MSC_VER)
\r
1498 #pragma warning(pop)
\r
1500 if ( written != (size_t)-1 )
\r
1502 destination.used = (u32)written+1;
\r
1503 destination.array[destination.used-1] = 0;
\r
1507 // Likely character which got converted until the invalid character was encountered are in destination now.
\r
1508 // And it seems even 0-terminated, but I found no documentation anywhere that this (the 0-termination) is guaranteed :-(
\r
1509 destination.clear();
\r
1515 destination.clear();
\r
1521 } // end namespace core
\r
1522 } // end namespace irr
\r