1 // Copyright (C) 2002-2005 Nikolaus Gebhardt 2 // This file is part of the "Irrlicht Engine" and the "irrXML" project. 3 // For conditions of distribution and use, see copyright notice in irrlicht.h and irrXML.h 4 5 #ifndef __IRR_STRING_H_INCLUDED__ 6 #define __IRR_STRING_H_INCLUDED__ 7 8 #include "irrTypes.h" 9 10 namespace irr 11 { 12 namespace core 13 { 14 15 //! Very simple string class with some useful features. 16 /** string<c8> and string<wchar_t> work both with unicode AND ascii, 17 so you can assign unicode to string<c8> and ascii to string<wchar_t> 18 (and the other way round) if your ever would want to. 19 Note that the conversation between both is not done using an encoding. 20 21 Known bugs: 22 Special characters like '�', '�' and '�' are ignored in the 23 methods make_upper, make_lower and equals_ignore_case. 24 */ 25 template <class T> 26 class string 27 { 28 public: 29 30 //! Default constructor string()31 string() 32 : array(0), allocated(1), used(1) 33 { 34 array = new T[1]; 35 array[0] = 0x0; 36 } 37 38 39 40 //! Constructor string(const string<T> & other)41 string(const string<T>& other) 42 : array(0), allocated(0), used(0) 43 { 44 *this = other; 45 } 46 47 48 //! Constructs a string from an int string(int number)49 string(int number) 50 : array(0), allocated(0), used(0) 51 { 52 // store if negative and make positive 53 54 bool negative = false; 55 if (number < 0) 56 { 57 number *= -1; 58 negative = true; 59 } 60 61 // temporary buffer for 16 numbers 62 63 c8 tmpbuf[16]; 64 tmpbuf[15] = 0; 65 s32 idx = 15; 66 67 // special case '0' 68 69 if (!number) 70 { 71 tmpbuf[14] = '0'; 72 *this = &tmpbuf[14]; 73 return; 74 } 75 76 // add numbers 77 78 while(number && idx) 79 { 80 idx--; 81 tmpbuf[idx] = (c8)('0' + (number % 10)); 82 number = number / 10; 83 } 84 85 // add sign 86 87 if (negative) 88 { 89 idx--; 90 tmpbuf[idx] = '-'; 91 } 92 93 *this = &tmpbuf[idx]; 94 } 95 96 97 98 //! Constructor for copying a string from a pointer with a given lenght 99 template <class B> string(const B * c,s32 lenght)100 string(const B* c, s32 lenght) 101 : array(0), allocated(0), used(0) 102 { 103 if (!c) 104 return; 105 106 allocated = used = lenght+1; 107 array = new T[used]; 108 109 for (s32 l = 0; l<lenght; ++l) 110 array[l] = (T)c[l]; 111 112 array[lenght] = 0; 113 } 114 115 116 117 //! Constructor for unicode and ascii strings 118 template <class B> string(const B * c)119 string(const B* c) 120 : array(0),allocated(0), used(0) 121 { 122 *this = c; 123 } 124 125 126 127 //! destructor ~string()128 ~string() 129 { 130 delete [] array; 131 } 132 133 134 135 //! Assignment operator 136 string<T>& operator=(const string<T>& other) 137 { 138 if (this == &other) 139 return *this; 140 141 delete [] array; 142 allocated = used = other.size()+1; 143 array = new T[used]; 144 145 const T* p = other.c_str(); 146 for (s32 i=0; i<used; ++i, ++p) 147 array[i] = *p; 148 149 return *this; 150 } 151 152 153 154 //! Assignment operator for strings, ascii and unicode 155 template <class B> 156 string<T>& operator=(const B* c) 157 { 158 if (!c) 159 { 160 if (!array) 161 { 162 array = new T[1]; 163 allocated = 1; 164 used = 1; 165 } 166 array[0] = 0x0; 167 return *this; 168 } 169 170 if ((void*)c == (void*)array) 171 return *this; 172 173 s32 len = 0; 174 const B* p = c; 175 while(*p) 176 { 177 ++len; 178 ++p; 179 } 180 181 // we'll take the old string for a while, because the new string could be 182 // a part of the current string. 183 T* oldArray = array; 184 185 allocated = used = len+1; 186 array = new T[used]; 187 188 for (s32 l = 0; l<len+1; ++l) 189 array[l] = (T)c[l]; 190 191 delete [] oldArray; 192 return *this; 193 } 194 195 //! Add operator for other strings 196 string<T> operator+(const string<T>& other) 197 { 198 string<T> str(*this); 199 str.append(other); 200 201 return str; 202 } 203 204 //! Add operator for strings, ascii and unicode 205 template <class B> 206 string<T> operator+(const B* c) 207 { 208 string<T> str(*this); 209 str.append(c); 210 211 return str; 212 } 213 214 215 216 //! Direct access operator 217 T& operator [](const s32 index) const 218 { 219 _IRR_DEBUG_BREAK_IF(index>=used) // bad index 220 221 return array[index]; 222 } 223 224 225 //! Comparison operator 226 bool operator ==(const T* str) const 227 { 228 int i; 229 for(i=0; array[i] && str[i]; ++i) 230 if (array[i] != str[i]) 231 return false; 232 233 return !array[i] && !str[i]; 234 } 235 236 237 238 //! Comparison operator 239 bool operator ==(const string<T>& other) const 240 { 241 for(s32 i=0; array[i] && other.array[i]; ++i) 242 if (array[i] != other.array[i]) 243 return false; 244 245 return used == other.used; 246 } 247 248 249 250 //! Is smaller operator 251 bool operator <(const string<T>& other) const 252 { 253 for(s32 i=0; array[i] && other.array[i]; ++i) 254 if (array[i] != other.array[i]) 255 return (array[i] < other.array[i]); 256 257 return used < other.used; 258 } 259 260 261 262 //! Equals not operator 263 bool operator !=(const string<T>& other) const 264 { 265 return !(*this == other); 266 } 267 268 269 270 //! Returns length of string 271 /** \return Returns length of the string in characters. */ size()272 s32 size() const 273 { 274 return used-1; 275 } 276 277 278 279 //! Returns character string 280 /** \return Returns pointer to C-style zero terminated string. */ c_str()281 const T* c_str() const 282 { 283 return array; 284 } 285 286 287 288 //! Makes the string lower case. make_lower()289 void make_lower() 290 { 291 const T A = (T)'A'; 292 const T Z = (T)'Z'; 293 const T diff = (T)'a' - A; 294 295 for (s32 i=0; i<used; ++i) 296 { 297 if (array[i]>=A && array[i]<=Z) 298 array[i] += diff; 299 } 300 } 301 302 303 304 //! Makes the string upper case. make_upper()305 void make_upper() 306 { 307 const T a = (T)'a'; 308 const T z = (T)'z'; 309 const T diff = (T)'A' - a; 310 311 for (s32 i=0; i<used; ++i) 312 { 313 if (array[i]>=a && array[i]<=z) 314 array[i] += diff; 315 } 316 } 317 318 319 320 //! Compares the string ignoring case. 321 /** \param other: Other string to compare. 322 \return Returns true if the string are equal ignoring case. */ equals_ignore_case(const string<T> & other)323 bool equals_ignore_case(const string<T>& other) const 324 { 325 for(s32 i=0; array[i] && other[i]; ++i) 326 if (toLower(array[i]) != toLower(other[i])) 327 return false; 328 329 return used == other.used; 330 } 331 332 333 //! compares the first n characters of the strings equalsn(const string<T> & other,int len)334 bool equalsn(const string<T>& other, int len) 335 { 336 int i; 337 for(i=0; array[i] && other[i] && i < len; ++i) 338 if (array[i] != other[i]) 339 return false; 340 341 // if one (or both) of the strings was smaller then they 342 // are only equal if they have the same lenght 343 return (i == len) || (used == other.used); 344 } 345 346 347 //! compares the first n characters of the strings equalsn(const T * str,int len)348 bool equalsn(const T* str, int len) 349 { 350 int i; 351 for(i=0; array[i] && str[i] && i < len; ++i) 352 if (array[i] != str[i]) 353 return false; 354 355 // if one (or both) of the strings was smaller then they 356 // are only equal if they have the same lenght 357 return (i == len) || (array[i] == 0 && str[i] == 0); 358 } 359 360 361 //! Appends a character to this string 362 /** \param character: Character to append. */ append(T character)363 void append(T character) 364 { 365 if (used + 1 > allocated) 366 reallocate((s32)used + 1); 367 368 used += 1; 369 370 array[used-2] = character; 371 array[used-1] = 0; 372 } 373 374 //! Appends a string to this string 375 /** \param other: String to append. */ append(const string<T> & other)376 void append(const string<T>& other) 377 { 378 --used; 379 380 s32 len = other.size(); 381 382 if (used + len + 1 > allocated) 383 reallocate((s32)used + (s32)len + 1); 384 385 for (s32 l=0; l<len+1; ++l) 386 array[l+used] = other[l]; 387 388 used = used + len + 1; 389 } 390 391 392 //! Appends a string of the length l to this string. 393 /** \param other: other String to append to this string. 394 \param length: How much characters of the other string to add to this one. */ append(const string<T> & other,s32 length)395 void append(const string<T>& other, s32 length) 396 { 397 s32 len = other.size(); 398 399 if (len < length) 400 { 401 append(other); 402 return; 403 } 404 405 len = length; 406 --used; 407 408 if (used + len > allocated) 409 reallocate((s32)used + (s32)len); 410 411 for (s32 l=0; l<len; ++l) 412 array[l+used] = other[l]; 413 414 used = used + len; 415 } 416 417 418 //! Reserves some memory. 419 /** \param count: Amount of characters to reserve. */ reserve(s32 count)420 void reserve(s32 count) 421 { 422 if (count < allocated) 423 return; 424 425 reallocate(count); 426 } 427 428 429 //! finds first occurrence of character in string 430 /** \param c: Character to search for. 431 \return Returns position where the character has been found, 432 or -1 if not found. */ findFirst(T c)433 s32 findFirst(T c) const 434 { 435 for (s32 i=0; i<used; ++i) 436 if (array[i] == c) 437 return i; 438 439 return -1; 440 } 441 442 //! finds first occurrence of a character of a list in string 443 /** \param c: List of strings to find. For example if the method 444 should find the first occurance of 'a' or 'b', this parameter should be "ab". 445 \param count: Amount of characters in the list. Ususally, 446 this should be strlen(ofParameter1) 447 \return Returns position where one of the character has been found, 448 or -1 if not found. */ findFirstChar(T * c,int count)449 s32 findFirstChar(T* c, int count) const 450 { 451 for (s32 i=0; i<used; ++i) 452 for (int j=0; j<count; ++j) 453 if (array[i] == c[j]) 454 return i; 455 456 return -1; 457 } 458 459 460 //! Finds first position of a character not in a given list. 461 /** \param c: List of characters not to find. For example if the method 462 should find the first occurance of a character not 'a' or 'b', this parameter should be "ab". 463 \param count: Amount of characters in the list. Ususally, 464 this should be strlen(ofParameter1) 465 \return Returns position where the character has been found, 466 or -1 if not found. */ 467 template <class B> findFirstCharNotInList(B * c,int count)468 s32 findFirstCharNotInList(B* c, int count) const 469 { 470 for (int i=0; i<used; ++i) 471 { 472 int j; 473 for (j=0; j<count; ++j) 474 if (array[i] == c[j]) 475 break; 476 477 if (j==count) 478 return i; 479 } 480 481 return -1; 482 } 483 484 //! Finds last position of a character not in a given list. 485 /** \param c: List of characters not to find. For example if the method 486 should find the first occurance of a character not 'a' or 'b', this parameter should be "ab". 487 \param count: Amount of characters in the list. Ususally, 488 this should be strlen(ofParameter1) 489 \return Returns position where the character has been found, 490 or -1 if not found. */ 491 template <class B> findLastCharNotInList(B * c,int count)492 s32 findLastCharNotInList(B* c, int count) const 493 { 494 for (int i=used-2; i>=0; --i) 495 { 496 int j; 497 for (j=0; j<count; ++j) 498 if (array[i] == c[j]) 499 break; 500 501 if (j==count) 502 return i; 503 } 504 505 return -1; 506 } 507 508 //! finds next occurrence of character in string 509 /** \param c: Character to search for. 510 \param startPos: Position in string to start searching. 511 \return Returns position where the character has been found, 512 or -1 if not found. */ findNext(T c,s32 startPos)513 s32 findNext(T c, s32 startPos) const 514 { 515 for (s32 i=startPos; i<used; ++i) 516 if (array[i] == c) 517 return i; 518 519 return -1; 520 } 521 522 523 //! finds last occurrence of character in string 524 //! \param c: Character to search for. 525 //! \return Returns position where the character has been found, 526 //! or -1 if not found. findLast(T c)527 s32 findLast(T c) const 528 { 529 for (s32 i=used-1; i>=0; --i) 530 if (array[i] == c) 531 return i; 532 533 return -1; 534 } 535 536 537 //! Returns a substring 538 //! \param begin: Start of substring. 539 //! \param length: Length of substring. subString(s32 begin,s32 length)540 string<T> subString(s32 begin, s32 length) 541 { 542 if (length <= 0) 543 return string<T>(""); 544 545 string<T> o; 546 o.reserve(length+1); 547 548 for (s32 i=0; i<length; ++i) 549 o.array[i] = array[i+begin]; 550 551 o.array[length] = 0; 552 o.used = o.allocated; 553 554 return o; 555 } 556 557 558 void operator += (T c) 559 { 560 append(c); 561 } 562 563 void operator += (const string<T>& other) 564 { 565 append(other); 566 } 567 568 void operator += (int i) 569 { 570 append(string<T>(i)); 571 } 572 573 //! replaces all characters of a special type with another one replace(T toReplace,T replaceWith)574 void replace(T toReplace, T replaceWith) 575 { 576 for (s32 i=0; i<used; ++i) 577 if (array[i] == toReplace) 578 array[i] = replaceWith; 579 } 580 581 //! trims the string. 582 /** Removes whitespace from begin and end of the string. */ trim()583 void trim() 584 { 585 const char whitespace[] = " \t\n"; 586 const int whitespacecount = 3; 587 588 // find start and end of real string without whitespace 589 int begin = findFirstCharNotInList(whitespace, whitespacecount); 590 if (begin == -1) 591 return; 592 593 int end = findLastCharNotInList(whitespace, whitespacecount); 594 if (end == -1) 595 return; 596 597 *this = subString(begin, (end +1) - begin); 598 } 599 600 601 //! Erases a character from the string. May be slow, because all elements 602 //! following after the erased element have to be copied. 603 //! \param index: Index of element to be erased. erase(int index)604 void erase(int index) 605 { 606 _IRR_DEBUG_BREAK_IF(index>=used || index<0) // access violation 607 608 for (int i=index+1; i<used; ++i) 609 array[i-1] = array[i]; 610 611 --used; 612 } 613 614 615 616 private: 617 618 //! Returns a character converted to lower case toLower(const T & t)619 T toLower(const T& t) const 620 { 621 if (t>=(T)'A' && t<=(T)'Z') 622 return t + ((T)'a' - (T)'A'); 623 else 624 return t; 625 } 626 627 //! Reallocate the array, make it bigger or smaler reallocate(s32 new_size)628 void reallocate(s32 new_size) 629 { 630 T* old_array = array; 631 632 array = new T[new_size]; 633 allocated = new_size; 634 635 s32 amount = used < new_size ? used : new_size; 636 for (s32 i=0; i<amount; ++i) 637 array[i] = old_array[i]; 638 639 if (allocated < used) 640 used = allocated; 641 642 delete [] old_array; 643 } 644 645 646 //--- member variables 647 648 T* array; 649 s32 allocated; 650 s32 used; 651 }; 652 653 654 //! Typedef for character strings 655 typedef string<irr::c8> stringc; 656 657 //! Typedef for wide character strings 658 typedef string<wchar_t> stringw; 659 660 } // end namespace core 661 } // end namespace irr 662 663 #endif 664 665