/* PIP - Platform Independent Primitives String Ivan Pelipenko peri4ko@yandex.ru, Andrey Bychkov work.a.b@yandex.ru This program is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program. If not, see . */ #include "piincludes_p.h" #include "pistring.h" #include "pistringlist.h" #ifdef PIP_ICU # define U_NOEXCEPT # include "unicode/ucnv.h" #endif #ifdef WINDOWS # include #endif #include #ifdef ANDROID # if __ANDROID_API__ < 21 # define wctomb(s, wc) wcrtomb(s, wc, NULL) # define mbtowc(pwc, s, n) mbrtowc(pwc, s, n, NULL) # endif #endif /*! \class PIString * \brief String class * \details PIP use this class for use string information. * * \section PIString_sec0 Synopsis * This class based on \a PIVector to store information. * String is a sequence of \a PIChar and can contain multibyte * symbols. Therefore real memory size of string is symbols count * 4. * String can be constucted from many types of data and can be converted * to many types. There are man operators and handly functions to use * string as you wish. * * \section PIString_sec1 To/from data convertions * Most common constructor is \a PIString(const char * str), where "str" * is null-terminated string, e.g. \c "string". This is 7 chars with last char = 0. * Also you can constructs \a PIString from single \a PIChar, \a PIByteArray, * other \a PIString or sequency of the same characters with custom length.\n \n * This class has implicit conversions to const char * and * \c std::string. Also there are functions to make same convertions: * * \a data() - to const char * , * * \a stdString() - to \c std::string, * * \a toByteArray() - to \a PIByteArray. * * \section PIString_sec2 Numeric operations * You can get symbolic representation of any numeric value with function * \a setNumber(any integer value, int base = 10, bool * ok = 0). Default * arguments are set for decimal base system, but you can choose any system * from 2 to 40. There are the same static functions \a fromNumber(), that * returns \a PIString. \n * Also there is function \a setReadableSize() which is set human-readable * size in bytes, Kb, Mb, Gb or Pb. Static analog is \a readableSize(). * */ /*! \fn int versionCompare(const PIString & v0, const PIString & v1, int components = 6) * \relatesalso PIString * \brief Compare two version strings in free notation and returns 0, -1 or 1 * \details This function parse version to number codes and labels. Then it * compare no more than "components" codes. If there is no difference, compare * labels. Each label has corresponding integer value, so * "prealpha" < "alpha" < "prebeta" < "beta" < "rcN" < "" < "rN". * Example: * \code * piCout << versionCompare("1.0.0_rc2-999", "1.0.1_rc2-999"); // -1 * piCout << versionCompare("1.0.0", "0.9.2"); // 1 * piCout << versionCompare("1.0.0_r1", "1.0.0"); // 1 * piCout << versionCompare("1.0.0_r1", "1.0.0", 3); // 0 * piCout << versionCompare("1.0.0_r1", "1.0.0", 3); // 0 * piCout << versionCompare(".2-alpha", "0.2_alpha"); // 0 * piCout << versionCompare("1_prebeta", "1.0_alpha"); // 1 * \endcode * \return * * 0 - equal * * 1 - v0 > v1 * * -1 - v0 < v1 * * * \fn PIString versionNormalize(const PIString & v) * \relatesalso PIString * \brief Converts version string in free notation to classic view * \details Parse version as described in \a versionCompare() and * returns classic view of codes and labels: major.minor.revision[-build][_label]. * Example: * \code * piCout << versionNormalize(""); // 0.0.0 * piCout << versionNormalize("1"); // 1.0.0 * piCout << versionNormalize("1.2"); // 1.2.0 * piCout << versionNormalize("1.2.3"); // 1.2.3 * piCout << versionNormalize("1.2+rc1.99"); // 1.2.99_rc1 * piCout << versionNormalize("1.2-alpha"); // 1.2.0_alpha * piCout << versionNormalize("1..4_rc2-999"); // 1.0.4-999_rc2 * \endcode * */ const char PIString::toBaseN[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', '[', '\\', ']', '^'}; const int PIString::fromBaseN[] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, -1, -1, -1, -1, -1, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, -1, -1, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; const float PIString::ElideLeft = 0.f; const float PIString::ElideCenter = .5f; const float PIString::ElideRight = 1.f; #ifndef CC_VC # define pisprintf(f, v) char ch[256]; memset(ch, 0, 256); sprintf(ch, f, v); #else # define pisprintf(f, v) char ch[256]; memset(ch, 0, 256); sprintf_s(ch, 256, f, v); #endif #ifdef ANDROID //int wctomb(char * c, wchar_t w) {*c = ((char * )&w)[0]; return 1;} #endif PIString PIString::itos(const int num) {pisprintf("%d", num); return PIString(ch);} PIString PIString::ltos(const long num) {pisprintf("%ld", num); return PIString(ch);} PIString PIString::lltos(const llong num) {pisprintf("%lld", num); return PIString(ch);} PIString PIString::uitos(const uint num) {pisprintf("%u", num); return PIString(ch);} PIString PIString::ultos(const ulong num) {pisprintf("%lu", num); return PIString(ch);} PIString PIString::ulltos(const ullong num) {pisprintf("%llu", num); return PIString(ch);} PIString PIString::ftos(const float num, char format, int precision) { char f[8] = "%."; int wr = sprintf(&(f[2]), "%d", precision); f[2 + wr] = format; f[3 + wr] = 0; pisprintf(f, num); return PIString(ch); } PIString PIString::dtos(const double num, char format, int precision) { char f[8] = "%."; int wr = sprintf(&(f[2]), "%d", precision); f[2 + wr] = format; f[3 + wr] = 0; pisprintf(f, num); return PIString(ch); } #undef pisprintf PIString PIString::fromNumberBaseS(const llong value, int base, bool * ok) { if (value == 0LL) return PIString('0'); if (base < 2 || base > 40) {if (ok != 0) *ok = false; return PIString();} if (ok != 0) *ok = true; if (base == 10) return lltos(value); PIString ret; llong v = value < 0 ? -value : value, cn; int b = base; while (v >= llong(base)) { cn = v % b; v /= b; //cout << int(cn) << ", " << int(v) << endl; ret.push_front(PIChar(toBaseN[cn])); } if (v > 0) ret.push_front(PIChar(toBaseN[v])); if (value < 0) ret.push_front('-'); return ret; } PIString PIString::fromNumberBaseU(const ullong value, int base, bool * ok) { if (value == 0ULL) return PIString('0'); if (base < 2 || base > 40) {if (ok != 0) *ok = false; return PIString();} if (ok != 0) *ok = true; if (base == 10) return ulltos(value); PIString ret; ullong v = value, cn; int b = base; while (v >= ullong(base)) { cn = v % b; v /= b; //cout << int(cn) << ", " << int(v) << endl; ret.push_front(PIChar(toBaseN[cn])); } if (v > 0) ret.push_front(PIChar(toBaseN[v])); return ret; } llong PIString::toNumberBase(const PIString & value, int base, bool * ok) { static const PIString s_0x = PIStringAscii("0x"); PIString v = value.trimmed(); if (base < 0) { int ind = v.find(s_0x); if (ind == 0 || ind == 1) {v.remove(ind, 2); base = 16;} else base = 10; } else if (base < 2 || base > 40) {if (ok != 0) *ok = false; return 0;} if (ok) *ok = true; PIVector digits; llong ret = 0, m = 1; bool neg = false; int cs; for (int i = 0; i < v.size_s(); ++i) { if (v[i] == PIChar('-')) {neg = !neg; continue;} cs = fromBaseN[int(v[i].toAscii())]; if (cs < 0 || cs >= base) { if (ok) *ok = false; break; } digits << cs; } for (int i = digits.size_s() - 1; i >= 0; --i) { ret += digits[i] * m; m *= base; } if (neg) ret = -ret; return ret; } void PIString::appendFromChars(const char * c, int s, const char * codepage) { if (s <= 0) return; int sz; #ifdef PIP_ICU UErrorCode e((UErrorCode)0); UConverter * cc = ucnv_open(codepage, &e); if (cc) { UChar * ucs = new UChar[s]; memset(ucs, 0, s * sizeof(UChar)); e = (UErrorCode)0; int sz = ucnv_toUChars(cc, ucs, s, c, s, &e); //printf("appendFromChars %d -> %d\n", s, sz); //printf("PIString %d -> %d\n", c[0], ucs[0]); reserve(size_s() + sz); for (int i = 0; i < sz; ++i) push_back(PIChar(ucs[i])); delete[] ucs; ucnv_close(cc); return; } #else # ifdef WINDOWS sz = MultiByteToWideChar((uint)(uintptr_t)codepage, MB_ERR_INVALID_CHARS, c, s, 0, 0); if (sz <= 0) return; int old_sz = size_s(); enlarge(sz); MultiByteToWideChar((uint)(uintptr_t)codepage, MB_ERR_INVALID_CHARS, c, s, (LPWSTR)PIDeque::data(old_sz), sz); return; //printf("request %d\n", sz); # else wchar_t wc; mbtowc(0,0,0); // reset mbtowc //qDebug() << "FromChars ..."; while (s>0) { //qDebug() << "0" << s; sz = mbtowc(&wc, c, s); //qDebug() << "1" << sz; if (sz < 1) break; push_back(PIChar(int(wc))); c += sz; s -= sz; //qDebug() << "2" << c; } //qDebug() << "FromChars done" << size(); # endif #endif } PIString PIString::fromConsole(const char * s) { int l = 0; while (s[l] != '\0') ++l; PIString ret; if (l > 0) ret.appendFromChars(s, l, __sysoemname__); return ret; } PIString PIString::fromSystem(const char * s) { int l = 0; while (s[l] != '\0') ++l; PIString ret; if (l > 0) ret.appendFromChars(s, l, __syslocname__); return ret; } PIString PIString::fromUTF8(const char * s) { int l = 0; while (s[l] != '\0') ++l; PIString ret; if (l > 0) ret.appendFromChars(s, l, __utf8name__); return ret; } PIString PIString::fromUTF8(const PIByteArray & ba) { PIString ret; if (ba.isEmpty()) return ret; ret.appendFromChars((const char*)ba.data(), ba.size(), __utf8name__); return ret; } PIString PIString::fromAscii(const char * s) { PIString ret; int l = 0; while (s[l] != '\0') { ret.push_back(PIChar(s[l])); ++l; } return ret; } PIString PIString::fromAscii(const char * s, int len) { PIString ret; for (int l = 0; l < len; ++l) ret.push_back(PIChar(s[l])); return ret; } PIString PIString::fromCodepage(const char * s, const char * c) { int l = 0; while (s[l] != '\0') ++l; PIString ret; if (l > 0) ret.appendFromChars(s, l #ifdef PIP_ICU , c #else # ifdef WINDOWS , __utf8name__ # endif #endif ); return ret; } PIString PIString::readableSize(llong bytes) { PIString s; s.setReadableSize(bytes); return s; } void PIString::buildData(const char * cp) const { data_.clear(); int sz = 0; #ifdef PIP_ICU UErrorCode e((UErrorCode)0); UConverter * cc = ucnv_open(cp, &e); if (cc) { char uc[8]; data_.reserve(size_s()); for (int i = 0; i < size_s(); ++i) { if (at(i).isAscii()) data_.push_back(uchar(at(i).unicode16Code())); else { e = (UErrorCode)0; sz = ucnv_fromUChars(cc, uc, 8, (const UChar*)(PIDeque::data(i)), 1, &e); for (int j = 0; j < sz; ++j) data_.push_back(uc[j]); } } ucnv_close(cc); data_.push_back('\0'); return; } #else # ifdef WINDOWS sz = WideCharToMultiByte((uint)(uintptr_t)cp, 0, (LPCWCH)PIDeque::data(), PIDeque::size_s(), 0, 0, NULL, NULL); //printf("WideCharToMultiByte %d %d\n", (uint)(uintptr_t)cp, sz); if (sz <= 0) { //printf("WideCharToMultiByte erro %d\n", GetLastError()); data_.push_back(uchar('\0')); return; } data_.resize(sz); WideCharToMultiByte((uint)(uintptr_t)cp, 0, (LPCWCH)PIDeque::data(), PIDeque::size_s(), (LPSTR)data_.data(), data_.size_s(), NULL, NULL); data_.push_back(uchar('\0')); return; # else wchar_t wc; char tc[8]; wctomb(0, 0); for (int i = 0; i < size_s(); ++i) { if (at(i).isAscii()) { data_.push_back(uchar(at(i).toAscii())); continue; } wc = at(i).toWChar(); sz = wctomb(tc, wc); for (int b = 0; b < sz; ++b) data_.push_back(uchar(tc[b])); } data_.push_back(uchar('\0')); # endif #endif } void PIString::trimsubstr(int &st, int &fn) const { for (int i = 0; i < length(); ++i) if (at(i) != ' ' && at(i) != '\t' && at(i) != '\n' && at(i) != '\r' && at(i) != char(12) && at(i) != uchar(0)) {st = i; break;} if (st < 0) return; for (int i = length() - 1; i >= 0; --i) if (at(i) != ' ' && at(i) != '\t' && at(i) != '\n' && at(i) != '\r' && at(i) != char(12) && at(i) != uchar(0)) {fn = i; break;} } const char * PIString::dataConsole() const { buildData(__sysoemname__ ); return (const char *)(data_.data()); } const char * PIString::dataUTF8() const { buildData(__utf8name__); return (const char *)(data_.data()); } const char * PIString::dataAscii() const { data_.clear(); for (int i = 0; i < size_s(); ++i) data_.push_back(uchar(at(i).ch)); data_.push_back(uchar('\0')); return (const char *)data_.data(); } uint PIString::hash() const { return piHashData((const uchar*)PIDeque::data(), size() * sizeof(PIChar)); } PIByteArray PIString::toUTF8() const { if (isEmpty()) return data_.resized(0); buildData(__utf8name__); return data_.resized(data_.size_s() - 1); } PIByteArray PIString::toCharset(const char * c) const { if (isEmpty()) return data_.resized(0); buildData( #ifdef PIP_ICU c #else # ifdef WINDOWS __utf8name__ # endif #endif ); return data_.resized(data_.size_s() - 1); } PIString & PIString::operator +=(const char * str) { if (!str) return *this; int l = 0; while (str[l] != '\0') ++l; appendFromChars(str, l, __syslocname__); return *this; } PIString & PIString::operator +=(const wchar_t * str) { if (!str) return *this; int i = -1; while (str[++i]) push_back(PIChar(ushort(str[i]))); return *this; } PIString & PIString::operator +=(const PIString & str) { *((PIDeque*)this) << *((PIDeque*)&str); return *this; } bool PIString::operator ==(const PIString & str) const { uint l = str.size(); if (size() != l) return false; for (uint i = 0; i < l; ++i) if (str[i] != at(i)) return false; return true; } bool PIString::operator !=(const PIString & str) const { uint l = str.size(); if (size() != l) return true; for (uint i = 0; i < l; ++i) if (str[i] != at(i)) return true; return false; } bool PIString::operator <(const PIString & str) const { uint l = str.size(); if (size() < l) return true; if (size() > l) return false; for (uint i = 0; i < l; ++i) { if (at(i) == str[i]) continue; if (at(i) < str[i]) return true; else return false; } return false; } bool PIString::operator >(const PIString & str) const { uint l = str.size(); if (size() < l) return false; if (size() > l) return true; for (uint i = 0; i < l; ++i) { if (at(i) == str[i]) continue; if (at(i) < str[i]) return false; else return true; } return false; } PIString PIString::mid(const int start, const int len) const { //PIString str; int s = start, l = len; if (l == 0 || s >= length()) return PIString(); if (s < 0) { l += s; s = 0; } if (l < 0) { return PIString(&(at(s)), size_s() - s); } else { if (l > length() - s) l = length() - s; return PIString(&(at(s)), l); } return PIString(); } PIString & PIString::cutMid(const int start, const int len) { int s = start, l = len; if (l == 0) return *this; if (s < 0) { l += s; s = 0; } if (l < 0) remove(s, size() - s); else { if (l > length() - s) l = length() - s; remove(s, l); } return *this; } PIString & PIString::trim() { int st = -1, fn = 0; trimsubstr(st, fn); if (st < 0) { clear(); return *this; } if (fn < size_s() - 1) cutRight(size_s() - fn - 1); if (st > 0) cutLeft(st); return *this; } PIString PIString::trimmed() const { int st = -1, fn = 0; trimsubstr(st, fn); if (st < 0) return PIString(); return mid(st, fn - st + 1); } PIString & PIString::replace(int from, int count, const PIString & with) { count = piMini(count, length() - from); if (count == with.size_s()) memcpy(PIDeque::data(from), static_cast>(with).data(), count * sizeof(PIChar)); else { remove(from, count); PIDeque::insert(from, with); } return *this; } PIString & PIString::replace(const PIString & what, const PIString & with, bool * ok) { if (what.isEmpty()) { if (ok != 0) *ok = false; return *this; } int s = find(what); if (s >= 0) replace(s, what.length(), with); if (ok != 0) *ok = (s >= 0); return *this; } PIString & PIString::replaceAll(const PIString & what, const PIString & with) { if (what.isEmpty() || what == with) return *this; if (with.isEmpty()) removeAll(what); else { int l = what.length(), dl = with.length() - what.length(); for (int i = 0; i < length() - l + 1; ++i) { bool match = true; for (int j = 0; j < l; ++j) { if (at(j + i) != what[j]) { match = false; break; } } if (!match) continue; if (dl > 0) PIDeque::insert(i, PIDeque((size_t)dl)); if (dl < 0) PIDeque::remove(i, -dl); memcpy(PIDeque::data(i), &(with.at(0)), with.length() * sizeof(PIChar)); //i -= l; } } return *this; } PIString & PIString::replaceAll(const PIString & what, const char with) { if (what.isEmpty()) return *this; int l = what.length(), dl = what.length() - 1; for (int i = 0; i < length() - l + 1; ++i) { bool match = true; for (int j = 0; j < l; ++j) { if (at(j + i) != what[j]) { match = false; break; } } if (!match) continue; if (dl > 0) PIDeque::remove(i, dl); (*this)[i] = PIChar(with); //i -= l; } return *this; } PIString & PIString::replaceAll(const char what, const char with) { int l = length(); for (int i = 0; i < l; ++i) { if (at(i) == what) (*this)[i] = with; } return *this; } PIString & PIString::removeAll(const PIString & str) { if (str.isEmpty()) return *this; int l = str.length(); for (int i = 0; i < length() - l + 1; ++i) { bool match = true; for (int j = 0; j < l; ++j) { if (at(j + i) != str[j]) { match = false; break; } } if (!match) continue; PIDeque::remove(i, l); i -= l; } return *this; } PIString & PIString::insert(int index, const PIString & str) { PIDeque::insert(index, *((const PIDeque*)&str)); return *this; } PIString & PIString::elide(int size, float pos) { static const PIString s_dotdot = PIStringAscii(".."); if (length() <= size) return *this; if (length() <= 2) { fill('.'); return *this; } pos = piClampf(pos, 0.f, 1.f); int ns = size - 2; int ls = piRoundf(ns * pos); remove(ls, length() - ns); insert(ls, s_dotdot); return *this; } PIStringList PIString::split(const PIString & delim) const { PIStringList sl; if (isEmpty() || delim.isEmpty()) return sl; PIString ts(*this); int ci = ts.find(delim); while (ci >= 0) { sl << ts.left(ci); ts.cutLeft(ci + delim.length()); ci = ts.find(delim); } if (ts.length() > 0) sl << ts; return sl; } int PIString::find(const char c, const int start) const { for (int i = start; i < length(); ++i) if (at(i) == c) return i; return -1; } int PIString::find(const PIString & str, const int start) const { int l = str.length(); for (int i = start; i < length() - l + 1; ++i) if (mid(i, l) == str) return i; return -1; } int PIString::findLast(const char c, const int start) const { for (int i = length() - 1; i >= start; --i) if (at(i) == c) return i; return -1; } int PIString::findLast(const PIString & str, const int start) const { int l = str.length(); for (int i = length() - l; i >= start; --i) if (mid(i, l) == str) return i; return -1; } int PIString::findWord(const PIString & word, const int start) const { int f = start - 1, tl = length(), wl = word.length(); while ((f = find(word, f + 1)) >= 0) { bool ok = true; PIChar c; if (f > 0) {c = (*this)[f - 1]; if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r')) {ok = false; continue;}} if (f + wl < tl) {c = (*this)[f + wl]; if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r')) {ok = false; continue;}} if (ok) return f; } return -1; } int PIString::findCWord(const PIString & word, const int start) const { int f = start - 1, tl = length(), wl = word.length(); while ((f = find(word, f + 1)) >= 0) { bool ok = true; PIChar c; if (f > 0) {c = (*this)[f - 1]; if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r' || (c != '_' && !c.isAlpha() && !c.isDigit()))) {ok = false; continue;}} if (f + wl < tl) {c = (*this)[f + wl]; if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r' || (c != '_' && !c.isAlpha() && !c.isDigit()))) {ok = false; continue;}} if (ok) return f; } return -1; } int PIString::findRange(const PIChar start, const PIChar end, const PIChar shield, const int start_index, int * len) const { if (len) *len = 0; bool trim_ = (start != ' ' && start != '\t' && start != '\n' && start != '\r'), eq = (start == end); int sz = size_s(), ls = -1, le = -1, cnt = 0; for (int i = start_index; i < sz; ++i) { PIChar c = at(i); if (c == shield) {++i; continue;} if (trim_) { if (c == ' ' || c == '\t' || c == '\n' || c == '\r') continue; trim_ = false; } if (eq) { if (c == start) { if (cnt == 0) ls = i; else {le = i; cnt = 0; break;} cnt++; } } else { if (c == start) { if (cnt == 0) ls = i; cnt++; } if (c == end) { cnt--; if (cnt == 0) le = i; } } if (cnt <= 0) break; } //piCout << ls << le << cnt; if (le < ls || ls < 0 || le < 0 || cnt != 0) return -1; if (len) *len = le - ls - 1; return ls + 1; } int PIString::findAny(const PIString & str, const int start) const { for (int i = start; i < length(); ++i) if (str.contains(at(i))) return i; return -1; } int PIString::findAnyLast(const PIString & str, const int start) const { for (int i = length() - 1; i >= start; --i) if (str.contains(at(i))) return i; return -1; } int PIString::entries(const PIChar c) const { int sz = size_s(), ret = 0; for (int i = 0; i < sz; ++i) if (at(i) == c) ++ret; return ret; } bool PIString::startsWith(const PIString & str) const { if (size() < str.size()) return false; return str == left(str.size()); } bool PIString::endsWith(const PIString & str) const { if (size() < str.size()) return false; return str == right(str.size()); } bool PIString::toBool() const { static const PIString s_true = PIStringAscii("true"); static const PIString s_yes = PIStringAscii("yes" ); static const PIString s_on = PIStringAscii("on" ); static const PIString s_ok = PIStringAscii("ok" ); PIString s(*this); s = s.trimmed().toLowerCase(); if (s == s_true || s == s_yes || s == s_on || s == s_ok) return true; if (atof(s.toNativeDecimalPoints().data()) > 0.) return true; return false; } PIString PIString::takeSymbol() { PIString ret; int sz = size_s(), ss = -1; for (int i = 0; i < sz; ++i) { PIChar c = at(i); if (c == ' ' || c == '\t' || c == '\n' || c == '\r') continue; ss = i; break; } if (ss < 0) return ret; ret = mid(ss, 1); cutLeft(ss + 1); return ret; } PIString PIString::takeWord() { int sz = size_s(), ws = -1, we = -1; for (int i = 0; i < sz; ++i) { PIChar c = at(i); if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { if (we < 0 && ws >= 0) { we = i; break; } } else { if (ws < 0) ws = i; if (we >= 0) break; } } PIString ret = mid(ws, we - ws); cutLeft(we < 0 ? sz : we); return ret; } PIString PIString::takeCWord() { PIString ret; int sz = size_s(), ws = -1, we = -1; for (int i = 0; i < sz; ++i) { PIChar c = at(i); if (c == ' ' || c == '\t' || c == '\n' || c == '\r') { if (we < 0 && ws >= 0) { we = i; break; } } else { if (ws < 0) { if (c.isAlpha() || c == '_') ws = i; else return ret; } else { if (!c.isAlpha() && !c.isDigit() && c != '_') { we = i; break; } } if (we >= 0) break; } } ret = mid(ws, we - ws); cutLeft(we < 0 ? sz : we); return ret; } PIString PIString::takeLine() { int sz = size_s(), le = -1; for (int i = 0; i < sz; ++i) { PIChar c = at(i); if (c == '\n') { le = i; break; } } PIString ret = left(le); if (!ret.isEmpty()) if (ret.back() == '\r') ret.cutRight(1); cutLeft(le < 0 ? sz : le + 1); return ret; } PIString PIString::takeNumber() { PIString ret; int sz = size_s(), ls = -1, le = -1, phase = 0; for (int i = 0; i < sz; ++i) { if (phase > 7) break; PIChar c = at(i); //piCout << "char " << c << "phase" << phase; switch (phase) { case 0: // trim if (c == ' ' || c == '\t' || c == '\n' || c == '\r') continue; phase = 7; case 7: // sign if (c == '-' || c == '+') {ls = i; phase = 1; break;} case 1: // search start if (c >= '0' && c <= '9') {le = i; if (ls < 0) ls = i; phase = 2; break;} if (c == '.') {le = i; if (ls < 0) ls = i; phase = 3; break;} phase = 9; break; case 2: // integer if (c == '.') {le = i; phase = 3; break;} if (c == 'e' || c == 'E') {le = i; phase = 4; break;} if ((c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || c == 'x') {le = i; break;} phase = 6; break; case 3: // point if (c == 'e' || c == 'E') {le = i; phase = 4; break;} if (c >= '0' && c <= '9') {le = i; break;} phase = 6; break; case 4: // exp if ((c >= '0' && c <= '9') || c == '-' || c == '+') {le = i; phase = 5; break;} phase = 6; break; case 5: // power if (c >= '0' && c <= '9') {le = i; break;} phase = 6; break; case 6: // suffix if (c == 'f' || c == 's' || c == 'u' || c == 'l' || c == 'L') {le = i; break;} phase = 9; break; } if (phase == 6) { if (c == 'f' || c == 's' || c == 'u' || c == 'l' || c == 'L') le = i; else phase = 9; } } //piCout << ls << le; if (le < ls) return ret; ret = mid(ls, le - ls + 1); cutLeft(le + 1); return ret; } PIString PIString::takeRange(const PIChar start, const PIChar end, const PIChar shield) { PIString ret; bool trim_ = (start != ' ' && start != '\t' && start != '\n' && start != '\r'), eq = (start == end); int sz = size_s(), ls = -1, le = -1, cnt = 0; for (int i = 0; i < sz; ++i) { PIChar c = at(i); if (c == shield) {++i; continue;} if (trim_) { if (c == ' ' || c == '\t' || c == '\n' || c == '\r') continue; trim_ = false; } if (eq) { if (c == start) { if (cnt == 0) ls = i; else {le = i; cnt = 0; break;} cnt++; } } else { if (c == start) { if (cnt == 0) ls = i; cnt++; } if (c == end) { cnt--; if (cnt == 0) le = i; } } if (cnt <= 0) break; } //piCout << ls << le << cnt; if (le < ls || ls < 0 || le < 0 || cnt != 0) return ret; ret = mid(ls + 1, le - ls - 1); cutLeft(le + 1); return ret; } PIString PIString::inBrackets(const PIChar start, const PIChar end) const { int slen = length(); int st = -1, bcnt = 0; PIChar cc; for (int i = 0; i < slen; i++) { cc = at(i); if (cc == start) { if (bcnt == 0) st = i; bcnt++; } if (cc == end && st >= 0) { bcnt--; if (bcnt == 0) return mid(st+1, i-st-1); } } return PIString(); } PIString PIString::toUpperCase() const { PIString str(*this); int l = str.size(); for (int i = 0; i < l; ++i) str[i] = str[i].toUpper(); return str; } PIString PIString::toLowerCase() const { PIString str(*this); int l = str.size(); for (int i = 0; i < l; ++i) str[i] = str[i].toLower(); return str; } PIString PIString::toNativeDecimalPoints() const { #ifdef HAS_LOCALE PIString s(*this); if (currentLocale == 0) return s; return s.replaceAll('.', currentLocale->decimal_point).replaceAll(',', currentLocale->decimal_point); #else return PIString(*this).replaceAll(',', '.'); #endif } char PIString::toChar() const { PIString s(toNativeDecimalPoints()); char v; sscanf(s.data(), "%c", &v); return v; } float PIString::toFloat() const { return (float)atof(toNativeDecimalPoints().data()); } double PIString::toDouble() const { return atof(toNativeDecimalPoints().data()); } ldouble PIString::toLDouble() const { return atof(toNativeDecimalPoints().data()); } PIString & PIString::setReadableSize(llong bytes) { clear(); if (bytes < 1024) {*this += (PIString::fromNumber(bytes) + PIStringAscii(" B")); return *this;} double fres = bytes / 1024.; llong res = bytes / 1024; fres -= res; if (res < 1024) {*this += (PIString::fromNumber(res) + PIStringAscii(".") + PIString::fromNumber(llong(fres * 10)).left(1) + PIStringAscii(" kB")); return *this;} fres = res / 1024.; res /= 1024; fres -= res; if (res < 1024) {*this += (PIString::fromNumber(res) + PIStringAscii(".") + PIString::fromNumber(llong(fres * 10)).left(1) + PIStringAscii(" MB")); return *this;} fres = res / 1024.; res /= 1024; fres -= res; if (res < 1024) {*this += (PIString::fromNumber(res) + PIStringAscii(".") + PIString::fromNumber(llong(fres * 10)).left(1) + PIStringAscii(" GB")); return *this;} fres = res / 1024.; res /= 1024; fres -= res; if (res < 1024) {*this += (PIString::fromNumber(res) + PIStringAscii(".") + PIString::fromNumber(llong(fres * 10)).left(1) + PIStringAscii(" TB")); return *this;} fres = res / 1024.; res /= 1024; fres -= res; *this += (PIString::fromNumber(res) + PIStringAscii(".") + PIString::fromNumber(llong(fres * 10)).left(1) + PIStringAscii(" PB")); return *this; } const static PIString _versionDelims_ = PIStringAscii("._-+"); void parseVersion(PIString s, PIVector & codes, PIStringList & strs) { s.trim(); if (s.isEmpty()) { codes.resize(3, 0); return; } int mccnt = 2 - s.entries('.'); if (mccnt > 0) { int ind = s.findLast('.') + 1; while (!_versionDelims_.contains(s[ind])) { ++ind; if (ind > s.size_s() - 1) break; } for (int i = 0; i < mccnt; ++i) s.insert(ind, PIStringAscii(".0")); } PIStringList comps; while (!s.isEmpty()) { int ind = s.findAny(_versionDelims_); if (ind >= 0) { comps << s.takeLeft(ind); s.cutLeft(1).trim(); } else { comps << s; s.clear(); } } for (int i = 0; i < comps.size_s(); ++i) { if (comps[i].isEmpty()) comps[i] = '0'; bool ok = false; int val = comps[i].toInt(-1, &ok); if (ok) { codes << val; } else { strs << comps[i]; } } //piCout << codes << strs; } int versionLabelValue(PIString s) { int ret = -10000; if (s.isEmpty()) return 0; if (s.startsWith(PIStringAscii("pre"))) { s.cutLeft(3); ret -= 1; } if (s.startsWith(PIStringAscii("rc"))) { s.cutLeft(2); ret += s.toInt(); } if (s.startsWith(PIStringAscii("r"))) { s.cutLeft(1); ret += 10000 + s.toInt(); } if (s == PIStringAscii("alpha")) ret -= 4; if (s == PIStringAscii("beta" )) ret -= 2; return ret; } int versionCompare(const PIString & v0, const PIString & v1, int components) { PIStringList strs[2]; PIVector codes[2]; parseVersion(v0.toLowerCase(), codes[0], strs[0]); parseVersion(v1.toLowerCase(), codes[1], strs[1]); //piCout << codes[0] << strs[0]; int mc = piMaxi(codes[0].size_s(), codes[1].size_s()); if (codes[0].size_s() < mc) codes[0].resize(mc, 0); if (codes[1].size_s() < mc) codes[1].resize(mc, 0); mc = piMaxi(strs[0].size_s(), strs[1].size_s()); if (strs[0].size_s() < mc) strs[0].resize(mc, ""); if (strs[1].size_s() < mc) strs[1].resize(mc, ""); int comps = piMini(components, codes[0].size_s(), codes[1].size_s()); if (comps < 1) return (v0 == v1 ? 0 : (v0 > v1 ? 1 : -1)); for (int c = 0; c < comps; ++c) { if (codes[0][c] > codes[1][c]) return 1; if (codes[0][c] < codes[1][c]) return -1; } mc = piClampi(mc, 0, components - comps); for (int c = 0; c < mc; ++c) { int lv0 = versionLabelValue(strs[0][c]); int lv1 = versionLabelValue(strs[1][c]); if (lv0 > lv1) return 1; if (lv0 < lv1) return -1; } return 0; } PIString versionNormalize(const PIString & v) { PIStringList strs; PIVector codes; parseVersion(v.toLowerCase(), codes, strs); PIString ret; for (int i = 0; i < codes.size_s(); ++i) { if (i > 0) { if (i < 3) ret += '.'; else ret += '-'; } ret += PIString::fromNumber(codes[i]); } for (int i = 0; i < strs.size_s(); ++i) { ret += '_'; ret += strs[i]; } return ret; } PICout operator <<(PICout s, const PIString & v) { s.space(); s.quote(); s.setControl(0, true); s << v.data(); s.restoreControl(); s.quote(); return s; }