1986 lines
50 KiB
C++
1986 lines
50 KiB
C++
/*
|
|
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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "pistring.h"
|
|
|
|
#include "piincludes_p.h"
|
|
#include "piliterals.h"
|
|
#include "pimathbase.h"
|
|
#include "pistringlist.h"
|
|
#include "pitranslator.h"
|
|
#ifdef PIP_ICU
|
|
# define U_NOEXCEPT
|
|
# include "unicode/ucnv.h"
|
|
#endif
|
|
#ifdef WINDOWS
|
|
# include <stringapiset.h>
|
|
#endif
|
|
#include <codecvt>
|
|
#include <locale>
|
|
#include <string>
|
|
|
|
//! \class PIString pistring.h
|
|
//! \~\details
|
|
//! \~english \section PIString_sec0 Synopsis
|
|
//! \~russian \section PIString_sec0 Краткий обзор
|
|
//!
|
|
//! \~english
|
|
//! String is a sequence of \a PIChar. Real memory size of string is symbols count * 2.
|
|
//! String can be constucted from many types of data and can be converted
|
|
//! to many types. There are many operators and handly functions to use
|
|
//! string as you wish.
|
|
//!
|
|
//! \~russian
|
|
//! Строка состоит из последовательности \a PIChar. Реальный объем памяти,
|
|
//! занимаемый строкой, равен количеству символов * 2. Строка может быть
|
|
//! создана из множества типов и преобразована в несколько типов.
|
|
//! Имеет множество методов для манипуляций.
|
|
//!
|
|
|
|
|
|
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 char PIString::fromBaseN[] = {
|
|
(char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1,
|
|
(char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1,
|
|
(char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1,
|
|
(char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)0, (char)1, (char)2, (char)3,
|
|
(char)4, (char)5, (char)6, (char)7, (char)8, (char)9, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1,
|
|
(char)10, (char)11, (char)12, (char)13, (char)14, (char)15, (char)16, (char)17, (char)18, (char)19, (char)20, (char)21, (char)22,
|
|
(char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29, (char)30, (char)31, (char)32, (char)33, (char)34, (char)35,
|
|
(char)36, (char)37, (char)38, (char)39, (char)-1, (char)-1, (char)10, (char)11, (char)12, (char)13, (char)14, (char)15, (char)16,
|
|
(char)17, (char)18, (char)19, (char)20, (char)21, (char)22, (char)23, (char)24, (char)25, (char)26, (char)27, (char)28, (char)29,
|
|
(char)30, (char)31, (char)32, (char)33, (char)34, (char)35, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1,
|
|
(char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1,
|
|
(char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1,
|
|
(char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1,
|
|
(char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1,
|
|
(char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1,
|
|
(char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1,
|
|
(char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1,
|
|
(char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1,
|
|
(char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1,
|
|
(char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1, (char)-1};
|
|
|
|
const float PIString::ElideLeft = 0.f;
|
|
const float PIString::ElideCenter = .5f;
|
|
const float PIString::ElideRight = 1.f;
|
|
|
|
|
|
template<typename T>
|
|
T toDecimal(const PIString & s) {
|
|
int part = 0, exp = 0;
|
|
bool negative = false, negative_exp = false, err = false, has_digit = false;
|
|
T ret = 0., frac = 0., frac_delim = 1.;
|
|
for (const PIChar pc: s) {
|
|
char c = pc.toAscii();
|
|
switch (part) {
|
|
case 0: // sign
|
|
if (pc.isSpace()) continue;
|
|
if (c >= '0' && c <= '9') {
|
|
has_digit = true;
|
|
ret = c - '0';
|
|
part = 1;
|
|
continue;
|
|
}
|
|
if (c == '+') {
|
|
part = 1;
|
|
continue;
|
|
}
|
|
if (c == '-') {
|
|
negative = true;
|
|
part = 1;
|
|
continue;
|
|
}
|
|
if (c == '.' || c == ',') {
|
|
part = 2;
|
|
continue;
|
|
}
|
|
err = true;
|
|
break;
|
|
case 1: // integer
|
|
if (c >= '0' && c <= '9') {
|
|
has_digit = true;
|
|
ret = ret * 10 + (c - '0');
|
|
continue;
|
|
}
|
|
if (c == '.' || c == ',') {
|
|
part = 2;
|
|
continue;
|
|
}
|
|
if ((c == 'e' || c == 'E') && has_digit) {
|
|
part = 3;
|
|
continue;
|
|
}
|
|
err = true;
|
|
break;
|
|
case 2: // fractional
|
|
if (c >= '0' && c <= '9') {
|
|
has_digit = true;
|
|
frac = frac * 10 + (c - '0');
|
|
frac_delim *= 10.;
|
|
continue;
|
|
}
|
|
if ((c == 'e' || c == 'E') && has_digit) {
|
|
part = 3;
|
|
continue;
|
|
}
|
|
err = true;
|
|
break;
|
|
case 3: // exponent with sign
|
|
if (c == '+') {
|
|
part = 4;
|
|
continue;
|
|
}
|
|
if (c == '-') {
|
|
negative_exp = true;
|
|
part = 4;
|
|
continue;
|
|
}
|
|
case 4: // exponent
|
|
if (c >= '0' && c <= '9') {
|
|
exp = (exp * 10) + (c - '0');
|
|
continue;
|
|
}
|
|
err = true;
|
|
break;
|
|
}
|
|
if (err) break;
|
|
}
|
|
frac /= frac_delim;
|
|
ret += frac;
|
|
if (negative && has_digit) ret = -ret;
|
|
if (exp > 0) {
|
|
if (negative_exp)
|
|
ret /= pow10((T)exp);
|
|
else
|
|
ret *= pow10((T)exp);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
#define pisprintf(f, v) \
|
|
char ch[256]; \
|
|
memset(ch, 0, 256); \
|
|
snprintf(ch, 256, f, v); \
|
|
return PIStringAscii(ch);
|
|
|
|
PIString PIString::itos(const int num) {
|
|
pisprintf("%d", num);
|
|
}
|
|
PIString PIString::ltos(const long num) {
|
|
pisprintf("%ld", num);
|
|
}
|
|
PIString PIString::lltos(const llong num) {
|
|
pisprintf("%lld", num);
|
|
}
|
|
PIString PIString::uitos(const uint num) {
|
|
pisprintf("%u", num);
|
|
}
|
|
PIString PIString::ultos(const ulong num) {
|
|
pisprintf("%lu", num);
|
|
}
|
|
PIString PIString::ulltos(const ullong num) {
|
|
pisprintf("%llu", num);
|
|
}
|
|
PIString PIString::dtos(const double num, char format, int precision) {
|
|
char f[8] = "%.";
|
|
int wr = snprintf(&(f[2]), 4, "%d", precision);
|
|
if (wr > 4) wr = 4;
|
|
f[2 + wr] = format;
|
|
f[3 + wr] = 0;
|
|
pisprintf(f, num);
|
|
}
|
|
#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 = "0x"_a;
|
|
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<int> digits;
|
|
llong ret = 0, m = 1;
|
|
bool neg = false;
|
|
char 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) {
|
|
// piCout << "appendFromChars";
|
|
if (s == 0) return;
|
|
int old_sz = size_s();
|
|
if (s == -1) s = strlen(c);
|
|
if (s <= 0) return;
|
|
#ifdef PIP_ICU
|
|
UErrorCode e((UErrorCode)0);
|
|
UConverter * cc = ucnv_open(codepage, &e);
|
|
if (cc) {
|
|
d.enlarge(s);
|
|
e = (UErrorCode)0;
|
|
int sz = ucnv_toUChars(cc, (UChar *)(d.data(old_sz)), s, c, s, &e);
|
|
d.resize(old_sz + sz);
|
|
ucnv_close(cc);
|
|
return;
|
|
}
|
|
#else
|
|
# ifdef WINDOWS
|
|
int sz = MultiByteToWideChar((uint)(uintptr_t)codepage, MB_ERR_INVALID_CHARS, c, s, 0, 0);
|
|
if (sz <= 0) return;
|
|
d.enlarge(sz);
|
|
MultiByteToWideChar((uint)(uintptr_t)codepage, MB_ERR_INVALID_CHARS, c, s, (LPWSTR)d.data(old_sz), sz);
|
|
# else
|
|
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> ucs2conv;
|
|
std::u16string ucs2 = ucs2conv.from_bytes(c, c + s);
|
|
d.enlarge(ucs2.size());
|
|
ucs2.copy((char16_t *)d.data(old_sz), ucs2.size());
|
|
# endif
|
|
#endif
|
|
changed_ = true;
|
|
}
|
|
|
|
|
|
PIString PIString::fromConsole(const PIByteArray & ba) {
|
|
PIString ret;
|
|
if (ba.isNotEmpty()) ret.appendFromChars((const char *)ba.data(), ba.size(), __sysoemname__);
|
|
return ret;
|
|
}
|
|
|
|
|
|
PIString PIString::fromConsole(const char * s) {
|
|
PIString ret;
|
|
if (!s) return ret;
|
|
if (s[0] != '\0') ret.appendFromChars(s, -1, __sysoemname__);
|
|
return ret;
|
|
}
|
|
|
|
|
|
PIString PIString::fromSystem(const PIByteArray & ba) {
|
|
PIString ret;
|
|
if (ba.isNotEmpty()) ret.appendFromChars((const char *)ba.data(), ba.size(), __syslocname__);
|
|
return ret;
|
|
}
|
|
|
|
|
|
PIString PIString::fromSystem(const char * s) {
|
|
PIString ret;
|
|
if (!s) return ret;
|
|
if (s[0] != '\0') ret.appendFromChars(s, -1, __syslocname__);
|
|
return ret;
|
|
}
|
|
|
|
|
|
PIString PIString::fromUTF8(const char * s, int l) {
|
|
PIString ret;
|
|
if (!s) return ret;
|
|
if (s[0] != '\0') {
|
|
if ((uchar)s[0] == 0xEF && (uchar)s[1] == 0xBB && (uchar)s[2] == 0xBF) {
|
|
s += 3;
|
|
if (l > 0) l -= 3;
|
|
if (s[0] == '\0') return ret;
|
|
}
|
|
ret.appendFromChars(s, l, __utf8name__);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
PIString PIString::fromUTF8(const PIByteArray & ba) {
|
|
PIString ret;
|
|
if (ba.isNotEmpty()) {
|
|
const char * data = (const char *)ba.data();
|
|
int size = ba.size();
|
|
if (ba.size() >= 3) {
|
|
if (ba[0] == 0xEF && ba[1] == 0xBB && ba[2] == 0xBF) {
|
|
data += 3;
|
|
size -= 3;
|
|
if (size == 0) return ret;
|
|
}
|
|
}
|
|
ret.appendFromChars(data, size, __utf8name__);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
PIString PIString::fromAscii(const char * s) {
|
|
return fromAscii(s, strlen(s));
|
|
}
|
|
|
|
|
|
PIString PIString::fromAscii(const char * s, int len) {
|
|
PIString ret;
|
|
ret.resize(len);
|
|
for (int l = 0; l < len; ++l) {
|
|
ret[l] = s[l];
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
PIString PIString::fromAscii(const PIByteArray & ascii) {
|
|
PIString ret;
|
|
ret.resize(ascii.size());
|
|
for (int l = 0; l < ret.size_s(); ++l) {
|
|
ret[l] = PIChar(ascii[l]);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
PIString PIString::fromCodepage(const char * s, const char * c) {
|
|
PIString ret;
|
|
if (s[0] > '\0')
|
|
ret.appendFromChars(s,
|
|
-1
|
|
#ifdef PIP_ICU
|
|
,
|
|
c
|
|
#else
|
|
# ifdef WINDOWS
|
|
,
|
|
__utf8name__
|
|
# endif
|
|
#endif
|
|
);
|
|
return ret;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~english
|
|
//! Example:
|
|
//! \~russian
|
|
//! Пример:
|
|
//! \~\code
|
|
//! piCout << PIString::readableSize(512); // 512 B
|
|
//! piCout << PIString::readableSize(5120); // 5.0 KiB
|
|
//! piCout << PIString::readableSize(512000); // 500.0 KiB
|
|
//! piCout << PIString::readableSize(5120000); // 4.8 MiB
|
|
//! piCout << PIString::readableSize(512000000); // 488.2 MiB
|
|
//! piCout << PIString::readableSize(51200000000); // 47.6 GiB
|
|
//! \endcode
|
|
PIString PIString::readableSize(llong bytes) {
|
|
PIString s;
|
|
s.setReadableSize(bytes);
|
|
return s;
|
|
}
|
|
|
|
|
|
void PIString::buildData(const char * cp) const {
|
|
if (!changed_ && (last_loc_ == cp) && data_) return;
|
|
last_loc_ = cp;
|
|
changed_ = false;
|
|
deleteData();
|
|
#ifdef PIP_ICU
|
|
UErrorCode e((UErrorCode)0);
|
|
UConverter * cc = ucnv_open(cp, &e);
|
|
if (cc) {
|
|
const size_t len = MB_CUR_MAX * size() + 1;
|
|
data_ = (char *)malloc(len);
|
|
int sz = ucnv_fromUChars(cc, data_, len, (const UChar *)(d.data()), d.size_s(), &e);
|
|
ucnv_close(cc);
|
|
data_[sz] = '\0';
|
|
data_size_ = sz;
|
|
return;
|
|
}
|
|
#else
|
|
# ifdef WINDOWS
|
|
int sz = WideCharToMultiByte((uint)(uintptr_t)cp, 0, (LPCWCH)d.data(), d.size_s(), 0, 0, NULL, NULL);
|
|
if (sz <= 0) {
|
|
data_ = (char *)malloc(1);
|
|
data_[0] = '\0';
|
|
return;
|
|
}
|
|
data_ = (char *)malloc(sz + 1);
|
|
WideCharToMultiByte((uint)(uintptr_t)cp, 0, (LPCWCH)d.data(), d.size_s(), (LPSTR)data_, sz, NULL, NULL);
|
|
data_[sz] = '\0';
|
|
data_size_ = sz;
|
|
return;
|
|
# else
|
|
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> ucs2conv;
|
|
std::string u8str = ucs2conv.to_bytes((char16_t *)d.data(), (char16_t *)d.data() + d.size());
|
|
data_ = (char *)malloc(u8str.size() + 1);
|
|
strcpy(data_, u8str.c_str());
|
|
data_size_ = u8str.size();
|
|
# endif
|
|
#endif
|
|
}
|
|
|
|
|
|
void PIString::deleteData() const {
|
|
if (!data_) return;
|
|
free(data_);
|
|
data_ = nullptr;
|
|
data_size_ = 0;
|
|
}
|
|
|
|
|
|
void PIString::trimsubstr(int & st, int & fn) const {
|
|
for (int i = 0; i < d.size_s(); ++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 = d.size_s() - 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
PIString PIString::minArgPlaceholder() {
|
|
if (size() < 2) return {};
|
|
int min = -1;
|
|
PIString ret, tmp;
|
|
for (int i = 0; i < size_s(); ++i) {
|
|
if (at(i) == '%') {
|
|
int j = 0;
|
|
for (j = i + 1; j < size_s(); ++j) {
|
|
if (!at(j).isDigit()) break;
|
|
}
|
|
bool ok = false;
|
|
tmp = mid(i + 1, j - i - 1);
|
|
int cur = tmp.toInt(10, &ok);
|
|
if (!ok) continue;
|
|
if (min < 0 || min > cur) {
|
|
min = cur;
|
|
ret = tmp;
|
|
}
|
|
}
|
|
}
|
|
if (ret.isEmpty()) return {};
|
|
return "%" + ret;
|
|
}
|
|
|
|
|
|
uint PIString::hash() const {
|
|
return piHashData((const uchar *)d.data(), d.size() * sizeof(PIChar));
|
|
}
|
|
|
|
|
|
PIByteArray PIString::toAscii() const {
|
|
if (isEmpty()) return PIByteArray();
|
|
PIByteArray ret;
|
|
ret.resize(size());
|
|
for (int i = 0; i < size_s(); ++i) {
|
|
ret[i] = uchar(at(i).ch);
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
PIByteArray PIString::toSystem() const {
|
|
if (isEmpty()) return PIByteArray();
|
|
buildData(__syslocname__);
|
|
return PIByteArray(data_, strlen(data_));
|
|
}
|
|
|
|
|
|
PIByteArray PIString::toUTF8() const {
|
|
if (isEmpty()) return PIByteArray();
|
|
buildData(__utf8name__);
|
|
return PIByteArray(data_, strlen(data_));
|
|
}
|
|
|
|
|
|
PIByteArray PIString::toCharset(const char * c) const {
|
|
if (isEmpty()) return PIByteArray();
|
|
buildData(
|
|
#ifdef PIP_ICU
|
|
c
|
|
#else
|
|
# ifdef WINDOWS
|
|
__utf8name__
|
|
# endif
|
|
#endif
|
|
);
|
|
return PIByteArray(data_, strlen(data_));
|
|
}
|
|
|
|
|
|
PIString PIString::simplified() const {
|
|
PIString ret(*this);
|
|
for (int i = 0; i < ret.size_s(); ++i)
|
|
if (!ret[i].isAscii()) ret[i] = '?';
|
|
return ret;
|
|
}
|
|
|
|
|
|
PIString & PIString::mask(const PIString & symbols, PIChar mc) {
|
|
for (int i = 0; i < size_s(); ++i)
|
|
if (symbols.contains(at(i))) {
|
|
insert(i, mc);
|
|
++i;
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIString & PIString::unmask(const PIString & symbols, const PIChar mc) {
|
|
for (int i = 0; i < size_s() - 1; ++i)
|
|
if (at(i) == mc) {
|
|
if (symbols.contains(at(i + 1))) {
|
|
remove(i);
|
|
}
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIString & PIString::operator+=(const char * str) {
|
|
if (!str) return *this;
|
|
appendFromChars(str, -1, __syslocname__);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIString::~PIString() {
|
|
deleteData();
|
|
}
|
|
|
|
|
|
PIString & PIString::operator+=(const wchar_t * str) {
|
|
if (!str) return *this;
|
|
int i = -1;
|
|
while (str[++i]) {
|
|
d.push_back(PIChar(str[i]));
|
|
}
|
|
changed_ = true;
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIString & PIString::operator+=(const char16_t * str) {
|
|
if (!str) return *this;
|
|
int i = -1;
|
|
while (str[++i]) {
|
|
d.push_back(PIChar(str[i]));
|
|
}
|
|
changed_ = true;
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIString & PIString::operator+=(const PIString & str) {
|
|
d.append(str.d);
|
|
changed_ = true;
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIString & PIString::operator+=(const PIConstChars & str) {
|
|
if (!str.isEmpty()) {
|
|
size_t os = d.size();
|
|
d.enlarge(str.size());
|
|
for (size_t l = 0; l < d.size(); ++l) {
|
|
d[os + l] = str[l];
|
|
}
|
|
}
|
|
changed_ = true;
|
|
return *this;
|
|
}
|
|
|
|
|
|
bool PIString::operator==(const PIString & str) const {
|
|
return d == str.d;
|
|
}
|
|
|
|
|
|
bool PIString::operator!=(const PIString & str) const {
|
|
return d != str.d;
|
|
}
|
|
|
|
|
|
bool PIString::operator<(const PIString & str) const {
|
|
size_t l = str.size();
|
|
if (size() < l) return true;
|
|
if (size() > l) return false;
|
|
for (size_t i = 0; i < l; ++i) {
|
|
if (at(i) == str.at(i)) continue;
|
|
if (at(i) < str.at(i))
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool PIString::operator>(const PIString & str) const {
|
|
size_t l = str.size();
|
|
if (size() < l) return false;
|
|
if (size() > l) return true;
|
|
for (size_t i = 0; i < l; ++i) {
|
|
if (at(i) == str.at(i)) continue;
|
|
if (at(i) < str.at(i))
|
|
return false;
|
|
else
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~english
|
|
//! If "len" < 0 then returns substring from symbol "start" to end.
|
|
//! "start" should be >= 0.
|
|
//! \~russian
|
|
//! Если "len" < 0 тогда возвращается подстрока от символа "start" и до конца.
|
|
//! "start" должен быть >= 0.
|
|
//! \~\code
|
|
//! PIString s("0123456789");
|
|
//! piCout << s.mid(0, -1); // s = "0123456789"
|
|
//! piCout << s.mid(0, 2); // s = "01"
|
|
//! piCout << s.mid(3); // s = "3456789"
|
|
//! piCout << s.mid(3, 4); // s = "3456"
|
|
//! piCout << s.mid(7, 1); // s = "7"
|
|
//! piCout << s.mid(7, 4); // s = "789"
|
|
//! piCout << s.mid(-1); // s = ""
|
|
//! \endcode
|
|
//! \~\sa \a left(), \a right()
|
|
PIString PIString::mid(int start, int len) const {
|
|
if (len == 0 || start >= length()) return PIString();
|
|
if (start < 0) {
|
|
return PIString();
|
|
}
|
|
if (len < 0) {
|
|
return PIString(d.data(start), size_s() - start);
|
|
} else {
|
|
if (len > length() - start) len = length() - start;
|
|
return PIString(d.data(start), len);
|
|
}
|
|
return PIString();
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~english
|
|
//! If "len" < 0 then remove substring from symbol "start" to end.
|
|
//! "start" should be >= 0.
|
|
//! \~russian
|
|
//! Если "len" < 0 тогда удаляется подстрока от символа "start" и до конца.
|
|
//! "start" должен быть >= 0.
|
|
//! \~\code
|
|
//! PIString s("0123456789");
|
|
//! s.cutMid(1, 3);
|
|
//! piCout << s; // s = "0456789"
|
|
//! s.cutMid(0, 2);
|
|
//! piCout << s; // s = "56789"
|
|
//! s.cutMid(3, -1);
|
|
//! piCout << s; // s = "567"
|
|
//! s.cutMid(-1, -1);
|
|
//! piCout << s; // s = "567"
|
|
//! \endcode
|
|
//! \~\sa \a cutLeft(), \a cutRight()
|
|
PIString & PIString::cutMid(int start, int len) {
|
|
if (len == 0) return *this;
|
|
if (start < 0) {
|
|
return *this;
|
|
}
|
|
if (len < 0) {
|
|
d.remove(start, size() - start);
|
|
} else {
|
|
if (len > length() - start) len = length() - start;
|
|
d.remove(start, len);
|
|
}
|
|
changed_ = true;
|
|
return *this;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~english Remove spaces, tabulations, line feeds and null symbols:
|
|
//! \~russian Удаляет пробелы, табуляцию, переводы строк и нулевые символы:
|
|
//! \~ ' ', '\\n', '\\r', '\\t', '\\0'
|
|
//! \~\code
|
|
//! PIString s(" \t string \n");
|
|
//! s.trim();
|
|
//! piCout << s; // s = "string"
|
|
//! \endcode
|
|
//! \~\sa \a trimmed()
|
|
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);
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~\code
|
|
//! PIString s("0123456789");
|
|
//! s.replace(2, 3, "_cut_");
|
|
//! piCout << s; // s = "01_cut_56789"
|
|
//! s.replace(0, 1, "one_");
|
|
//! piCout << s; // s = "one_1_cut_56789"
|
|
//! \endcode
|
|
//! \~\sa \a replaced(), \a replaceAll()
|
|
PIString & PIString::replace(int from, int count, const PIString & with) {
|
|
count = piMini(count, length() - from);
|
|
if (count == with.size_s()) {
|
|
memcpy(d.data(from), with.d.data(), count * sizeof(PIChar));
|
|
} else {
|
|
d.remove(from, count);
|
|
d.insert(from, with.d);
|
|
}
|
|
changed_ = true;
|
|
return *this;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~english If "ok" is not null, it set to "true" if something was replaced
|
|
//! \~russian Если "ok" не null, то устанавливает в "true" если замена произведена
|
|
//! \~\code
|
|
//! PIString s("pip string");
|
|
//! bool ok;
|
|
//! s.replace("string", "conf", &ok);
|
|
//! piCout << s << ok; // s = "pip conf", true
|
|
//! s.replace("PIP", "PlInPr", &ok);
|
|
//! piCout << s << ok; // s = "pip conf", false
|
|
//! \endcode
|
|
//! \~\sa \a replaced(), \a replaceAll()
|
|
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;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~\code
|
|
//! PIString s("substrings");
|
|
//! s.replaceAll("s", "_");
|
|
//! piCout << s; // s = "_ub_tring_"
|
|
//! \endcode
|
|
//! \~\sa \a replace(), \a replaced(), \a replacedAll()
|
|
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) d.insert(i, PIDeque<PIChar>((size_t)dl));
|
|
if (dl < 0) d.remove(i, -dl);
|
|
memcpy(d.data(i), with.d.data(), with.size() * sizeof(PIChar));
|
|
i += dl;
|
|
}
|
|
}
|
|
changed_ = true;
|
|
return *this;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~\code
|
|
//! PIString s("substrings");
|
|
//! s.replaceAll("s", '_');
|
|
//! piCout << s; // s = "_ub_tring_"
|
|
//! \endcode
|
|
//! \~\sa \a replace(), \a replaced(), \a replacedAll()
|
|
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) d.remove(i, dl);
|
|
d[i] = PIChar(with);
|
|
}
|
|
changed_ = true;
|
|
return *this;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~\code
|
|
//! PIString s("substrings");
|
|
//! s.replaceAll('s', '_');
|
|
//! piCout << s; // s = "_ub_tring_"
|
|
//! \endcode
|
|
//! \~\sa \a replace(), \a replaced(), \a replacedAll()
|
|
PIString & PIString::replaceAll(const char what, const char with) {
|
|
int l = length();
|
|
for (int i = 0; i < l; ++i) {
|
|
if (at(i) == what) d[i] = with;
|
|
}
|
|
changed_ = true;
|
|
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 (d.at(j + i) != str.at(j)) {
|
|
match = false;
|
|
break;
|
|
}
|
|
}
|
|
if (!match) continue;
|
|
d.remove(i, l);
|
|
i -= l;
|
|
}
|
|
changed_ = true;
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIString & PIString::insert(int index, const PIString & str) {
|
|
d.insert(index, str.d);
|
|
changed_ = true;
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIString & PIString::elide(int size, float pos) {
|
|
static const PIString s_dotdot = ".."_a;
|
|
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);
|
|
d.remove(ls, length() - ns);
|
|
d.insert(ls, s_dotdot.d);
|
|
changed_ = true;
|
|
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;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~\code
|
|
//! PIString s("012345012345");
|
|
//! piCout << s.find('-'); // -1
|
|
//! piCout << s.find('3'); // 3
|
|
//! piCout << s.find('3', 4); // 9
|
|
//! piCout << s.find('3', 10); // -1
|
|
//! \endcode
|
|
//! \~\sa \a findAny(), \a findLast(), \a findAnyLast(), \a findWord(), \a findCWord(), \a findRange()
|
|
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;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~\code
|
|
//! PIString s("012345012345");
|
|
//! piCout << s.find("-"); // -1
|
|
//! piCout << s.find("34"); // 3
|
|
//! piCout << s.find("3", 4); // 9
|
|
//! piCout << s.find("3", 10); // -1
|
|
//! \endcode
|
|
//! \~\sa \a findAny(), \a findLast(), \a findAnyLast(), \a findWord(), \a findCWord(), \a findRange()
|
|
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;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~\code
|
|
//! piCout << PIString("1.str").findAny(".,:"); // 1
|
|
//! piCout << PIString("1,str").findAny(".,:"); // 1
|
|
//! piCout << PIString("1:str").findAny(".,:"); // 1
|
|
//! \endcode
|
|
//! \~\sa \a find(), \a findLast(), \a findAnyLast(), \a findWord(), \a findCWord(), \a findRange()
|
|
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;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~\code
|
|
//! PIString s("012345012345");
|
|
//! piCout << s.findLast('-'); // -1
|
|
//! piCout << s.findLast('3'); // 9
|
|
//! piCout << s.findLast('3', 4); // 9
|
|
//! piCout << s.findLast('3', 10); // -1
|
|
//! \endcode
|
|
//! \~\sa \a find(), \a findAny(), \a findAnyLast(), \a findWord(), \a findCWord(), \a findRange()
|
|
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(PIChar c, const int start) const {
|
|
for (int i = length() - 1; i >= start; --i) {
|
|
if (at(i) == c) return i;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~\code
|
|
//! PIString s("012345012345");
|
|
//! piCout << s.findLast("-"); // -1
|
|
//! piCout << s.findLast("34"); // 9
|
|
//! piCout << s.findLast("3", 4); // 9
|
|
//! piCout << s.findLast("3", 10); // -1
|
|
//! \endcode
|
|
//! \~\sa \a find(), \a findAny(), \a findAnyLast(), \a findWord(), \a findCWord(), \a findRange()
|
|
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;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~\code
|
|
//! piCout << PIString(".str.0").findAnyLast(".,:"); // 4
|
|
//! piCout << PIString(".str,0").findAnyLast(".,:"); // 4
|
|
//! piCout << PIString(".str:0").findAnyLast(".,:"); // 4
|
|
//! \endcode
|
|
//! \~\sa \a find(), \a findAny(), \a findLast(), \a findWord(), \a findCWord(), \a findRange()
|
|
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;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~\code
|
|
//! PIString s("this is <PIP>");
|
|
//! piCout << s.findWord("this"); // 0
|
|
//! piCout << s.findWord("is"); // 5
|
|
//! piCout << s.findWord("PIP", 4); // -1
|
|
//! piCout << s.findWord("<PIP>", 4); // 8
|
|
//! \endcode
|
|
//! \~\sa \a find(), \a findAny(), \a findLast(), \a findAnyLast(), \a findCWord(), \a findRange()
|
|
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 = at(f - 1);
|
|
if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r')) {
|
|
ok = false;
|
|
continue;
|
|
}
|
|
}
|
|
if (f + wl < tl) {
|
|
c = at(f + wl);
|
|
if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r')) {
|
|
ok = false;
|
|
continue;
|
|
}
|
|
}
|
|
if (ok) return f;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~\code
|
|
//! PIString s("this::is <PIP>");
|
|
//! piCout << s.findCWord("this"); // 0
|
|
//! piCout << s.findCWord("is"); // 6
|
|
//! piCout << s.findCWord("PIP", 4); // 10
|
|
//! piCout << s.findCWord("<PIP>", 4); // 9
|
|
//! \endcode
|
|
//! \~\sa \a find(), \a findAny(), \a findLast(), \a findAnyLast(), \a findWord(), \a findRange()
|
|
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 = at(f - 1);
|
|
if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r' || (c != '_' && !c.isAlpha() && !c.isDigit()))) {
|
|
ok = false;
|
|
continue;
|
|
}
|
|
}
|
|
if (f + wl < tl) {
|
|
c = at(f + wl);
|
|
if (!(c == ' ' || c == '\t' || c == '\n' || c == '\r' || (c != '_' && !c.isAlpha() && !c.isDigit()))) {
|
|
ok = false;
|
|
continue;
|
|
}
|
|
}
|
|
if (ok) return f;
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~\code
|
|
//! PIString s(" {figures{inside}}");
|
|
//! int len = -1;
|
|
//! piCout << s.findRange('{', '}', '\\', 0, &len) << len << s.mid(2, len); // 2 15 figures{inside}
|
|
//! s = "\"text\\\"shielded\" next";
|
|
//! piCout << s.findRange('"', '"', '\\', 0, &len) << len << s.mid(1, len); // 1 14 text\"shielded
|
|
//! \endcode
|
|
//! \~\sa \a find(), \a findAny(), \a findLast(), \a findAnyLast(), \a findWord(), \a findCWord()
|
|
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::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;
|
|
}
|
|
|
|
|
|
int PIString::entries(const PIString & str) const {
|
|
int sz = size_s(), ssz = str.size_s();
|
|
if (ssz == 0 || sz < ssz) return 0;
|
|
int btc = str.size_s() * sizeof(PIChar), ret = 0;
|
|
for (int i = 0; i < sz - ssz + 1; ++i) {
|
|
if (piCompareBinary(d.data(i), str.d.data(0), btc)) ++ret;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
int PIString::lineNumber(int pos) const {
|
|
if (isEmpty()) return 0;
|
|
if (pos < 0 || pos >= size_s()) pos = size_s() - 1;
|
|
int line = 1;
|
|
for (int i = 0; i < pos; ++i) {
|
|
if (at(i) == '\n') ++line;
|
|
}
|
|
return line;
|
|
}
|
|
|
|
|
|
bool PIString::startsWith(const PIChar c) const {
|
|
if (isEmpty()) return false;
|
|
return front() == c;
|
|
}
|
|
|
|
|
|
bool PIString::startsWith(const PIString & str) const {
|
|
if (size() < str.size()) return false;
|
|
return str == left(str.size());
|
|
}
|
|
|
|
|
|
bool PIString::endsWith(const PIChar c) const {
|
|
if (isEmpty()) return false;
|
|
return back() == c;
|
|
}
|
|
|
|
|
|
bool PIString::endsWith(const PIString & str) const {
|
|
if (size() < str.size()) return false;
|
|
return str == right(str.size());
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~\code
|
|
//! piCout << PIString("true").toBool(); // true
|
|
//! piCout << PIString("Yes").toBool(); // true
|
|
//! piCout << PIString(" TRUE ").toBool(); // true
|
|
//! piCout << PIString(" 1 ").toBool(); // true
|
|
//! piCout << PIString("0").toBool(); // false
|
|
//! piCout << PIString("0.1").toBool(); // true
|
|
//! piCout << PIString("-1").toBool(); // false
|
|
//! piCout << PIString("").toBool(); // false
|
|
//! \endcode
|
|
bool PIString::toBool() const {
|
|
static const PIString s_true = "true"_a;
|
|
static const PIString s_yes = "yes"_a;
|
|
static const PIString s_on = "on"_a;
|
|
static const PIString s_ok = "ok"_a;
|
|
PIString s(*this);
|
|
s = s.trimmed().toLowerCase();
|
|
if (s == s_true || s == s_yes || s == s_on || s == s_ok) return true;
|
|
if (toDouble() > 0.) return true;
|
|
return false;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~\code
|
|
//! PIString s("\t ! word");
|
|
//! piCout << s.takeSymbol(); // "!"
|
|
//! piCout << s.takeSymbol(); // "w"
|
|
//! piCout << s.takeSymbol(); // "o"
|
|
//! piCout << s; // "rd"
|
|
//! \endcode
|
|
//! \~\sa \a takeWord(), \a takeCWord(), \a takeLine(), \a takeNumber(), \a takeRange()
|
|
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;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~\code
|
|
//! PIString s("some words\nnew line ");
|
|
//! piCout << s.takeWord(); // "some"
|
|
//! piCout << s.takeWord(); // "words"
|
|
//! piCout << s.takeWord(); // "new"
|
|
//! piCout << s; // " line "
|
|
//! \endcode
|
|
//! \~\sa \a takeSymbol(), \a takeCWord(), \a takeLine(), \a takeNumber(), \a takeRange()
|
|
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;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~\code
|
|
//! \endcode
|
|
//! \~\sa \a takeSymbol(), \a takeWord(), \a takeLine(), \a takeNumber(), \a takeRange()
|
|
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;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~\code
|
|
//! PIString s("some words\nnew line \n\nend");
|
|
//! piCout << s.takeLine(); // "some words"
|
|
//! piCout << s.takeLine(); // "new line "
|
|
//! piCout << s.takeLine(); // ""
|
|
//! piCout << s; // "end"
|
|
//! \endcode
|
|
//! \~\sa \a takeSymbol(), \a takeWord(), \a takeCWord(), \a takeNumber(), \a takeRange()
|
|
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;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~\code
|
|
//! PIString s(" 0xFF -99 1.2E+5f 1000L");
|
|
//! piCout << s.takeNumber(); // "0xFF"
|
|
//! piCout << s.takeNumber(); // "-99"
|
|
//! piCout << s.takeNumber(); // "1.2E+5f"
|
|
//! piCout << s.takeNumber(); // "1000L"
|
|
//! piCout << s; // ""
|
|
//! \endcode
|
|
//! \~\sa \a takeSymbol(), \a takeWord(), \a takeCWord(), \a takeLine(), \a takeRange()
|
|
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') || c == '.') {
|
|
le = i;
|
|
if (ls < 0) ls = i;
|
|
if (c == '.')
|
|
phase = 3;
|
|
else
|
|
phase = 2;
|
|
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;
|
|
default: 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::takeInteger() {
|
|
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) ws = i;
|
|
if (!c.isDigit()) {
|
|
we = i;
|
|
break;
|
|
}
|
|
if (we >= 0) break;
|
|
}
|
|
}
|
|
ret = mid(ws, we - ws);
|
|
cutLeft(we < 0 ? sz : we);
|
|
return ret;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~english "shield" symbol prevent analysis of the next symbol
|
|
//! \~russian Символ "shield" экранирует следующий символ
|
|
//! \~\code
|
|
//! PIString s(" {figures{inside}}");
|
|
//! piCout << s.takeRange('{', '}'); // "figures{inside}"
|
|
//! piCout << s; // ""
|
|
//! s = "\"text\\\"shielded\" next";
|
|
//! piCout << s.takeRange('"', '"'); // "text\"shielded"
|
|
//! piCout << s; // " next"
|
|
//! \endcode
|
|
//! \~\sa \a takeSymbol(), \a takeWord(), \a takeLine(), \a takeNumber()
|
|
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;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~\code
|
|
//! PIString s("a(b(c)d)e");
|
|
//! piCout << s.inBrackets('(', ')'); // "b(c)d"
|
|
//! piCout << s; // s = "a(b(c)d)e"
|
|
//! \endcode
|
|
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();
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~english
|
|
//! This function fill internal buffer by sequence of chars.
|
|
//! Length of this buffer is count of symbols + end byte '\0'.
|
|
//! Returned pointer is valid until next execution of this function.
|
|
//! \~russian
|
|
//! Этот метод заполняет внутренный байтовый буфер. Размер
|
|
//! этого буфера равен количеству символов строки + завершающий байт '\0'.
|
|
//! Возвращаемый указатель действителен до следующего вызова этого метода.
|
|
//! \~\code
|
|
//! piCout << PIString("0123456789").data(); // 0123456789
|
|
//! piCout << PIString("№1").data(); // №1
|
|
//! \endcode
|
|
//! \~\sa \a dataConsole(), \a dataUTF8()
|
|
const char * PIString::data() const {
|
|
if (isEmpty()) return "";
|
|
buildData(__syslocname__);
|
|
return data_;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~english
|
|
//! This function fill internal buffer by sequence of chars.
|
|
//! Length of this buffer is count of symbols + end byte '\0'.
|
|
//! Returned pointer is valid until next execution of this function.
|
|
//! \~russian
|
|
//! Этот метод заполняет внутренный байтовый буфер. Размер
|
|
//! этого буфера равен количеству символов строки + завершающий байт '\0'.
|
|
//! Возвращаемый указатель действителен до следующего вызова этого метода.
|
|
//! \~\sa \a data(), \a dataUTF8()
|
|
const char * PIString::dataConsole() const {
|
|
if (isEmpty()) return "";
|
|
buildData(__sysoemname__);
|
|
return data_;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~english
|
|
//! This function fill internal buffer by sequence of chars.
|
|
//! Length of this buffer is count of symbols + end byte '\0'.
|
|
//! Returned pointer is valid until next execution of this function.
|
|
//! \~russian
|
|
//! Этот метод заполняет внутренный байтовый буфер. Размер
|
|
//! этого буфера равен количеству символов строки + завершающий байт '\0'.
|
|
//! Возвращаемый указатель действителен до следующего вызова этого метода.
|
|
//! \~\sa \a data(), \a dataConsole()
|
|
const char * PIString::dataUTF8() const {
|
|
if (isEmpty()) return "";
|
|
buildData(__utf8name__);
|
|
return data_;
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~english
|
|
//! This function fill internal buffer by sequence of chars.
|
|
//! Length of this buffer is count of symbols + end byte '\0'.
|
|
//! Returned pointer is valid until next execution of this function.
|
|
//! \~russian
|
|
//! Этот метод заполняет внутренный байтовый буфер. Размер
|
|
//! этого буфера равен количеству символов строки + завершающий байт '\0'.
|
|
//! Возвращаемый указатель действителен до следующего вызова этого метода.
|
|
//! \~\sa \a dataConsole(), \a dataUTF8()
|
|
const char * PIString::dataAscii() const {
|
|
if (isEmpty()) return "";
|
|
deleteData();
|
|
data_ = (char *)malloc(size() + 1);
|
|
for (int i = 0; i < size_s(); ++i) {
|
|
data_[i] = uchar(at(i).ch);
|
|
}
|
|
data_[size()] = '\0';
|
|
return data_;
|
|
}
|
|
|
|
|
|
PIString PIString::toUpperCase() const {
|
|
PIString str(*this);
|
|
int l = str.size();
|
|
for (int i = 0; i < l; ++i) {
|
|
str.d[i] = str.d[i].toUpper();
|
|
}
|
|
return str;
|
|
}
|
|
|
|
|
|
PIString PIString::toLowerCase() const {
|
|
PIString str(*this);
|
|
int l = str.size();
|
|
for (int i = 0; i < l; ++i) {
|
|
str.d[i] = str.d[i].toLower();
|
|
}
|
|
return str;
|
|
}
|
|
|
|
|
|
char PIString::toChar() const {
|
|
char v;
|
|
sscanf(dataAscii(), "%c", &v);
|
|
return v;
|
|
}
|
|
|
|
|
|
float PIString::toFloat() const {
|
|
return toDecimal<float>(*this);
|
|
}
|
|
|
|
|
|
double PIString::toDouble() const {
|
|
return toDecimal<double>(*this);
|
|
}
|
|
|
|
|
|
ldouble PIString::toLDouble() const {
|
|
return toDecimal<ldouble>(*this);
|
|
}
|
|
|
|
|
|
//! \~\details
|
|
//! \~english
|
|
//! Example:
|
|
//! \~russian
|
|
//! Пример:
|
|
//! \~\code
|
|
//! PIString s;
|
|
//! s.setReadableSize(512);
|
|
//! piCout << s; // 512 B
|
|
//! s.setReadableSize(5120);
|
|
//! piCout << s; // 5.0 KiB
|
|
//! s.setReadableSize(512000);
|
|
//! piCout << s; // 500.0 KiB
|
|
//! s.setReadableSize(5120000);
|
|
//! piCout << s; // 4.8 MiB
|
|
//! s.setReadableSize(512000000);
|
|
//! piCout << s; // 488.2 MiB
|
|
//! s.setReadableSize(51200000000);
|
|
//! piCout << s; // 47.6 GiB
|
|
//! \endcode
|
|
PIString & PIString::setReadableSize(llong bytes) {
|
|
clear();
|
|
if (bytes < 1024) {
|
|
*this += (PIString::fromNumber(bytes) + " "_a + "B"_tr("PIString"));
|
|
return *this;
|
|
}
|
|
double fres = bytes;
|
|
llong res = bytes;
|
|
auto checkRange = [this, &fres, &res](const PIString & unit, bool last = false) {
|
|
fres = res / 1024.;
|
|
res /= 1024;
|
|
fres -= res;
|
|
if (res < 1024 || last) {
|
|
*this += (PIString::fromNumber(res) + "."_a + PIString::fromNumber(llong(fres * 10)).left(1) + " "_a + unit);
|
|
return true;
|
|
}
|
|
return false;
|
|
};
|
|
if (checkRange("KiB"_tr("PIString"))) return *this;
|
|
if (checkRange("MiB"_tr("PIString"))) return *this;
|
|
if (checkRange("GiB"_tr("PIString"))) return *this;
|
|
if (checkRange("TiB"_tr("PIString"))) return *this;
|
|
if (checkRange("PiB"_tr("PIString"))) return *this;
|
|
if (checkRange("EiB"_tr("PIString"))) return *this;
|
|
if (checkRange("ZiB"_tr("PIString"))) return *this;
|
|
checkRange("YiB"_tr("PIString"), true);
|
|
return *this;
|
|
}
|
|
|
|
|
|
PIString & PIString::arg(const PIString & v) {
|
|
auto ph = minArgPlaceholder();
|
|
if (!ph.isEmpty()) replaceAll(ph, v);
|
|
return *this;
|
|
}
|
|
|
|
|
|
const static PIString _versionDelims_ = "._-+"_a;
|
|
|
|
|
|
void parseVersion(PIString s, PIVector<int> & 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, ".0"_a);
|
|
}
|
|
}
|
|
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("pre"_a)) {
|
|
s.cutLeft(3);
|
|
ret -= 1;
|
|
}
|
|
if (s.startsWith("rc"_a)) {
|
|
s.cutLeft(2);
|
|
ret += s.toInt();
|
|
}
|
|
if (s.startsWith("r"_a)) {
|
|
s.cutLeft(1);
|
|
ret += 10000 + s.toInt();
|
|
}
|
|
if (s == "alpha"_a) ret -= 4;
|
|
if (s == "beta"_a) ret -= 2;
|
|
return ret;
|
|
}
|
|
|
|
|
|
//! \relatesalso PIString
|
|
//! \~\details
|
|
//! \~english
|
|
//! 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" < "rc[N]" < "" < "r[N]".
|
|
//! Example:
|
|
//! \~russian
|
|
//! Этот метод разбирает версии на числовые части и метку. Затем сравнивает
|
|
//! не более чем "components" частей. Если различий нет, то сравниваются
|
|
//! метки. Каждой метке соответствует своё значение так, что
|
|
//! "prealpha" < "alpha" < "prebeta" < "beta" < "rc[N]" < "" < "r[N]".
|
|
//! Пример:
|
|
//! \~\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_r2", "1.0.0", 3); // 0, =
|
|
//! piCout << versionCompare(".2-alpha", "0.2_alpha"); // 0, =
|
|
//! piCout << versionCompare("1_prebeta", "1.0_alpha"); // 1, >
|
|
//! \endcode
|
|
//! \~\return
|
|
//! \~english
|
|
//! * 0 - equal
|
|
//! * 1 - v0 > v1
|
|
//! * -1 - v0 < v1
|
|
//! \~russian
|
|
//! * 0 - равны
|
|
//! * 1 - v0 > v1
|
|
//! * -1 - v0 < v1
|
|
int versionCompare(const PIString & v0, const PIString & v1, int components) {
|
|
PIStringList strs[2];
|
|
PIVector<int> 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;
|
|
}
|
|
|
|
|
|
//! \relatesalso PIString
|
|
//! \~\details
|
|
//! \~english
|
|
//! Parse version as described in \a versionCompare() and returns
|
|
//! classic view of codes and labels: major.minor.revision[-build][_label].
|
|
//! Example:
|
|
//! \~russian
|
|
//! Разбирает версию по описанию \a versionCompare() и возвращает
|
|
//! классическое представление версии и метки: major.minor.revision[-build][_label].
|
|
//! Пример:
|
|
//! \~\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
|
|
PIString versionNormalize(const PIString & v) {
|
|
PIStringList strs;
|
|
PIVector<int> 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;
|
|
}
|