added methods for String
This commit is contained in:
parent
c46ca6b7bc
commit
6f9db08697
9 changed files with 415 additions and 71 deletions
|
@ -143,11 +143,11 @@ ByteArray MscReader::readScoreFile() const
|
|||
QString mscxFileName = mainFileName();
|
||||
ByteArray data = fileData(mscxFileName);
|
||||
if (data.empty() && reader()->isContainer()) {
|
||||
QStringList files = reader()->fileList();
|
||||
for (const QString& name : files) {
|
||||
StringList files = reader()->fileList();
|
||||
for (const String& name : files) {
|
||||
// mscx file in the root dir
|
||||
if (!name.contains("/") && name.endsWith(".mscx", Qt::CaseInsensitive)) {
|
||||
mscxFileName = name;
|
||||
if (!name.contains('/') && name.endsWith(".mscx", mu::CaseInsensitive)) {
|
||||
mscxFileName = name.toQString();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -164,10 +164,10 @@ std::vector<QString> MscReader::excerptNames() const
|
|||
}
|
||||
|
||||
std::vector<QString> names;
|
||||
QStringList files = reader()->fileList();
|
||||
for (const QString& filePath : files) {
|
||||
if (filePath.startsWith("Excerpts/") && filePath.endsWith(".mscx", Qt::CaseInsensitive)) {
|
||||
names.push_back(FileInfo(filePath).completeBaseName());
|
||||
StringList files = reader()->fileList();
|
||||
for (const String& filePath : files) {
|
||||
if (filePath.startsWith("Excerpts/") && filePath.endsWith(".mscx", mu::CaseInsensitive)) {
|
||||
names.push_back(FileInfo(filePath.toQString()).completeBaseName());
|
||||
}
|
||||
}
|
||||
return names;
|
||||
|
@ -208,10 +208,10 @@ std::vector<QString> MscReader::imageFileNames() const
|
|||
}
|
||||
|
||||
std::vector<QString> names;
|
||||
QStringList files = reader()->fileList();
|
||||
for (const QString& filePath : files) {
|
||||
StringList files = reader()->fileList();
|
||||
for (const String& filePath : files) {
|
||||
if (filePath.startsWith("Pictures/")) {
|
||||
names.push_back(FileInfo(filePath).fileName());
|
||||
names.push_back(FileInfo(filePath.toQString()).fileName());
|
||||
}
|
||||
}
|
||||
return names;
|
||||
|
@ -285,13 +285,13 @@ bool MscReader::ZipFileReader::isContainer() const
|
|||
return true;
|
||||
}
|
||||
|
||||
QStringList MscReader::ZipFileReader::fileList() const
|
||||
StringList MscReader::ZipFileReader::fileList() const
|
||||
{
|
||||
IF_ASSERT_FAILED(m_zip) {
|
||||
return QStringList();
|
||||
return StringList();
|
||||
}
|
||||
|
||||
QStringList files;
|
||||
StringList files;
|
||||
std::vector<ZipReader::FileInfo> fileInfoList = m_zip->fileInfoList();
|
||||
if (m_zip->status() != ZipReader::NoError) {
|
||||
LOGD() << "failed read meta, status: " << m_zip->status();
|
||||
|
@ -299,7 +299,7 @@ QStringList MscReader::ZipFileReader::fileList() const
|
|||
|
||||
for (const ZipReader::FileInfo& fi : fileInfoList) {
|
||||
if (fi.isFile) {
|
||||
files << fi.filePath;
|
||||
files << String::fromQString(fi.filePath);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -354,14 +354,14 @@ bool MscReader::DirReader::isContainer() const
|
|||
return FileInfo::exists(m_rootPath + "/META-INF/container.xml");
|
||||
}
|
||||
|
||||
QStringList MscReader::DirReader::fileList() const
|
||||
StringList MscReader::DirReader::fileList() const
|
||||
{
|
||||
QStringList files;
|
||||
StringList files;
|
||||
QDirIterator::IteratorFlags flags = QDirIterator::Subdirectories;
|
||||
QDirIterator it(m_rootPath, QStringList(), QDir::NoDotAndDotDot | QDir::NoSymLinks | QDir::Readable | QDir::Files, flags);
|
||||
|
||||
while (it.hasNext()) {
|
||||
QString filePath = it.next();
|
||||
String filePath = String::fromQString(it.next());
|
||||
files << filePath.mid(m_rootPath.length() + 1);
|
||||
}
|
||||
|
||||
|
@ -415,13 +415,13 @@ bool MscReader::XmlFileReader::isContainer() const
|
|||
return true;
|
||||
}
|
||||
|
||||
QStringList MscReader::XmlFileReader::fileList() const
|
||||
StringList MscReader::XmlFileReader::fileList() const
|
||||
{
|
||||
if (!m_device) {
|
||||
return QStringList();
|
||||
return StringList();
|
||||
}
|
||||
|
||||
QStringList files;
|
||||
StringList files;
|
||||
|
||||
m_device->seek(0);
|
||||
XmlStreamReader xml(m_device);
|
||||
|
@ -437,7 +437,7 @@ QStringList MscReader::XmlFileReader::fileList() const
|
|||
continue;
|
||||
}
|
||||
|
||||
QString fileName = xml.attribute("name");
|
||||
String fileName = xml.attribute("name");
|
||||
files << fileName;
|
||||
xml.skipCurrentElement();
|
||||
}
|
||||
|
@ -466,8 +466,8 @@ ByteArray MscReader::XmlFileReader::fileData(const QString& fileName) const
|
|||
continue;
|
||||
}
|
||||
|
||||
QString file = xml.attribute("name");
|
||||
if (file != fileName) {
|
||||
String file = xml.attribute("name");
|
||||
if (file.toQString() != fileName) {
|
||||
xml.skipCurrentElement();
|
||||
continue;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include <QString>
|
||||
#include "io/iodevice.h"
|
||||
#include "mscio.h"
|
||||
#include "types/string.h"
|
||||
|
||||
namespace mu {
|
||||
class ZipReader;
|
||||
|
@ -83,7 +84,7 @@ private:
|
|||
//! it may happen that we are not reading a container (a directory with a certain structure),
|
||||
//! but only one file among others (`.mscx` from MU 3.x)
|
||||
virtual bool isContainer() const = 0;
|
||||
virtual QStringList fileList() const = 0;
|
||||
virtual StringList fileList() const = 0;
|
||||
virtual ByteArray fileData(const QString& fileName) const = 0;
|
||||
};
|
||||
|
||||
|
@ -94,7 +95,7 @@ private:
|
|||
void close() override;
|
||||
bool isOpened() const override;
|
||||
bool isContainer() const override;
|
||||
QStringList fileList() const override;
|
||||
StringList fileList() const override;
|
||||
ByteArray fileData(const QString& fileName) const override;
|
||||
private:
|
||||
io::IODevice* m_device = nullptr;
|
||||
|
@ -108,7 +109,7 @@ private:
|
|||
void close() override;
|
||||
bool isOpened() const override;
|
||||
bool isContainer() const override;
|
||||
QStringList fileList() const override;
|
||||
StringList fileList() const override;
|
||||
ByteArray fileData(const QString& fileName) const override;
|
||||
private:
|
||||
QString m_rootPath;
|
||||
|
@ -120,7 +121,7 @@ private:
|
|||
void close() override;
|
||||
bool isOpened() const override;
|
||||
bool isContainer() const override;
|
||||
QStringList fileList() const override;
|
||||
StringList fileList() const override;
|
||||
ByteArray fileData(const QString& fileName) const override;
|
||||
private:
|
||||
io::IODevice* m_device = nullptr;
|
||||
|
|
|
@ -267,7 +267,7 @@ void XmlReader::htmlToString(int level, QString* s)
|
|||
{
|
||||
*s += QString("<%1").arg(name().toQLatin1String());
|
||||
for (const Attribute& a : attributes()) {
|
||||
*s += QString(" %1=\"%2\"").arg(a.name, a.value);
|
||||
*s += QString(" %1=\"%2\"").arg(a.name.ascii(), a.value.toQString());
|
||||
}
|
||||
*s += ">";
|
||||
++level;
|
||||
|
|
|
@ -34,7 +34,7 @@
|
|||
namespace mu::engraving {
|
||||
class ReadContext;
|
||||
|
||||
class XmlReader : public mu::XmlStreamReader
|
||||
class XmlReader : public XmlStreamReader
|
||||
{
|
||||
public:
|
||||
|
||||
|
@ -55,7 +55,7 @@ public:
|
|||
void unknown();
|
||||
|
||||
// attribute helper routines:
|
||||
QString attribute(const char* s) const { return mu::XmlStreamReader::attribute(s); }
|
||||
QString attribute(const char* s) const { return mu::XmlStreamReader::attribute(s).toQString(); }
|
||||
QString attribute(const char* s, const QString&) const;
|
||||
int intAttribute(const char* s) const;
|
||||
int intAttribute(const char* s, int _default) const;
|
||||
|
|
|
@ -178,21 +178,21 @@ void XmlStreamReader::tryParseEntity(Xml* xml)
|
|||
|
||||
const char* str = xml->node->Value();
|
||||
if (std::strncmp(str, ENTITY, 6) == 0) {
|
||||
QString val(str);
|
||||
QStringList list = val.split(' ');
|
||||
if (list.length() == 3) {
|
||||
QString name = list.at(1);
|
||||
QString val = list.at(2);
|
||||
m_entities["&" + name + ";"] = val.mid(1, val.size() - 2);
|
||||
String val(str);
|
||||
StringList list = val.split(' ');
|
||||
if (list.size() == 3) {
|
||||
String name = list.at(1);
|
||||
String val = list.at(2);
|
||||
m_entities[u"&" + name + u";"] = val.mid(1, val.size() - 2);
|
||||
} else {
|
||||
LOGW() << "unknown ENTITY: " << val;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QString XmlStreamReader::nodeValue(Xml* xml) const
|
||||
String XmlStreamReader::nodeValue(Xml* xml) const
|
||||
{
|
||||
QString str = xml->node->Value();
|
||||
String str = String::fromUtf8(xml->node->Value());
|
||||
if (!m_entities.empty()) {
|
||||
for (const auto& p : m_entities) {
|
||||
str.replace(p.first, p.second);
|
||||
|
@ -245,17 +245,17 @@ AsciiStringView XmlStreamReader::name() const
|
|||
return (m_xml->node && m_xml->node->ToElement()) ? m_xml->node->Value() : AsciiStringView();
|
||||
}
|
||||
|
||||
QString XmlStreamReader::attribute(const char* name) const
|
||||
String XmlStreamReader::attribute(const char* name) const
|
||||
{
|
||||
if (m_token != TokenType::StartElement) {
|
||||
return QString();
|
||||
return String();
|
||||
}
|
||||
|
||||
XMLElement* e = m_xml->node->ToElement();
|
||||
if (!e) {
|
||||
return QString();
|
||||
return String();
|
||||
}
|
||||
return e->Attribute(name);
|
||||
return String(e->Attribute(name));
|
||||
}
|
||||
|
||||
AsciiStringView XmlStreamReader::asciiAttribute(const char* name) const
|
||||
|
@ -308,14 +308,14 @@ std::vector<XmlStreamReader::Attribute> XmlStreamReader::attributes() const
|
|||
QString XmlStreamReader::readElementText()
|
||||
{
|
||||
if (isStartElement()) {
|
||||
QString result;
|
||||
String result;
|
||||
while (1) {
|
||||
switch (readNext()) {
|
||||
case Characters:
|
||||
result.append(nodeValue(m_xml));
|
||||
result = nodeValue(m_xml);
|
||||
break;
|
||||
case EndElement:
|
||||
return result;
|
||||
return result.toQString();
|
||||
case Comment:
|
||||
break;
|
||||
case StartElement:
|
||||
|
@ -331,7 +331,7 @@ QString XmlStreamReader::readElementText()
|
|||
QString XmlStreamReader::text() const
|
||||
{
|
||||
if (m_xml->node && (m_xml->node->ToText() || m_xml->node->ToComment())) {
|
||||
return nodeValue(m_xml);
|
||||
return nodeValue(m_xml).toQString();
|
||||
}
|
||||
return QString();
|
||||
}
|
||||
|
|
|
@ -59,8 +59,8 @@ public:
|
|||
|
||||
struct Attribute
|
||||
{
|
||||
QString name;
|
||||
QString value;
|
||||
AsciiStringView name;
|
||||
String value;
|
||||
};
|
||||
|
||||
XmlStreamReader();
|
||||
|
@ -90,7 +90,7 @@ public:
|
|||
|
||||
AsciiStringView name() const;
|
||||
|
||||
QString attribute(const char* name) const;
|
||||
String attribute(const char* name) const;
|
||||
AsciiStringView asciiAttribute(const char* name) const;
|
||||
bool hasAttribute(const char* name) const;
|
||||
std::vector<Attribute> attributes() const;
|
||||
|
@ -112,12 +112,12 @@ private:
|
|||
struct Xml;
|
||||
|
||||
void tryParseEntity(Xml* xml);
|
||||
QString nodeValue(Xml* xml) const;
|
||||
String nodeValue(Xml* xml) const;
|
||||
|
||||
Xml* m_xml = nullptr;
|
||||
TokenType m_token = TokenType::NoToken;
|
||||
|
||||
std::map<QString, QString> m_entities;
|
||||
std::map<String, String> m_entities;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -109,6 +109,43 @@ TEST_F(Global_Types_StringTests, String_Convert)
|
|||
}
|
||||
}
|
||||
|
||||
TEST_F(Global_Types_StringTests, String_Compare)
|
||||
{
|
||||
{
|
||||
//! GIVEN Some Strings
|
||||
String str1(u"123abc");
|
||||
String str2(u"123abc");
|
||||
String str3(u"abc123");
|
||||
String str4(u"abc");
|
||||
|
||||
//! CHECK
|
||||
EXPECT_TRUE(str1 == str2);
|
||||
EXPECT_TRUE(str1 != str3);
|
||||
EXPECT_TRUE(str1 != str4);
|
||||
|
||||
EXPECT_TRUE(str1 == u"123abc");
|
||||
EXPECT_TRUE(str1 != u"abc123");
|
||||
EXPECT_TRUE(str1 != u"abc");
|
||||
|
||||
EXPECT_TRUE(u"123abc" == str1);
|
||||
EXPECT_TRUE(u"abc123" != str1);
|
||||
EXPECT_TRUE(u"abc" != str1);
|
||||
}
|
||||
|
||||
{
|
||||
//! GIVEN Some ASCII Strings
|
||||
String str1(u"02");
|
||||
String str2(u"20");
|
||||
String str3(u"22");
|
||||
String str4(u"002");
|
||||
|
||||
//! CHECK
|
||||
EXPECT_TRUE(str1 < str2);
|
||||
EXPECT_TRUE(str2 < str3);
|
||||
EXPECT_TRUE(str4 < str3);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(Global_Types_StringTests, String_Access)
|
||||
{
|
||||
{
|
||||
|
@ -130,6 +167,107 @@ TEST_F(Global_Types_StringTests, String_Access)
|
|||
}
|
||||
}
|
||||
|
||||
TEST_F(Global_Types_StringTests, String_Replace)
|
||||
{
|
||||
{
|
||||
//! GIVEN Some String
|
||||
String str = u"123abcПыф";
|
||||
//! DO
|
||||
str.replace(u"abc", u"def").replace(u"Пыф", u"Длж");
|
||||
//! CHECK
|
||||
EXPECT_EQ(str, u"123defДлж");
|
||||
}
|
||||
|
||||
{
|
||||
//! GIVEN Some String
|
||||
String str = u"123abcПыф";
|
||||
//! DO
|
||||
str.replace(u"abc", u"de");
|
||||
//! CHECK
|
||||
EXPECT_EQ(str, u"123deПыф");
|
||||
}
|
||||
|
||||
{
|
||||
//! GIVEN Some String
|
||||
String str = u"123abc456abc";
|
||||
//! DO
|
||||
str.replace(u"abc", u"de");
|
||||
//! CHECK
|
||||
EXPECT_EQ(str, u"123de456de");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(Global_Types_StringTests, String_Modify)
|
||||
{
|
||||
{
|
||||
//! GIVEN Some String
|
||||
String str = u"123abc";
|
||||
//! DO
|
||||
String newStr = str + u"Пыф";
|
||||
//! CHECK
|
||||
EXPECT_EQ(newStr, u"123abcПыф");
|
||||
}
|
||||
|
||||
{
|
||||
//! GIVEN Some String
|
||||
String str = u"123abc";
|
||||
//! DO
|
||||
str += u"Пыф";
|
||||
//! CHECK
|
||||
EXPECT_EQ(str, u"123abcПыф");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(Global_Types_StringTests, String_SubStr)
|
||||
{
|
||||
{
|
||||
//! GIVEN Some String
|
||||
String str = u"123abc";
|
||||
//! DO
|
||||
String newStr = str.mid(2, 2);
|
||||
//! CHECK
|
||||
EXPECT_EQ(newStr, u"3a");
|
||||
}
|
||||
|
||||
{
|
||||
//! GIVEN Some String
|
||||
String str = u"123abc";
|
||||
//! DO
|
||||
String newStr = str.mid(2);
|
||||
//! CHECK
|
||||
EXPECT_EQ(newStr, u"3abc");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(Global_Types_StringTests, String_Split)
|
||||
{
|
||||
{
|
||||
//! GIVEN Some String
|
||||
String str = u"wwww gggg ыыыы";
|
||||
//! DO
|
||||
StringList list = str.split(u' ');
|
||||
//! CHECK
|
||||
EXPECT_EQ(list.size(), 3);
|
||||
EXPECT_EQ(list.at(0), u"wwww");
|
||||
EXPECT_EQ(list.at(1), u"gggg");
|
||||
EXPECT_EQ(list.at(2), u"ыыыы");
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(Global_Types_StringTests, String_StartEndWith)
|
||||
{
|
||||
{
|
||||
//! GIVEN Some String
|
||||
String str = u"123abc";
|
||||
//! CHECK
|
||||
EXPECT_TRUE(str.startsWith("12"));
|
||||
EXPECT_FALSE(str.startsWith("13"));
|
||||
//! CHECK
|
||||
EXPECT_TRUE(str.endsWith("bc"));
|
||||
EXPECT_FALSE(str.endsWith("ac"));
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(Global_Types_StringTests, AsciiString_Construct)
|
||||
{
|
||||
//! GIVEN Some ASCII String
|
||||
|
|
|
@ -22,6 +22,8 @@
|
|||
#include "string.h"
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <locale>
|
||||
#include <cctype>
|
||||
|
||||
#include "global/thirdparty/utfcpp-3.2.1/utf8.h"
|
||||
|
||||
|
@ -32,6 +34,20 @@ using namespace mu;
|
|||
// ============================
|
||||
// Char
|
||||
// ============================
|
||||
char Char::ascii(bool* ok) const
|
||||
{
|
||||
if (m_ch > 0xff) {
|
||||
if (ok) {
|
||||
*ok = false;
|
||||
}
|
||||
return '?';
|
||||
} else {
|
||||
if (ok) {
|
||||
*ok = true;
|
||||
}
|
||||
return static_cast<char>(m_ch);
|
||||
}
|
||||
}
|
||||
|
||||
// ============================
|
||||
// UtfCodec
|
||||
|
@ -60,12 +76,48 @@ String::String(const char16_t* str)
|
|||
m_data = std::make_shared<std::u16string>(str);
|
||||
}
|
||||
|
||||
const std::u16string& String::constStr() const
|
||||
{
|
||||
return *m_data.get();
|
||||
}
|
||||
|
||||
std::u16string& String::mutStr()
|
||||
{
|
||||
detach();
|
||||
return *m_data.get();
|
||||
}
|
||||
|
||||
void String::detach()
|
||||
{
|
||||
if (!m_data) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_data.use_count() == 1) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_data = std::make_shared<std::u16string>(*m_data);
|
||||
}
|
||||
|
||||
String& String::operator=(const char16_t* str)
|
||||
{
|
||||
m_data = std::make_shared<std::u16string>(str);
|
||||
return *this;
|
||||
}
|
||||
|
||||
String& String::operator +=(const String& s)
|
||||
{
|
||||
mutStr() += s.constStr();
|
||||
return *this;
|
||||
}
|
||||
|
||||
String& String::operator +=(const char16_t* s)
|
||||
{
|
||||
mutStr() += s;
|
||||
return *this;
|
||||
}
|
||||
|
||||
String String::fromUtf8(const char* str)
|
||||
{
|
||||
String s;
|
||||
|
@ -90,15 +142,12 @@ ByteArray String::toAscii(bool* ok) const
|
|||
}
|
||||
|
||||
for (size_t i = 0; i < size(); ++i) {
|
||||
char16_t ch = m_data->at(i);
|
||||
if (ch > 0xff) {
|
||||
ba[i] = '?';
|
||||
if (ok) {
|
||||
*ok = false;
|
||||
}
|
||||
} else {
|
||||
ba[i] = static_cast<uint8_t>(ch);
|
||||
bool cok = false;
|
||||
char ch = Char(m_data->at(i)).ascii(&cok);
|
||||
if (!cok && ok) {
|
||||
*ok = false;
|
||||
}
|
||||
ba[i] = static_cast<uint8_t>(ch);
|
||||
}
|
||||
return ba;
|
||||
}
|
||||
|
@ -153,6 +202,106 @@ Char String::at(size_t i) const
|
|||
return Char(m_data->at(i));
|
||||
}
|
||||
|
||||
bool String::contains(const Char& ch) const
|
||||
{
|
||||
return constStr().find(ch.unicode()) != std::u16string::npos;
|
||||
}
|
||||
|
||||
bool String::startsWith(const AsciiStringView& str, CaseSensitivity cs) const
|
||||
{
|
||||
if (str.size() > size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < str.size(); ++i) {
|
||||
if (Char(m_data->at(i)).ascii() == str.at(i).ascii()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cs == CaseInsensitive) {
|
||||
if (AsciiChar::toLower(Char(m_data->at(i)).ascii()) == AsciiChar::toLower(str.at(i).ascii())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool String::endsWith(const AsciiStringView& str, CaseSensitivity cs) const
|
||||
{
|
||||
if (str.size() > size()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t start = size() - str.size();
|
||||
for (size_t i = 0; i < str.size(); ++i) {
|
||||
if (Char(m_data->at(start + i)).ascii() == str.at(i).ascii()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (cs == CaseInsensitive) {
|
||||
if (AsciiChar::toLower(Char(m_data->at(start + i)).ascii()) == AsciiChar::toLower(str.at(i).ascii())) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
StringList String::split(const Char& ch) const
|
||||
{
|
||||
StringList out;
|
||||
std::size_t current, previous = 0;
|
||||
current = constStr().find(ch.unicode());
|
||||
while (current != std::string::npos) {
|
||||
String sub = mid(previous, current - previous);
|
||||
out.push_back(std::move(sub));
|
||||
previous = current + 1;
|
||||
current = constStr().find(ch.unicode(), previous);
|
||||
}
|
||||
String sub = mid(previous, current - previous);
|
||||
out.push_back(std::move(sub));
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
String& String::replace(const String& before, const String& after)
|
||||
{
|
||||
size_t start_pos = 0;
|
||||
while ((start_pos = m_data->find(before.constStr(), start_pos)) != std::string::npos) {
|
||||
m_data->replace(start_pos, before.size(), after.constStr());
|
||||
start_pos += after.size(); // Handles case where 'after' is a substring of 'before'
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
String String::mid(size_t pos, size_t count) const
|
||||
{
|
||||
String s;
|
||||
s.mutStr() = constStr().substr(pos, count);
|
||||
return s;
|
||||
}
|
||||
|
||||
// ============================
|
||||
// AsciiStringView
|
||||
// ============================
|
||||
|
||||
char AsciiChar::toLower(char ch)
|
||||
{
|
||||
return static_cast<char>(std::tolower(static_cast<unsigned char>(ch)));
|
||||
}
|
||||
|
||||
char AsciiChar::toUpper(char ch)
|
||||
{
|
||||
return static_cast<char>(std::toupper(static_cast<unsigned char>(ch)));
|
||||
}
|
||||
|
||||
// ============================
|
||||
// AsciiStringView
|
||||
// ============================
|
||||
|
|
|
@ -32,6 +32,11 @@
|
|||
#include "global/logstream.h"
|
||||
|
||||
namespace mu {
|
||||
enum CaseSensitivity {
|
||||
CaseInsensitive = 0,
|
||||
CaseSensitive = 1
|
||||
};
|
||||
|
||||
// ============================
|
||||
// AsciiChar (ASCII)
|
||||
// ============================
|
||||
|
@ -45,6 +50,9 @@ public:
|
|||
inline char ascii() const noexcept { return ch; }
|
||||
inline char16_t unicode() const noexcept { return char16_t(ch); }
|
||||
|
||||
static char toLower(char ch);
|
||||
static char toUpper(char ch);
|
||||
|
||||
private:
|
||||
char ch = 0;
|
||||
};
|
||||
|
@ -58,16 +66,22 @@ public:
|
|||
|
||||
Char() = default;
|
||||
Char(char16_t c)
|
||||
: ch(c) {}
|
||||
: m_ch(c) {}
|
||||
Char(AsciiChar c)
|
||||
: ch(c.unicode()) {}
|
||||
: m_ch(c.unicode()) {}
|
||||
|
||||
inline bool operator ==(Char c) const { return ch == c.ch; }
|
||||
inline bool operator ==(char16_t c) const { return ch == c; }
|
||||
inline bool operator ==(AsciiChar c) const { return ch == c.unicode(); }
|
||||
inline bool operator ==(Char c) const { return m_ch == c.m_ch; }
|
||||
inline bool operator !=(Char c) const { return !operator ==(c); }
|
||||
inline bool operator ==(char16_t c) const { return m_ch == c; }
|
||||
inline bool operator !=(char16_t c) const { return !operator ==(c); }
|
||||
inline bool operator ==(AsciiChar c) const { return m_ch == c.unicode(); }
|
||||
inline bool operator !=(AsciiChar c) const { return !operator ==(c); }
|
||||
|
||||
inline char16_t unicode() const { return m_ch; }
|
||||
inline char ascii(bool* ok = nullptr) const;
|
||||
|
||||
private:
|
||||
char16_t ch = 0;
|
||||
char16_t m_ch = 0;
|
||||
};
|
||||
|
||||
// ============================
|
||||
|
@ -83,6 +97,8 @@ public:
|
|||
// ============================
|
||||
// String (UTF-16)
|
||||
// ============================
|
||||
class StringList;
|
||||
class AsciiStringView;
|
||||
class String
|
||||
{
|
||||
public:
|
||||
|
@ -94,12 +110,12 @@ public:
|
|||
String(const char16_t* str);
|
||||
String& operator=(const char16_t* str);
|
||||
|
||||
bool operator ==(const String& s) const { return *m_data.get() == *s.m_data.get(); }
|
||||
bool operator !=(const String& s) const { return !operator ==(s); }
|
||||
inline bool operator ==(const String& s) const { return constStr() == s.constStr(); }
|
||||
inline bool operator !=(const String& s) const { return !operator ==(s); }
|
||||
inline bool operator <(const String& s) const { return constStr() < s.constStr(); }
|
||||
|
||||
size_t size() const;
|
||||
bool empty() const;
|
||||
Char at(size_t i) const;
|
||||
String& operator +=(const String& s);
|
||||
String& operator +=(const char16_t* s);
|
||||
|
||||
static String fromUtf8(const char* str);
|
||||
ByteArray toUtf8() const;
|
||||
|
@ -115,10 +131,35 @@ public:
|
|||
QString toQString() const;
|
||||
//#endif
|
||||
|
||||
size_t size() const;
|
||||
bool empty() const;
|
||||
Char at(size_t i) const;
|
||||
bool contains(const Char& ch) const;
|
||||
|
||||
//! NOTE Now implemented only compare with ASCII
|
||||
bool startsWith(const AsciiStringView& str, CaseSensitivity cs = CaseSensitive) const;
|
||||
bool endsWith(const AsciiStringView& str, CaseSensitivity cs = CaseSensitive) const;
|
||||
|
||||
StringList split(const Char& ch) const;
|
||||
String& replace(const String& before, const String& after);
|
||||
|
||||
String mid(size_t pos, size_t count = npos) const;
|
||||
|
||||
private:
|
||||
const std::u16string& constStr() const;
|
||||
std::u16string& mutStr();
|
||||
void detach();
|
||||
|
||||
std::shared_ptr<std::u16string> m_data;
|
||||
};
|
||||
|
||||
class StringList : public std::vector<String>
|
||||
{
|
||||
public:
|
||||
|
||||
StringList& operator <<(const String& s) { push_back(s); return *this; }
|
||||
};
|
||||
|
||||
// ============================
|
||||
// AsciiStringView (ASCII)
|
||||
// Be carefully!!, this class just hold pointer to string (no copy), so source string should be present, while use view
|
||||
|
@ -173,6 +214,21 @@ private:
|
|||
};
|
||||
}
|
||||
|
||||
// ============================
|
||||
// String (UTF-16)
|
||||
// ============================
|
||||
inline bool operator ==(const char16_t* s1, const mu::String& s2) { return s2 == s1; }
|
||||
inline bool operator !=(const char16_t* s1, const mu::String& s2) { return s2 != s1; }
|
||||
inline const mu::String operator+(const mu::String& s1, const mu::String& s2) { mu::String t(s1); t += s2; return t; }
|
||||
inline const mu::String operator+(const mu::String& s1, const char16_t* s2) { mu::String t(s1); t += s2; return t; }
|
||||
inline const mu::String operator+(const char16_t* s1, const mu::String& s2) { mu::String t(s1); t += s2; return t; }
|
||||
|
||||
inline mu::logger::Stream& operator<<(mu::logger::Stream& s, const mu::String& str)
|
||||
{
|
||||
s << str.toUtf8().constChar();
|
||||
return s;
|
||||
}
|
||||
|
||||
// ============================
|
||||
// AsciiStringView (ASCII)
|
||||
// ============================
|
||||
|
|
Loading…
Reference in a new issue