// ************************************************************************** // * This file is part of the FreeFileSync project. It is distributed under * // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2011 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** // #ifndef STRING_TOOLS_HEADER_213458973046 #define STRING_TOOLS_HEADER_213458973046 #include //size_t #include //isspace #include //iswspace #include #include #include #include #include #include "loki/TypeTraits.h" //enhance arbitray string class with useful non-member functions: namespace zen { template size_t cStringLength(const C* input); //strlen() template bool cStringIsWhiteSpace(C ch); template bool cStringIsDigit(C ch); template bool startsWith(const S& str, typename S::value_type prefix); template bool startsWith(const S& str, const typename S::value_type* prefix); template bool startsWith(const S& str, const T& prefix, typename T::value_type dummy = 0); //SFINAE: T must be a "string" template bool endsWith(const S& str, typename S::value_type postfix); template bool endsWith(const S& str, const typename S::value_type* postfix); template bool endsWith(const S& str, const T& postfix, typename T::value_type dummy = 0); //SFINAE: T must be a "string" template S afterLast (const S& str, typename S::value_type ch); //returns the whole string if ch not found template S beforeLast (const S& str, typename S::value_type ch); //returns empty string if ch not found template S afterFirst (const S& str, typename S::value_type ch); //returns empty string if ch not found template S beforeFirst(const S& str, typename S::value_type ch); //returns the whole string if ch not found template std::vector split(const S& str, const T& delimiter); template void truncate(S& str, size_t newLen); template void replace(S& str, const typename S::value_type* old, const typename S::value_type* replacement, bool replaceAll); template void trim(S& str, bool fromLeft = true, bool fromRight = true); //formatted number conversion the C++ way template S toString(const Num& number); template Num toNumber(const S& str); //---------------------- implementation ---------------------- template inline size_t cStringLength(const C* input) //strlen() { const C* iter = input; while (*iter != 0) ++iter; return iter - input; } template <> inline bool cStringIsWhiteSpace(char ch) { const unsigned char usc = ch; //caveat 1: std::isspace() takes an int, but expects an unsigned char return usc < 128 && //caveat 2: some parts of UTF-8 chars are erroneously seen as whitespace, e.g. the a0 from "\xec\x8b\xa0" (MSVC) std::isspace(usc) != 0; } template <> inline bool cStringIsWhiteSpace(wchar_t ch) { return std::iswspace(ch) != 0; //some compilers (e.g. VC++ 6.0) return true for a call to isspace('\xEA'); but no issue with newer versions of MSVC } template <> inline bool cStringIsDigit(char ch) { return std::isdigit(static_cast(ch)) != 0; //caveat: takes an int, but expects an unsigned char } template <> inline bool cStringIsDigit(wchar_t ch) { return std::iswdigit(ch) != 0; } template inline bool startsWith(const S& str, typename S::value_type prefix) { return str.length() != 0 && str.operator[](0) == prefix; } template inline bool startsWith(const S& str, const typename S::value_type* prefix) { const size_t pfLength = cStringLength(prefix); if (str.length() < pfLength) return false; return std::equal(str.c_str(), str.c_str() + pfLength, prefix); } template inline bool startsWith(const S& str, const T& prefix, typename T::value_type) { if (str.length() < prefix.length()) return false; return std::equal(str.c_str(), str.c_str() + prefix.length(), prefix.c_str()); } template inline bool endsWith(const S& str, typename S::value_type postfix) { const size_t len = str.length(); return len != 0 && str.operator[](len - 1) == postfix; } template inline bool endsWith(const S& str, const typename S::value_type* postfix) { const size_t pfLength = cStringLength(postfix); if (str.length() < pfLength) return false; const typename S::value_type* cmpBegin = str.c_str() + str.length() - pfLength; return std::equal(cmpBegin, cmpBegin + pfLength, postfix); } template inline bool endsWith(const S& str, const T& postfix, typename T::value_type) { if (str.length() < postfix.length()) return false; const typename S::value_type* cmpBegin = str.c_str() + str.length() - postfix.length(); return std::equal(cmpBegin, cmpBegin + postfix.length(), postfix.c_str()); } // get all characters after the last occurence of ch // (returns the whole string if ch not found) template inline S afterLast(const S& str, typename S::value_type ch) { const size_t pos = str.rfind(ch); if (pos != S::npos) return S(str.c_str() + pos + 1, str.length() - pos - 1); else return str; } // get all characters before the last occurence of ch // (returns empty string if ch not found) template inline S beforeLast(const S& str, typename S::value_type ch) { const size_t pos = str.rfind(ch); if (pos != S::npos) return S(str.c_str(), pos); //data is non-empty string in this context: else ch would not have been found! else return S(); } //returns empty string if ch not found template inline S afterFirst(const S& str, typename S::value_type ch) { const size_t pos = str.find(ch); if (pos != S::npos) return S(str.c_str() + pos + 1, str.length() - pos - 1); else return S(); } //returns the whole string if ch not found template inline S beforeFirst(const S& str, typename S::value_type ch) { const size_t pos = str.find(ch, 0); if (pos != S::npos) return S(str.c_str(), pos); //data is non-empty string in this context: else ch would not have been found! else return str; } template inline std::vector split(const S& str, const T& delimiter) { const S delim = S() + delimiter; //handle char, char* and string std::vector output; size_t bockStart = 0; if (!delim.empty()) { for (size_t blockEnd = str.find(delim, bockStart); blockEnd != S::npos; bockStart = blockEnd + delim.size(), blockEnd = str.find(delim, bockStart)) { output.push_back(S(str.c_str() + bockStart, blockEnd - bockStart)); } } output.push_back(S(str.c_str() + bockStart, str.length() - bockStart)); return output; } template inline void truncate(S& str, size_t newLen) { if (newLen < str.length()) str.resize(newLen); } template inline void replace(S& str, const typename S::value_type* old, const typename S::value_type* replacement, bool replaceAll) { const size_t oldLen = cStringLength(old); const size_t replacementLen = cStringLength(replacement); size_t pos = 0; while ((pos = str.find(old, pos)) != S::npos) { str.replace(pos, oldLen, replacement, replacementLen); pos += replacementLen; //move past the string that was replaced if (!replaceAll) break; } } template inline void trim(S& str, bool fromLeft, bool fromRight) { assert(fromLeft || fromRight); typedef typename S::value_type CharType; const CharType* newBegin = str.c_str(); const CharType* newEnd = str.c_str() + str.length(); if (fromRight) while (newBegin != newEnd && cStringIsWhiteSpace(newEnd[-1])) --newEnd; if (fromLeft) while (newBegin != newEnd && cStringIsWhiteSpace(*newBegin)) ++newBegin; const size_t newLength = newEnd - newBegin; if (newLength != str.length()) { if (newBegin != str.c_str()) str = S(newBegin, newLength); //minor inefficiency: in case "str" is not shared, we could save an allocation and do a memory move only else str.resize(newLength); } } namespace { template struct CnvtStringToString { T convert(const S& src) const { return T(src.c_str(), src.size()); } }; template struct CnvtStringToString //optimization: for "basic_string -> basic_string" we don't need a deep copy { S convert(const S& src) const { return src; } }; template struct CvrtNumberToString { S convert(const Num& number) const //convert string to number using streams: convenient, but SLOW { typedef typename S::value_type CharType; std::basic_ostringstream ss; ss << number; return CnvtStringToString, S>().convert(ss.str()); } }; template struct CvrtStringToNumber { Num convert(const S& str) const //convert number to string using streams: convenient, but SLOW { typedef typename S::value_type CharType; Num number = 0; std::basic_istringstream(CnvtStringToString >().convert(str)) >> number; return number; } }; template struct CvrtStringToNumber { Num convert(const S& str) const //very fast conversion to integers: slightly faster than std::atoi, but more importantly: generic { typedef typename S::value_type CharType; const CharType* first = str.c_str(); const CharType* last = first + str.size(); while (first != last && cStringIsWhiteSpace(*first)) //skip leading whitespace ++first; bool hasMinusSign = false; //handle minus sign if (first != last) { if (*first == '-') { hasMinusSign = true; ++first; } else if (*first == '+') ++first; } Num number = 0; for (const CharType* iter = first; iter != last; ++iter) { const CharType c = *iter; if ('0' <= c && c <= '9') { number *= 10; number += c - '0'; } else { assert(std::find_if(iter, last, std::not1(std::ptr_fun(&cStringIsWhiteSpace))) == last); //rest of string should contain whitespace only break; } } return hasMinusSign ? -number : number; } }; } template inline S toString(const Num& number) //convert number to string the C++ way { return CvrtNumberToString().convert(number); } template inline Num toNumber(const S& str) //convert number to string the C++ way { return CvrtStringToNumber::isIntegral>().convert(str); } } #endif //STRING_TOOLS_HEADER_213458973046