// ==========================================================================
// Author: Yee Hsu
// Date: 9/20/2013
// File: String.hpp
//
// Desc: String class that inherits from std::string. This is a header
// only file and can be used standalone. The String class
// implements many useful string functions not existing in the
// std::string library.
// ==========================================================================
#ifndef __STRING_HPP__
#define __STRING_HPP__
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <string>
#include <vector>
#include <sstream>
#include <iomanip>
#include <iostream>
#include <algorithm>
namespace TeraCode
{
class String;
typedef std::vector<String> svector;
typedef unsigned int uint;
class String : public std::string {
public:
// String Constructors
String() : std::string() { }
String( const std::string& s ) : std::string( s ) { }
String( const std::string& s, std::size_t n ) : std::string( s,n ) { }
String( const char *s, std::size_t n ) : std::string( s,n ) { }
String( const char *s ) : std::string( s ) { }
String( std::size_t n, char c ) : std::string ( n,c ) { }
template <class InputIterator>
String( InputIterator _f, InputIterator _l ) : std::string( _f,_l ) { }
String& String::Clone() { return *this; }
// No need Destructor, in fact, do not implement it, since std::string Destructor is non-virtual
// convert String into a number
template <typename T>
T Convert() {
T _r = 0;
if(isint() or isfloat()) {
std::stringstream ss;
ss << *this; ss >> _r;
} return _r;
}
// trim left and right
String String::Trim(char _s = ' ') {
TrimLeft(_s); TrimRight(_s); return *this;
}
String String::TrimLeft(char _s = ' ') {
char* _o = const_cast<char*>(c_str());
char *p = _o; int _t = 0;
do { if (*_o != _s || _t) { _t = 1; *p++ = *_o; } }
while (*_o++ != 0); return *this;
}
String String::TrimRight(char _s = ' ') {
char* _o = const_cast<char*>(c_str());
size_t _l = strlen(_o);
while (*_o && _l) {
char* _e = _o + _l-1;
if( _s == *_e) *_e = 0; else break;
_l = strlen(_o);
} return *this;
}
// take a line and split it into a vector
svector String::Split(const String& _s, uint _ul) {
svector _l; size_t _e, _p = 0;
while(true) {
_e = find(_s,_p);
_l.push_back(substr(_p,_e-_p));
_p = _e + _s.size();
if(_e == std::string::npos) break;
if(_ul != 0) { if(_l.size() >= _ul) { _l.push_back(substr(_p)); break; } }
} return _l;
}
// append a svector to the string
String String::JoinVector(const String& _s, svector& _l) {
for(uint i = 0; i < _l.size(); i++) { append(_s); append(_l[i]);
} return *this;
}
svector String::Tokenize(const String& _d = " ") {
svector _t; std::string::size_type _l = 0;
std::string::size_type _c = find_first_of(_d);
while (_c != npos) {
_t.push_back(substr(_l, _c-_l));
_l = _c+1; _c = find_first_of(_d, _l);
}
}
int String::HashCode() {
int _h = 0; int _l = length();
for (int i = 0; i < _l; i++) _h = 31 * _h + at(i);
return _h;
}
// counts how many times it appears
uint String::Count(const String& _s) const {
uint _t = 0; size_t _e,_p = 0;
while(true) { _e = find(_s,_p); if(_e == std::string::npos) break; _p = _e + _s.size(); _t++; }
return _t;
}
bool String::Equals(const String& _s, bool _case = false) {
if (_case) { String _t = *this; String _c = _s;
return _t.ToLower() == _c.ToLower();
} return *this == _s;
}
bool String::StartsWith(const String& _s) {
if (_s.size() > size()) return false;
return std::equal( _s.begin(), _s.end(), begin() );
}
bool String::EndsWith(const String& _s) {
if (_s.size() > size()) return false;
return std::equal(begin() + size() - _s.size(), end(), _s.begin());
}
bool String::Contains(const String& _s) {
return find(_s) != std::string::npos;
}
String String::InsertBefore(const String& _l, const String _r, bool _all = false) {
return Replace(_l, _r + _l, _all);
}
String String::InsertAfter(const String& _l, const String _r, bool _all = false) {
return Replace(_l, _l + _r, _all);
}
String String::ExtractSubString(const String& _l, const String& _r) {
size_t _bs = 0; size_t _be = length();
if (!_l.empty()) _bs = find(_l) + _l.length();
if (!_r.empty()) _be = find(_r, _bs);
String _s = substr(_bs, _be-_bs);
if (_bs >= 0 && _be >= _bs) return _s; else return "";
}
String String::ExtractSubStringReverse(const String& _l, const String& _r) {
size_t _bs = rfind(_l) + _l.length(); size_t _be = find(_r, _bs);
String _s = substr(_bs, _be-_bs);
if (_bs >= 0 && _be >= _bs) return _s; else return "";
}
String String::RemoveSubString(const String& _s) {
std::string _r = *this; std::string::size_type _p;
while( (_p = _r.find( _s )) != _r.npos )
_r = _r.erase( _p, _s.size() );
return _r;
}
String String::Replace(const String& _s, const String& _r, bool _all = false) {
size_t _p = 0; while ((_p = find(_s, _p)) != std::string::npos) {
replace(_p, _s.length(), _r); _p += _r.length(); if (!_all) break;
} return *this;
}
bool String::IsAlpha() const {
if (empty()) return false;
for (std::string::size_type i=0; i<length(); ++i) {
if (!::isalpha((*this)[i])) return false;
} return true;
}
bool String::IsAlphaNumeric() const {
if (empty()) return false;
for (std::string::size_type i=0; i<length(); ++i) {
if (!::isalnum((*this)[i])) return false;
} return true;
}
bool String::IsLower() const {
if (empty()) return false;
for (std::string::size_type i=0; i<length(); ++i) {
if (!::islower((*this)[i])) return false;
} return true;
}
bool String::IsUpper() const {
if (empty()) return false;
for (std::string::size_type i=0; i<length(); ++i) {
if (!::isupper((*this)[i])) return false;
} return true;
}
bool String::IsSpace() const {
if (empty()) return false;
for (std::string::size_type i=0; i<length(); ++i) {
if (!::isspace((*this)[i])) return false;
} return true;
}
bool String::IsInteger() const {
if (empty()) return false;
return find_first_not_of("0123456789") == std::string::npos;
}
bool String::IsFloat() const {
if (empty()) return false;
if (find_first_not_of("0123456789.") == std::string::npos) {
std::string::size_type _p = find("."); std::string::size_type _r = rfind(".");
if (_p == _r && _p != std::string::npos) return true;
} return false;
}
bool String::ContainDigit() const {
if (empty()) return false;
if (std::string::npos != find_first_of("0123456789")) return true;
else return false; // C++11: return std::any_of(s.begin(), s.end(), ::isdigit);
}
String String::ToLower() {
std::transform(begin(), end(), begin(), ::tolower);
return *this;
}
String String::ToUpper() {
std::transform(begin(), end(), begin(), ::toupper);
return *this;
}
String String::Format(size_t sz, const char* _f, ...) {
va_list _arg; va_start(_arg, _f); char* _s = new char[sz];
if (_s) {
memset(_s, 0, sz); vsnprintf(_s, sz, _f, _arg);
*this = _s; delete [] _s;
} va_end(_arg); return *this;
}
// BAD, obsolete...
String String::Format(const char* _f, ...) {
char *s, ch=0; int n, i=0, m; long l; double d;
std::string sf = _f; std::stringstream ss;
va_list _arg; va_start(_arg, _f); m = sf.length();
while (i<m) { ch = sf.at(i); if (ch == '%') { i++; if (i<m) { ch = sf.at(i); switch(ch) {
case 's': { s = va_arg(_arg, char*); ss << s; } break;
case 'c': { n = va_arg(_arg, int); ss << (char)n; } break;
case 'd': { n = va_arg(_arg, int); ss << (int)n; } break;
case 'l': { l = va_arg(_arg, long); ss << (long)l; } break;
case 'f': { d = va_arg(_arg, double); ss << (float)d; } break;
case 'e': { d = va_arg(_arg, double); ss << (double)d; } break;
case 'X':
case 'x':
{
if (++i<m)
{
ss << std::hex << std::setiosflags (std::ios_base::showbase);
if (ch == 'X') ss << std::setiosflags (std::ios_base::uppercase);
char ch2 = sf.at(i);
if (ch2 == 'c') { n = va_arg(_arg, int); ss << std::hex << (char)n; }
else if (ch2 == 'd') { n = va_arg(_arg, int); ss << std::hex << (int) n; }
else if (ch2 == 'l') { l = va_arg(_arg, long); ss << std::hex << (long)l; }
else ss << '%' << ch << ch2;
ss << std::resetiosflags (std::ios_base::showbase | std::ios_base::uppercase) << std::dec;
}
} break;
case '%': { ss << '%'; } break;
default: { ss << "%" << ch; /* i = m; //get out of loop */ }
} } } else ss << ch; i++; } va_end(_arg); *this = ss.str(); return *this;
}
};
}
#endif