1151 lines
23 KiB
C++
1151 lines
23 KiB
C++
#include <stdio.h>
|
||
#include <stdlib.h>
|
||
#include <string.h>
|
||
#include <ctype.h>
|
||
#include <math.h>
|
||
#include <stdarg.h>
|
||
|
||
// gcc里面strncasecmp位于strings.h
|
||
#ifdef __GNUC__
|
||
#include <strings.h>
|
||
#endif
|
||
|
||
#include "_Core.h"
|
||
|
||
#include "ByteArray.h"
|
||
|
||
#include "SString.h"
|
||
|
||
char* utohex(uint value, byte size, char* string, bool upper);
|
||
//extern char* itoa(int value, char* string, int radix);
|
||
extern char* ltoa(Int64 value, char* string, int radix);
|
||
extern char* utoa(uint value, char* string, int radix);
|
||
extern char* ultoa(UInt64 value, char* string, int radix);
|
||
char* dtostrf(double val, byte prec, char* sout, int len);
|
||
|
||
|
||
/******************************** String ********************************/
|
||
|
||
String::String(cstring cstr) : Array(Arr, ArrayLength(Arr))
|
||
{
|
||
init();
|
||
|
||
_Length = strlen(cstr);
|
||
if (_Length)
|
||
{
|
||
_Arr = (char*)cstr;
|
||
// 此时保证外部一定是0结尾
|
||
_Capacity = _Length + 1;
|
||
_canWrite = false;
|
||
}
|
||
}
|
||
|
||
String::String(const String& value) : Array(Arr, ArrayLength(Arr))
|
||
{
|
||
init();
|
||
*this = value;
|
||
}
|
||
|
||
String::String(String&& rval) : Array(Arr, ArrayLength(Arr))
|
||
{
|
||
init();
|
||
move(rval);
|
||
}
|
||
|
||
String::String(bool value) : Array(Arr, ArrayLength(Arr))
|
||
{
|
||
init();
|
||
|
||
Concat(value);
|
||
}
|
||
|
||
String::String(char c) : Array(Arr, ArrayLength(Arr))
|
||
{
|
||
init();
|
||
|
||
_Arr[0] = c;
|
||
_Arr[1] = 0;
|
||
_Length = 1;
|
||
}
|
||
|
||
String::String(byte value, int radix) : Array(Arr, ArrayLength(Arr))
|
||
{
|
||
init();
|
||
|
||
Concat(value, radix);
|
||
}
|
||
|
||
String::String(short value, int radix) : Array(Arr, ArrayLength(Arr))
|
||
{
|
||
init();
|
||
|
||
Concat(value, radix);
|
||
}
|
||
|
||
String::String(ushort value, int radix) : Array(Arr, ArrayLength(Arr))
|
||
{
|
||
init();
|
||
|
||
Concat(value, radix);
|
||
}
|
||
|
||
String::String(int value, int radix) : Array(Arr, ArrayLength(Arr))
|
||
{
|
||
init();
|
||
|
||
Concat(value, radix);
|
||
}
|
||
|
||
String::String(uint value, int radix) : Array(Arr, ArrayLength(Arr))
|
||
{
|
||
init();
|
||
|
||
Concat(value, radix);
|
||
}
|
||
|
||
String::String(Int64 value, int radix) : Array(Arr, ArrayLength(Arr))
|
||
{
|
||
init();
|
||
|
||
Concat(value, radix);
|
||
}
|
||
|
||
String::String(UInt64 value, int radix) : Array(Arr, ArrayLength(Arr))
|
||
{
|
||
init();
|
||
|
||
Concat(value, radix);
|
||
}
|
||
|
||
String::String(float value, int decimalPlaces) : Array(Arr, ArrayLength(Arr))
|
||
{
|
||
init();
|
||
|
||
Concat(value, decimalPlaces);
|
||
}
|
||
|
||
String::String(double value, int decimalPlaces) : Array(Arr, ArrayLength(Arr))
|
||
{
|
||
init();
|
||
|
||
Concat(value, decimalPlaces);
|
||
}
|
||
|
||
// 外部传入缓冲区供内部使用,内部计算字符串长度,注意长度减去零结束符
|
||
String::String(char* str, int length) : Array(str, length)
|
||
{
|
||
_Arr = str;
|
||
_Capacity = length - 1;
|
||
|
||
// 计算外部字符串长度
|
||
int len = strlen(str);
|
||
if (len >= length) len = length - 1;
|
||
_Length = len;
|
||
_Arr[_Length] = '\0';
|
||
}
|
||
|
||
// 外部传入缓冲区供内部使用,内部计算字符串长度,注意长度减去零结束符
|
||
String::String(char* str, int length, bool expand) : String(str, length)
|
||
{
|
||
Expand = expand;
|
||
}
|
||
|
||
// 包装静态字符串,直接使用,修改时扩容
|
||
String::String(cstring str, int length) : Array((char*)str, length)
|
||
{
|
||
// 此时不能保证外部一定是0结尾
|
||
_Capacity = length + 1;
|
||
_canWrite = false;
|
||
}
|
||
|
||
inline void String::init()
|
||
{
|
||
_Arr = Arr;
|
||
_Capacity = sizeof(Arr) - 1;
|
||
_Length = 0;
|
||
_Arr[0] = '\0';
|
||
}
|
||
|
||
void String::release()
|
||
{
|
||
Array::Release();
|
||
|
||
init();
|
||
}
|
||
|
||
bool String::CheckCapacity(int size)
|
||
{
|
||
int old = _Capacity;
|
||
CheckCapacity(size + 1, _Length);
|
||
if (old == _Capacity) return true;
|
||
|
||
// 强制最后一个字符为0
|
||
_Arr[_Length] = '\0';
|
||
|
||
_Capacity--;
|
||
|
||
return true;
|
||
}
|
||
|
||
void* String::Alloc(int len)
|
||
{
|
||
if (len <= sizeof(Arr))
|
||
{
|
||
_needFree = false;
|
||
return Arr;
|
||
}
|
||
else
|
||
{
|
||
_needFree = true;
|
||
return new byte[len];
|
||
}
|
||
}
|
||
|
||
String& String::copy(cstring cstr, int length)
|
||
{
|
||
if (!cstr || !length) return *this;
|
||
|
||
if (!CheckCapacity(length))
|
||
release();
|
||
else
|
||
{
|
||
_Length = length;
|
||
//strcpy(_Arr, cstr);
|
||
//!!! 特别注意要拷贝的长度
|
||
if (length) Buffer(_Arr, _Capacity).Copy(0, cstr, length);
|
||
_Arr[length] = '\0';
|
||
}
|
||
|
||
return *this;
|
||
}
|
||
|
||
void String::move(String& rhs)
|
||
{
|
||
/*
|
||
move逻辑:
|
||
1,如果右值是内部指针,则必须拷贝数据,因为右值销毁的时候,内部数据跟着释放
|
||
2,如果右值是外部指针,并且需要释放,则直接拿指针过来使用,由当前对象负责释放
|
||
3,如果右值是外部指针,而不需要释放,则拷贝数据,因为那指针可能是借用外部的栈内存
|
||
*/
|
||
|
||
if (rhs._Arr != rhs.Arr && rhs._needFree)
|
||
{
|
||
Array::move(rhs);
|
||
|
||
return;
|
||
}
|
||
|
||
SetLength(rhs.Length());
|
||
copy(rhs._Arr, rhs._Length);
|
||
}
|
||
|
||
// 修改时拷贝
|
||
bool String::CopyOrWrite()
|
||
{
|
||
// 如果不可写
|
||
if (!_canWrite) return CheckCapacity(_Length);
|
||
|
||
return false;
|
||
}
|
||
|
||
bool String::SetLength(int len, bool bak)
|
||
{
|
||
//if(!Array::SetLength(length, bak)) return false;
|
||
// 字符串的最大长度为容量减一,因为需要保留一个零结束字符
|
||
if (len < _Capacity)
|
||
{
|
||
_Length = len;
|
||
}
|
||
else
|
||
{
|
||
if (!CheckCapacity(len + 1, bak ? _Length : 0)) return false;
|
||
// 扩大长度
|
||
if (len > _Length) _Length = len;
|
||
}
|
||
|
||
_Arr[_Length] = '\0';
|
||
|
||
return true;
|
||
}
|
||
|
||
// 拷贝数据,默认-1长度表示当前长度
|
||
int String::Copy(int destIndex, const void* src, int len)
|
||
{
|
||
int rs = Buffer::Copy(destIndex, src, len);
|
||
if (!rs) return 0;
|
||
|
||
_Arr[_Length] = '\0';
|
||
|
||
return rs;
|
||
}
|
||
|
||
// 把数据复制到目标缓冲区,默认-1长度表示当前长度
|
||
int String::CopyTo(int srcIndex, void* dest, int len) const
|
||
{
|
||
int rs = Buffer::CopyTo(srcIndex, dest, len);
|
||
if (!rs) return 0;
|
||
|
||
((char*)dest)[rs] = '\0';
|
||
|
||
return rs;
|
||
}
|
||
|
||
String& String::operator = (const String& rhs)
|
||
{
|
||
if (this == &rhs) return *this;
|
||
|
||
if (rhs._Arr) copy(rhs._Arr, rhs._Length);
|
||
else release();
|
||
|
||
return *this;
|
||
}
|
||
|
||
String& String::operator = (String&& rval)
|
||
{
|
||
if (this != &rval) move(rval);
|
||
return *this;
|
||
}
|
||
|
||
String& String::operator = (cstring cstr)
|
||
{
|
||
if (cstr) copy(cstr, strlen(cstr));
|
||
else release();
|
||
|
||
return *this;
|
||
}
|
||
|
||
bool String::Concat(const Object& obj)
|
||
{
|
||
return Concat(obj.ToString());
|
||
}
|
||
|
||
bool String::Concat(const String& s)
|
||
{
|
||
return Concat(s._Arr, s._Length);
|
||
}
|
||
|
||
bool String::Concat(cstring cstr, int length)
|
||
{
|
||
if (!cstr) return false;
|
||
if (length == 0) return true;
|
||
|
||
int newlen = _Length + length;
|
||
if (!CheckCapacity(newlen)) return false;
|
||
|
||
//strcpy(_Arr + _Length, cstr);
|
||
Buffer(_Arr, _Capacity).Copy(_Length, cstr, length);
|
||
_Length = newlen;
|
||
_Arr[_Length] = '\0';
|
||
|
||
return true;
|
||
}
|
||
|
||
bool String::Concat(cstring cstr)
|
||
{
|
||
if (!cstr) return 0;
|
||
return Concat(cstr, strlen(cstr));
|
||
}
|
||
|
||
bool String::Concat(bool value)
|
||
{
|
||
return Concat(value ? "true" : "false");
|
||
}
|
||
|
||
bool String::Concat(char c)
|
||
{
|
||
if (!CheckCapacity(_Length + 1)) return false;
|
||
|
||
_Arr[_Length++] = c;
|
||
|
||
return true;
|
||
}
|
||
|
||
bool String::Concat(byte num, int radix)
|
||
{
|
||
// 十六进制固定长度
|
||
if (radix == 16 || radix == -16)
|
||
{
|
||
if (!CheckCapacity(_Length + (sizeof(num) << 1))) return false;
|
||
|
||
utohex(num, sizeof(num), _Arr + _Length, radix < 0);
|
||
_Length += (sizeof(num) << 1);
|
||
|
||
return true;
|
||
}
|
||
|
||
char buf[1 + 3 * sizeof(byte)];
|
||
#if defined(_MSC_VER)
|
||
_itoa_s(num, buf, sizeof(buf), radix);
|
||
#else
|
||
itoa(num, buf, radix);
|
||
#endif
|
||
|
||
return Concat(buf, strlen(buf));
|
||
}
|
||
|
||
bool String::Concat(short num, int radix)
|
||
{
|
||
// 十六进制固定长度
|
||
if (radix == 16 || radix == -16) return Concat((ushort)num, radix);
|
||
|
||
char buf[2 + 3 * sizeof(int)];
|
||
#if defined(_MSC_VER)
|
||
_itoa_s(num, buf, sizeof(buf), radix);
|
||
#else
|
||
itoa(num, buf, radix);
|
||
#endif
|
||
return Concat(buf, strlen(buf));
|
||
}
|
||
|
||
bool String::Concat(ushort num, int radix)
|
||
{
|
||
// 十六进制固定长度
|
||
if (radix == 16 || radix == -16)
|
||
{
|
||
if (!CheckCapacity(_Length + (sizeof(num) << 1))) return false;
|
||
|
||
utohex(num, sizeof(num), _Arr + _Length, radix < 0);
|
||
_Length += (sizeof(num) << 1);
|
||
|
||
return true;
|
||
}
|
||
|
||
char buf[2 + 3 * sizeof(int)];
|
||
utoa(num, buf, radix);
|
||
return Concat(buf, strlen(buf));
|
||
}
|
||
|
||
bool String::Concat(int num, int radix)
|
||
{
|
||
// 十六进制固定长度
|
||
if (radix == 16 || radix == -16) return Concat((uint)num, radix);
|
||
|
||
char buf[2 + 3 * sizeof(int)];
|
||
#if defined(_MSC_VER)
|
||
_itoa_s(num, buf, sizeof(buf), radix);
|
||
#else
|
||
itoa(num, buf, radix);
|
||
#endif
|
||
return Concat(buf, strlen(buf));
|
||
}
|
||
|
||
bool String::Concat(uint num, int radix)
|
||
{
|
||
// 十六进制固定长度
|
||
if (radix == 16 || radix == -16)
|
||
{
|
||
if (!CheckCapacity(_Length + (sizeof(num) << 1))) return false;
|
||
|
||
utohex(num, sizeof(num), _Arr + _Length, radix < 0);
|
||
_Length += (sizeof(num) << 1);
|
||
|
||
return true;
|
||
}
|
||
|
||
char buf[1 + 3 * sizeof(uint)];
|
||
utoa(num, buf, radix);
|
||
return Concat(buf, strlen(buf));
|
||
}
|
||
|
||
bool String::Concat(Int64 num, int radix)
|
||
{
|
||
// 十六进制固定长度
|
||
if (radix == 16 || radix == -16) return Concat((UInt64)num, radix);
|
||
|
||
char buf[2 + 3 * sizeof(Int64)];
|
||
ltoa(num, buf, radix);
|
||
return Concat(buf, strlen(buf));
|
||
}
|
||
|
||
bool String::Concat(UInt64 num, int radix)
|
||
{
|
||
// 十六进制固定长度
|
||
if (radix == 16 || radix == -16)
|
||
{
|
||
if (!CheckCapacity(_Length + (sizeof(num) << 1))) return false;
|
||
|
||
utohex((int)(num >> 32), sizeof(num) >> 1, _Arr + _Length, radix < 0);
|
||
_Length += sizeof(num);
|
||
utohex((int)(num & 0xFFFFFFFF), sizeof(num) >> 1, _Arr + _Length, radix < 0);
|
||
_Length += sizeof(num);
|
||
|
||
return true;
|
||
}
|
||
|
||
char buf[1 + 3 * sizeof(UInt64)];
|
||
ultoa(num, buf, radix);
|
||
return Concat(buf, strlen(buf));
|
||
}
|
||
|
||
static char* ftoa(char* str, int len, double num)
|
||
{
|
||
#if defined(_MSC_VER)
|
||
len = sprintf_s(str, len, "%.8f", num);
|
||
#else
|
||
len = sprintf(str, "%.8f", num);
|
||
#endif
|
||
// 干掉后面多余的0
|
||
for (int i = len; i >= 0; i--)
|
||
{
|
||
if (str[i] == '0') str[i] = '\0';
|
||
}
|
||
|
||
return str;
|
||
}
|
||
|
||
bool String::Concat(float num, int decimalPlaces)
|
||
{
|
||
char buf[20];
|
||
dtostrf(num, decimalPlaces, buf, sizeof(buf));
|
||
//sprintf(buf, "%f", num);
|
||
//ftoa(buf, num);
|
||
return Concat(buf, strlen(buf));
|
||
}
|
||
|
||
bool String::Concat(double num, int decimalPlaces)
|
||
{
|
||
char buf[20];
|
||
dtostrf(num, decimalPlaces, buf, sizeof(buf));
|
||
//sprintf(buf, "%f", num);
|
||
//ftoa(buf, num);
|
||
return Concat(buf, strlen(buf));
|
||
}
|
||
|
||
int String::CompareTo(const String& s) const
|
||
{
|
||
return CompareTo(s._Arr, s._Length, false);
|
||
}
|
||
|
||
#ifdef _MSC_VER
|
||
#define strncasecmp _strnicmp
|
||
#endif
|
||
int String::CompareTo(cstring cstr, int len, bool ignoreCase) const
|
||
{
|
||
if (len < 0) len = strlen(cstr);
|
||
if (!_Arr)
|
||
{
|
||
if (cstr && len > 0) return -1;
|
||
return 0;
|
||
}
|
||
if (!cstr && _Arr && _Length > 0) return 1;
|
||
|
||
// 逐个比较字符,直到指定长度或者源字符串结束
|
||
if (ignoreCase)
|
||
return strncasecmp(_Arr, cstr, _Length);
|
||
else
|
||
return strncmp(_Arr, cstr, _Length);
|
||
}
|
||
|
||
bool String::Equals(const String& str) const
|
||
{
|
||
return _Length == str._Length && CompareTo(str._Arr, str._Length, false) == 0;
|
||
}
|
||
|
||
bool String::Equals(cstring cstr) const
|
||
{
|
||
int len = strlen(cstr);
|
||
if (len != _Length) return false;
|
||
|
||
return CompareTo(cstr, len, false) == 0;
|
||
}
|
||
|
||
bool String::EqualsIgnoreCase(const String &str) const
|
||
{
|
||
return _Length == str._Length && CompareTo(str._Arr, str._Length, true) == 0;
|
||
}
|
||
|
||
bool String::EqualsIgnoreCase(cstring cstr) const
|
||
{
|
||
int len = strlen(cstr);
|
||
if (len != _Length) return false;
|
||
|
||
return CompareTo(cstr, len, true) == 0;
|
||
}
|
||
|
||
bool String::operator<(const String& rhs) const
|
||
{
|
||
return CompareTo(rhs) < 0;
|
||
}
|
||
|
||
bool String::operator>(const String& rhs) const
|
||
{
|
||
return CompareTo(rhs) > 0;
|
||
}
|
||
|
||
bool String::operator<=(const String& rhs) const
|
||
{
|
||
return CompareTo(rhs) <= 0;
|
||
}
|
||
|
||
bool String::operator>=(const String& rhs) const
|
||
{
|
||
return CompareTo(rhs) >= 0;
|
||
}
|
||
|
||
/*void String::SetAt(int loc, char c)
|
||
{
|
||
if (loc < _Length) _Arr[loc] = c;
|
||
}*/
|
||
|
||
char& String::operator[](int index)
|
||
{
|
||
static char dummy_writable_char;
|
||
if (index >= _Length || !_Arr) {
|
||
dummy_writable_char = 0;
|
||
return dummy_writable_char;
|
||
}
|
||
|
||
// 修改时拷贝
|
||
CopyOrWrite();
|
||
|
||
return _Arr[index];
|
||
}
|
||
|
||
char String::operator[](int index) const
|
||
{
|
||
if (index >= _Length || !_Arr) return 0;
|
||
return _Arr[index];
|
||
}
|
||
|
||
void String::GetBytes(byte* buf, int bufsize, int index) const
|
||
{
|
||
if (!bufsize || !buf) return;
|
||
if (index >= _Length) {
|
||
buf[0] = 0;
|
||
return;
|
||
}
|
||
int n = bufsize;
|
||
if (n > _Length - index) n = _Length - index;
|
||
//strncpy((char*)buf, _Arr + index, n);
|
||
Buffer(buf, bufsize).Copy(0, _Arr + index, n);
|
||
//buf[n] = '\0';
|
||
}
|
||
|
||
ByteArray String::GetBytes() const
|
||
{
|
||
ByteArray bs;
|
||
bs.SetLength(_Length);
|
||
|
||
GetBytes(bs.GetBuffer(), bs.Length());
|
||
|
||
return bs;
|
||
}
|
||
|
||
ByteArray String::ToHex() const
|
||
{
|
||
ByteArray bs;
|
||
bs.SetLength(_Length / 2);
|
||
|
||
char cs[3];
|
||
cs[2] = 0;
|
||
byte* b = bs.GetBuffer();
|
||
auto p = _Arr;
|
||
int n = 0;
|
||
for (int i = 0; i < _Length; i += 2)
|
||
{
|
||
cs[0] = *p++;
|
||
cs[1] = *p++;
|
||
|
||
*b++ = (byte)strtol(cs, nullptr, 16);
|
||
|
||
// 过滤横杠和空格
|
||
if (*p == '-' || isspace(*p))
|
||
{
|
||
p++;
|
||
i++;
|
||
}
|
||
|
||
n++;
|
||
}
|
||
bs.SetLength(n);
|
||
|
||
return bs;
|
||
}
|
||
|
||
int String::ToInt() const
|
||
{
|
||
if (_Length == 0) return 0;
|
||
|
||
if (_Arr[_Length] == '\0') return atoi(_Arr);
|
||
|
||
// 非零结尾字符串需要特殊处理
|
||
String s;
|
||
s.copy(_Arr, _Length);
|
||
return s.ToInt();
|
||
}
|
||
|
||
float String::ToFloat() const
|
||
{
|
||
if (_Length == 0) return 0;
|
||
|
||
if (_Arr[_Length] == '\0') return (float)atof(_Arr);
|
||
|
||
// 非零结尾字符串需要特殊处理
|
||
String s;
|
||
s.copy(_Arr, _Length);
|
||
return s.ToFloat();
|
||
}
|
||
|
||
double String::ToDouble() const
|
||
{
|
||
if (_Length == 0) return 0;
|
||
|
||
if (_Arr[_Length] == '\0') return atof(_Arr);
|
||
|
||
// 非零结尾字符串需要特殊处理
|
||
String s;
|
||
s.copy(_Arr, _Length);
|
||
return s.ToDouble();
|
||
}
|
||
|
||
// 输出对象的字符串表示方式
|
||
String& String::ToStr(String& str) const
|
||
{
|
||
// 把当前字符串复制到目标字符串后面
|
||
//str.Copy(*this, str._Length);
|
||
str += *this;
|
||
|
||
return (String&)*this;
|
||
}
|
||
|
||
// 输出对象的字符串表示方式
|
||
String String::ToString() const
|
||
{
|
||
return *this;
|
||
}
|
||
|
||
// 调试输出字符串
|
||
void String::Show(bool newLine) const
|
||
{
|
||
//if(_Length) debug_printf("%s", _Arr);
|
||
for (int i = 0; i < _Length; i++)
|
||
{
|
||
//fput(_Arr[i]);
|
||
debug_printf("%c", _Arr[i]);
|
||
}
|
||
if (newLine) debug_printf("\r\n");
|
||
}
|
||
|
||
// 格式化字符串,输出到现有字符串后面。方便我们连续格式化多个字符串
|
||
String& String::Format(cstring format, ...)
|
||
{
|
||
va_list ap;
|
||
|
||
va_start(ap, format);
|
||
|
||
// 无法准确估计长度,大概乘以2处理
|
||
CheckCapacity(_Length + (strlen(format) << 1));
|
||
|
||
//char* p = _Arr;
|
||
int len2 = vsnprintf(_Arr + _Length, _Capacity - _Length, format, ap);
|
||
_Length += len2;
|
||
|
||
va_end(ap);
|
||
|
||
return *this;
|
||
}
|
||
|
||
int String::IndexOf(const char ch, int startIndex) const
|
||
{
|
||
if (startIndex < 0) return -1;
|
||
if (startIndex >= _Length) return -1;
|
||
|
||
for (int i = startIndex; i < _Length; i++)
|
||
{
|
||
if (_Arr[i] == ch) return i;
|
||
}
|
||
|
||
return -1;
|
||
}
|
||
|
||
int String::IndexOf(const String& str, int startIndex) const
|
||
{
|
||
return Search(str._Arr, str._Length, startIndex, false);
|
||
}
|
||
|
||
int String::IndexOf(cstring str, int startIndex) const
|
||
{
|
||
return Search(str, strlen(str), startIndex, false);
|
||
}
|
||
|
||
int String::LastIndexOf(const char ch, int startIndex) const
|
||
{
|
||
if (startIndex >= _Length) return -1;
|
||
|
||
for (int i = _Length - 1; i >= startIndex; i--)
|
||
{
|
||
if (_Arr[i] == ch) return i;
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
int String::LastIndexOf(const String& str, int startIndex) const
|
||
{
|
||
return Search(str._Arr, str._Length, startIndex, true);
|
||
}
|
||
|
||
int String::LastIndexOf(cstring str, int startIndex) const
|
||
{
|
||
return Search(str, strlen(str), startIndex, true);
|
||
}
|
||
|
||
int String::Search(cstring str, int len, int startIndex, bool rev) const
|
||
{
|
||
if (!str) return -1;
|
||
if (startIndex < 0) return -1;
|
||
|
||
// 可遍历的长度
|
||
int count = _Length - len;
|
||
if (startIndex > count) return -1;
|
||
|
||
// 遍历源字符串
|
||
auto s = _Arr + startIndex;
|
||
auto e = _Arr + _Length - 1;
|
||
auto p = rev ? e : s;
|
||
for (int i = 0; i <= count; i++)
|
||
{
|
||
// 最大比较个数以目标字符串为准,源字符串确保长度足够
|
||
if (strncmp(p, str, len) == 0) return p - _Arr;
|
||
|
||
if (rev)
|
||
{
|
||
if (--p < s) break;
|
||
}
|
||
else
|
||
{
|
||
if (++p > e) break;
|
||
}
|
||
}
|
||
return -1;
|
||
}
|
||
|
||
bool String::Contains(const String& str) const { return IndexOf(str) >= 0; }
|
||
|
||
bool String::Contains(cstring str) const { return IndexOf(str) >= 0; }
|
||
|
||
bool String::StartsWith(const String& str, int startIndex) const
|
||
{
|
||
if (!_Arr || !str._Arr) return false;
|
||
if (str._Length == 0) return false;
|
||
if (startIndex + str._Length > _Length) return false;
|
||
|
||
return strncmp(&_Arr[startIndex], str._Arr, str._Length) == 0;
|
||
}
|
||
|
||
bool String::StartsWith(cstring str, int startIndex) const
|
||
{
|
||
if (!_Arr || !str) return false;
|
||
int slen = strlen(str);
|
||
if (slen == 0) return false;
|
||
if (startIndex + slen > _Length) return false;
|
||
|
||
return strncmp(&_Arr[startIndex], str, slen) == 0;
|
||
}
|
||
|
||
bool String::EndsWith(const String& str) const
|
||
{
|
||
if (!_Arr || !str._Arr) return false;
|
||
if (str._Length == 0) return false;
|
||
if (str._Length > _Length) return false;
|
||
|
||
return strncmp(&_Arr[_Length - str._Length], str._Arr, str._Length) == 0;
|
||
}
|
||
|
||
bool String::EndsWith(cstring str) const
|
||
{
|
||
if (!_Arr || !str) return false;
|
||
int slen = strlen(str);
|
||
if (slen == 0) return false;
|
||
if (slen > _Length) return false;
|
||
|
||
return strncmp(&_Arr[_Length - slen], str, slen) == 0;
|
||
}
|
||
|
||
StringSplit String::Split(const String& sep) const
|
||
{
|
||
return StringSplit(*this, sep.GetBuffer());
|
||
}
|
||
|
||
StringSplit String::Split(cstring sep) const
|
||
{
|
||
return StringSplit(*this, sep);
|
||
}
|
||
|
||
String String::Substring(int start, int len) const
|
||
{
|
||
String str;
|
||
|
||
if (len < 0) len = _Length - start;
|
||
//str.Copy(this, _Length, start);
|
||
if (_Length && start < _Length) str.copy(_Arr + start, len);
|
||
|
||
return str;
|
||
}
|
||
|
||
void trim(char* buffer, int& len, bool trimBegin, bool trimEnd)
|
||
{
|
||
if (!buffer || len == 0) return;
|
||
char *begin = buffer;
|
||
if (trimBegin) while (isspace(*begin)) begin++;
|
||
char *end = buffer + len - 1;
|
||
if (trimEnd) while (isspace(*end) && end >= begin) end--;
|
||
len = end + 1 - begin;
|
||
if (begin > buffer && len) Buffer(buffer, len) = begin;
|
||
buffer[len] = 0;
|
||
}
|
||
|
||
String String::TrimStart() const
|
||
{
|
||
String str(*this);
|
||
trim(str._Arr, str._Length, true, false);
|
||
|
||
return str;
|
||
}
|
||
|
||
String String::TrimEnd() const
|
||
{
|
||
String str(*this);
|
||
trim(str._Arr, str._Length, false, true);
|
||
|
||
return str;
|
||
}
|
||
|
||
String String::Trim() const
|
||
{
|
||
String str(*this);
|
||
trim(str._Arr, str._Length, true, true);
|
||
|
||
return str;
|
||
}
|
||
|
||
String String::Replace(char find, char replace) const
|
||
{
|
||
String str(*this);
|
||
|
||
auto p = (char*)str.GetBuffer();
|
||
for (int i = 0; i < Length(); i++, p++)
|
||
{
|
||
if (*p == find) *p = replace;
|
||
}
|
||
|
||
return str;
|
||
}
|
||
|
||
String String::ToLower() const
|
||
{
|
||
String str(*this);
|
||
auto p = str._Arr;
|
||
for (int i = 0; i < str._Length; i++)
|
||
p[i] = tolower(p[i]);
|
||
|
||
return str;
|
||
}
|
||
|
||
String String::ToUpper() const
|
||
{
|
||
String str(*this);
|
||
auto p = str._Arr;
|
||
for (int i = 0; i < str._Length; i++)
|
||
p[i] = toupper(p[i]);
|
||
|
||
return str;
|
||
}
|
||
|
||
// 静态比较器。比较两个字符串指针
|
||
int String::Compare(const void* v1, const void* v2)
|
||
{
|
||
if (!v1) return v1 == v2 ? 0 : -1;
|
||
if (!v2) return 1;
|
||
|
||
auto str1 = (cstring)v1;
|
||
auto str2 = (cstring)v2;
|
||
return strncmp((char*)v1, str2, strlen(str1));
|
||
}
|
||
|
||
/******************************** 辅助 ********************************/
|
||
|
||
#ifndef _MSC_VER
|
||
extern char* itoa(int value, char *string, int radix)
|
||
{
|
||
return ltoa(value, string, radix);
|
||
}
|
||
|
||
extern char* ltoa(Int64 value, char* string, int radix)
|
||
{
|
||
char tmp[33];
|
||
char *tp = tmp;
|
||
Int64 i;
|
||
UInt64 v;
|
||
int sign;
|
||
char *sp;
|
||
|
||
if (string == nullptr) return 0;
|
||
|
||
if (radix > 36 || radix <= 1) return 0;
|
||
|
||
sign = (radix == 10 && value < 0);
|
||
if (sign)
|
||
v = -value;
|
||
else
|
||
v = (UInt64)value;
|
||
|
||
while (v || tp == tmp)
|
||
{
|
||
i = v % radix;
|
||
v = v / radix;
|
||
if (i < 10)
|
||
*tp++ = i + '0';
|
||
else
|
||
*tp++ = i + 'a' - 10;
|
||
}
|
||
|
||
sp = string;
|
||
|
||
if (sign) *sp++ = '-';
|
||
while (tp > tmp)
|
||
*sp++ = *--tp;
|
||
*sp = 0;
|
||
|
||
return string;
|
||
}
|
||
#endif
|
||
|
||
char* utohex(uint value, byte size, char* string, bool upper)
|
||
{
|
||
if (string == nullptr) return 0;
|
||
|
||
// 字节数乘以2是字符个数
|
||
size <<= 1;
|
||
// 指针提前指向最后一个字符,数字从小往大处理,字符需要倒过来赋值
|
||
auto tp = string + size;;
|
||
*tp-- = '\0';
|
||
char ch = upper ? 'A' : 'a';
|
||
for (int i = 0; i < size; i++)
|
||
{
|
||
byte bt = value & 0x0F;
|
||
value >>= 4;
|
||
if (bt < 10)
|
||
*tp-- = bt + '0';
|
||
else
|
||
*tp-- = bt + ch - 10;
|
||
}
|
||
|
||
return string;
|
||
}
|
||
|
||
#ifndef _MSC_VER
|
||
extern char* utoa(uint value, char* string, int radix)
|
||
{
|
||
return ultoa(value, string, radix);
|
||
}
|
||
|
||
extern char* ultoa(UInt64 value, char* string, int radix)
|
||
{
|
||
if (string == nullptr) return 0;
|
||
|
||
if (radix > 36 || radix <= 1) return 0;
|
||
|
||
char tmp[33];
|
||
auto tp = tmp;
|
||
auto v = value;
|
||
char ch = radix < 0 ? 'A' : 'a';
|
||
while (v || tp == tmp)
|
||
{
|
||
auto i = v % radix;
|
||
v = v / radix;
|
||
if (i < 10)
|
||
*tp++ = i + '0';
|
||
else
|
||
*tp++ = i + ch - 10;
|
||
}
|
||
|
||
auto sp = string;
|
||
while (tp > tmp)
|
||
*sp++ = *--tp;
|
||
*sp = '\0';
|
||
|
||
return string;
|
||
}
|
||
#endif
|
||
|
||
static char *dtostrf(double val, byte prec, char* str, int len)
|
||
{
|
||
char fmt[20];
|
||
#if defined(_MSC_VER)
|
||
sprintf_s(fmt, sizeof(fmt), "%%.%df", prec);
|
||
len = sprintf_s(str, len, fmt, val);
|
||
#else
|
||
sprintf(fmt, "%%.%df", prec);
|
||
len = sprintf(str, fmt, val);
|
||
#endif
|
||
|
||
// 干掉后面多余的0
|
||
for (int i = len; i >= 0; i--)
|
||
{
|
||
if (str[i] == '0') str[i] = '\0';
|
||
}
|
||
|
||
return str;
|
||
}
|
||
|
||
/******************************** StringSplit ********************************/
|
||
|
||
StringSplit::StringSplit(const String& str, cstring sep) :
|
||
_Str(str)
|
||
{
|
||
Sep = sep;
|
||
Position = -1;
|
||
Length = 0;
|
||
|
||
// 先算好第一段
|
||
//int p = _Str.IndexOf(_Sep);
|
||
//if(p >= 0) _Length = p;
|
||
}
|
||
|
||
const String StringSplit::Next()
|
||
{
|
||
cstring ptr = nullptr;
|
||
int len = 0;
|
||
|
||
if (Position >= -1 && Sep)
|
||
{
|
||
String sp = Sep;
|
||
|
||
// 从当前段之后开始找一段
|
||
int s = Position + Length;
|
||
// 除首次以外,每次都要跳过分隔符
|
||
if (s < 0)
|
||
s = 0;
|
||
else
|
||
s += sp.Length();
|
||
|
||
// 检查是否已经越界
|
||
if (s >= _Str.Length())
|
||
{
|
||
Position = -2;
|
||
Length = 0;
|
||
}
|
||
else
|
||
{
|
||
// 查找分隔符
|
||
int p = _Str.IndexOf(Sep, s);
|
||
|
||
int sz = 0;
|
||
// 剩余全部长度,如果找不到下一个,那么这个就是最后长度。不用跳过分隔符
|
||
if (p < 0)
|
||
sz = _Str.Length() - s;
|
||
else
|
||
sz = p - s;
|
||
|
||
Position = s;
|
||
Length = sz;
|
||
|
||
ptr = _Str.GetBuffer() + Position;
|
||
len = Length;
|
||
}
|
||
}
|
||
|
||
// 包装一层指针
|
||
return String(ptr, len);
|
||
}
|