1148 lines
28 KiB
C++
Executable File
1148 lines
28 KiB
C++
Executable File
/*
|
||
PIP - Platform Independent Primitives
|
||
String
|
||
Copyright (C) 2019 Ivan Pelipenko peri4ko@yandex.ru
|
||
|
||
This program is free software: you can redistribute it and/or modify
|
||
it under the terms of the GNU 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 General Public License for more details.
|
||
|
||
You should have received a copy of the GNU General Public License
|
||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
*/
|
||
#include "piincludes_p.h"
|
||
#include "pistring.h"
|
||
#ifdef PIP_ICU
|
||
# define U_NOEXCEPT
|
||
# include "unicode/ucnv.h"
|
||
#endif
|
||
#ifdef WINDOWS
|
||
# include <stringapiset.h>
|
||
#endif
|
||
#include <wchar.h>
|
||
#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 <tt>const char * </tt> and
|
||
* \c std::string. Also there are functions to make same convertions:
|
||
* * \a data() - to <tt>const char * </tt>,
|
||
* * \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().
|
||
*
|
||
*/
|
||
|
||
|
||
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) {
|
||
PIString v = value.trimmed();
|
||
if (base < 0) {
|
||
int ind = v.find("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;}
|
||
//v.reverse();
|
||
if (ok) *ok = true;
|
||
PIVector<int> 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;
|
||
/*piForeachC (PIChar & i, v) {
|
||
if (i == PIChar('-')) {ret = -ret; continue;}
|
||
cs = fromBaseN[int(i.toAscii())];
|
||
cout << i << " = " << cs << endl;
|
||
if (cs < 0 || cs >= base) return ret;
|
||
ret += cs * m;
|
||
m *= base;
|
||
}*/
|
||
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]);
|
||
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;
|
||
wchar_t * buffer = new wchar_t[sz];
|
||
MultiByteToWideChar((uint)(uintptr_t)codepage, MB_ERR_INVALID_CHARS, c, s, buffer, sz);
|
||
for (int i = 0; i < sz; ++i)
|
||
push_back(PIChar((ushort)buffer[i]));
|
||
delete[] buffer;
|
||
return;
|
||
//printf("request %d\n", sz);
|
||
# endif
|
||
#endif
|
||
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();
|
||
}
|
||
|
||
|
||
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(short(s[l])));
|
||
++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];
|
||
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<PIChar>::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<PIChar>::data(), PIDeque<PIChar>::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<PIChar>::data(), PIDeque<PIChar>::size_s(), (LPSTR)data_.data(), data_.size_s(), NULL, NULL);
|
||
data_.push_back(uchar('\0'));
|
||
return;
|
||
# endif
|
||
#endif
|
||
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'));
|
||
}
|
||
|
||
|
||
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<PIChar>::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;
|
||
//cout << "wc" << endl;
|
||
/*int l = 0, sz;
|
||
char * c = new char[MB_CUR_MAX];
|
||
while (str[l] != 0) ++l;
|
||
for (int i = 0; i < l; ++i) {
|
||
sz = wctomb(c, str[i]);
|
||
switch (sz) {
|
||
case 4:
|
||
push_back(PIChar(*(int*)c));
|
||
continue;
|
||
case 3:
|
||
push_back(PIChar(*(int*)c));
|
||
back().ch &= 0xFFFFFF;
|
||
continue;
|
||
case 2:
|
||
push_back(PIChar(*(short * )c));
|
||
continue;
|
||
default:
|
||
push_back(PIChar(c[0]));
|
||
break;
|
||
}
|
||
}
|
||
delete[] c;*/
|
||
int i = -1;
|
||
while (str[++i])
|
||
push_back(PIChar(int(str[i])));
|
||
return *this;
|
||
}
|
||
|
||
|
||
PIString & PIString::operator +=(const PIString & str) {
|
||
*((PIDeque<PIChar>*)this) << *((PIDeque<PIChar>*)&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 (str[i] == at(i)) continue;
|
||
if (str[i] < at(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 (str[i] == at(i)) continue;
|
||
if (str[i] < at(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;
|
||
//for (int i = s; i < s + l; ++i)
|
||
// str += at(i);
|
||
// std::cout << "mid " << s << " " << l << " " << size_s() << " " << start << " " << len << "\n";
|
||
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) {
|
||
if (count < length() - from) remove(from, count);
|
||
else remove(from, length() - from);
|
||
uint c = with.length();
|
||
for (uint i = 0; i < c; ++i) insert(from + i, with[i]);
|
||
return *this;
|
||
}
|
||
|
||
|
||
PIString & PIString::replace(const PIString & what, const PIString & with, bool * ok) {
|
||
//piCout << "replace" << what << with;
|
||
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;
|
||
bool ok = true;
|
||
while (ok) replace(what, with, &ok);
|
||
return *this;
|
||
}
|
||
|
||
|
||
PIString & PIString::insert(int index, const PIString & str) {
|
||
//uint c = str.length();
|
||
//for (uint i = 0; i < c; ++i) insert(index + i, str[i]);
|
||
PIDeque<PIChar>::insert(index, *((const PIDeque<PIChar>*)&str));
|
||
return *this;
|
||
}
|
||
|
||
|
||
PIString & PIString::elide(int size, float pos) {
|
||
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, "..");
|
||
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 str, const int start) const {
|
||
for (int i = start; i < length(); ++i)
|
||
if (at(i) == str)
|
||
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 str, const int start) const {
|
||
for (int i = length() - 1; i >= start; --i)
|
||
if (at(i) == str)
|
||
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;
|
||
}
|
||
|
||
|
||
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 {
|
||
PIString s(*this);
|
||
s = s.trimmed().toLowerCase();
|
||
if ( atof(s.toNativeDecimalPoints().data()) > 0. || s == "true" || s == "yes" || s == "on" || s == "ok") 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());
|
||
}
|
||
|
||
/*
|
||
short PIString::toShort() const {
|
||
PIString s(trimmed().toLowerCase().toNativeDecimalPoints());
|
||
short v;
|
||
if (s.left(2) == "0x") {sscanf(s.data(), "%hx", &v); return v;}
|
||
if (s.left(1) == "0") {sscanf(s.data(), "%ho", &v); return v;}
|
||
sscanf(s.data(), "%hd", &v);
|
||
return v;
|
||
}
|
||
|
||
|
||
int PIString::toInt() const {
|
||
PIString s(trimmed().toLowerCase().toNativeDecimalPoints());
|
||
int v;
|
||
if (s.left(2) == "0x") {sscanf(s.data(), "%x", &v); return v;}
|
||
if (s.left(1) == "0") {sscanf(s.data(), "%o", &v); return v;}
|
||
sscanf(s.data(), "%d", &v);
|
||
return v;
|
||
}
|
||
|
||
|
||
long PIString::toLong() const {
|
||
PIString s(trimmed().toLowerCase().toNativeDecimalPoints());
|
||
long v;
|
||
if (s.left(2) == "0x") {sscanf(s.data(), "%lx", &v); return v;}
|
||
if (s.left(1) == "0") {sscanf(s.data(), "%lo", &v); return v;}
|
||
sscanf(s.data(), "%ld", &v);
|
||
return v;
|
||
}
|
||
|
||
|
||
llong PIString::toLLong() const {
|
||
PIString s(trimmed().toLowerCase().toNativeDecimalPoints());
|
||
llong v;
|
||
if (s.left(2) == "0x") {sscanf(s.data(), "%llx", &v); return v;}
|
||
if (s.left(1) == "0") {sscanf(s.data(), "%llo", &v); return v;}
|
||
sscanf(s.data(), "%lld", &v);
|
||
return v;
|
||
}
|
||
*/
|
||
|
||
PIString & PIString::setReadableSize(llong bytes) {
|
||
clear();
|
||
if (bytes < 1024) {*this += (PIString::fromNumber(bytes) + " B"); return *this;}
|
||
double fres = bytes / 1024.;
|
||
llong res = bytes / 1024;
|
||
fres -= res;
|
||
if (res < 1024) {*this += (PIString::fromNumber(res) + "." + PIString::fromNumber(llong(fres * 10)).left(1) + " kB"); return *this;}
|
||
fres = res / 1024.;
|
||
res /= 1024;
|
||
fres -= res;
|
||
if (res < 1024) {*this += (PIString::fromNumber(res) + "." + PIString::fromNumber(llong(fres * 10)).left(1) + " MB"); return *this;}
|
||
fres = res / 1024.;
|
||
res /= 1024;
|
||
fres -= res;
|
||
if (res < 1024) {*this += (PIString::fromNumber(res) + "." + PIString::fromNumber(llong(fres * 10)).left(1) + " GB"); return *this;}
|
||
fres = res / 1024.;
|
||
res /= 1024;
|
||
fres -= res;
|
||
if (res < 1024) {*this += (PIString::fromNumber(res) + "." + PIString::fromNumber(llong(fres * 10)).left(1) + " TB"); return *this;}
|
||
fres = res / 1024.;
|
||
res /= 1024;
|
||
fres -= res;
|
||
*this += (PIString::fromNumber(res) + "." + PIString::fromNumber(llong(fres * 10)).left(1) + " PB");
|
||
return *this;
|
||
}
|
||
|
||
|
||
inline char chrUpr(char c) {
|
||
if (c >= 'a' && c <= 'z') return c + 'A' - 'a';
|
||
//if (c >= 'а' && c <= 'я') return c + 'А' - 'а';
|
||
return c;
|
||
}
|
||
|
||
|
||
inline char chrLwr(char c) {
|
||
if (c >= 'A' && c <= 'Z') return c + 'a' - 'A';
|
||
//if (c >= 'А' && c <= 'Я') return c + 'а' - 'А';
|
||
return c;
|
||
}
|
||
|
||
|
||
PICout operator <<(PICout s, const PIString & v) {
|
||
s.space();
|
||
s.quote();
|
||
s.setControl(0, true);
|
||
//if (PICout::isBufferActive())
|
||
s << v.data();
|
||
//else {
|
||
// s << v.dataConsole();
|
||
//}
|
||
s.restoreControl();
|
||
s.quote();
|
||
return s;
|
||
}
|
||
|
||
|
||
|
||
PIStringList& PIStringList::removeDuplicates() {
|
||
PIStringList l;
|
||
PIString s;
|
||
bool ae;
|
||
for (int i = 0; i < size_s(); ++i) {
|
||
ae = false;
|
||
s = at(i);
|
||
for (int j = 0; j < l.size_s(); ++j) {
|
||
if (s != l[j]) continue;
|
||
ae = true; break;
|
||
}
|
||
if (!ae) {
|
||
l << s;
|
||
continue;
|
||
}
|
||
remove(i);
|
||
--i;
|
||
}
|
||
return *this;
|
||
}
|
||
|