Qt Core 之 QString

发布于:2025-09-04 ⋅ 阅读:(19) ⋅ 点赞:(0)

QString 是 Qt 框架中用于处理字符串的核心类,在 Qt 开发中,涉及到与 Qt 的库交互时,使用 QString,往往会比标准库的 std::string更方便些。

Tips:使用 utf-8 编码格式,可以规避中文乱码的情况。

表格速览

方法类别 方法签名 功能描述
构造与初始化 QString() 创建空字符串
QString(const char *str) 从 C 字符串构造
QString(QChar ch) 从单个字符构造
QString(int size, QChar ch) 创建指定大小的填充字符串
static QString number(int n, int base=10) 从数字创建字符串
static QString fromUtf8(const char *str) 从 UTF-8 字节数组创建
容量查询 int length() const 返回字符数
int size() const 同 length()
bool isEmpty() const 判断是否为空
bool isNull() const 判断是否为 null 字符串
int capacity() const 返回已分配内存的字符数
访问元素 QChar at(int position) const 访问指定位置字符(只读)
QChar operator[](int position) const 访问指定位置字符(只读)
QChar& operator[](int position) 访问指定位置字符(可修改)
QChar front() const 访问第一个字符
QChar back() const 访问最后一个字符
修改操作 void append(const QString &str) 追加字符串
void prepend(const QString &str) 前置字符串
void insert(int position, const QString &str) 插入字符串
void remove(int position, int n) 移除指定位置开始的 n 个字符
void replace(int position, int n, const QString &after) 替换子字符串
void chop(int n) 移除末尾 n 个字符
void truncate(int position) 截断到指定位置
void clear() 清空字符串
void resize(int size) 调整大小
void reserve(int size) 预留空间
void squeeze() 释放未使用的内存
子字符串操作 QString left(int n) const 获取左边 n 个字符
QString right(int n) const 获取右边 n 个字符
QString mid(int position, int n = -1) const 获取中间子字符串
QString sliced(int pos, int n) const 获取切片(Qt 6)
QString first(int n) const 获取前 n 个字符(Qt 6)
QString last(int n) const 获取后 n 个字符(Qt 6)
查找与比较 int indexOf(const QString &str, int from = 0) const 查找子字符串位置
int lastIndexOf(const QString &str, int from = -1) const 从后向前查找
bool contains(const QString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const 判断是否包含
bool startsWith(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const 判断是否以指定字符串开头
bool endsWith(const QString &s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const 判断是否以指定字符串结尾
int compare(const QString &other, Qt::CaseSensitivity cs = Qt::CaseSensitive) const 比较字符串
转换与格式化 QByteArray toUtf8() const 转换为 UTF-8 字节数组
QByteArray toLatin1() const 转换为 Latin-1 字节数组
std::string toStdString() const 转换为 std::string
int toInt(bool *ok = nullptr, int base = 10) const 转换为整数
float toFloat(bool *ok = nullptr) const 转换为浮点数
double toDouble(bool *ok = nullptr) const 转换为双精度浮点数
QString arg(const QString &a, int fieldWidth = 0, const QChar &fillChar = QLatin1Char(’ ')) const 参数化格式化
大小写转换 QString toLower() const 转换为小写
QString toUpper() const 转换为大写
QString toCaseFolded() const 转换为大小写折叠形式
空白处理 QString trimmed() const 移除两端空白
QString simplified() const 移除两端空白并压缩内部空白
正则表达式 bool contains(const QRegularExpression &re) const 使用正则表达式判断是否包含
QString captured(const QRegularExpression &re) const 使用正则表达式捕获
内存管理 void detach() 强制分离(禁用隐式共享)
bool isDetached() const 判断是否已分离

一、 为什么是 QString?

在 C++ 中,处理字符串通常使用 std::string(或更古老的 char*)。Qt 提供了 QString,并非重复造轮子,而是为了解决一些关键问题:

  1. Unicode 支持QString 内部使用 UTF-16 编码,这意味着它可以无缝地存储和显示世界上几乎所有书写系统的字符(如英文、中文、阿拉伯文、表情符号 😊)。这是它与 std::string(本质是字节数组)最根本的区别。
  2. 隐式共享(写时复制)QString 使用了隐式共享技术来优化内存和性能。多个 QString 对象可以共享同一份字符串数据,只有在某个对象需要修改数据时,才会真正执行复制操作。这对传递大型字符串非常高效。
  3. 丰富的 APIQString 提供了大量便捷的成员函数,用于字符串拼接、分割、查找、替换、大小写转换等操作,大大简化了字符串处理。
  4. 与 Qt 框架的无缝集成:几乎所有 Qt 的 API 都接受和返回 QString,这使得它在 Qt 生态中是无可替代的。
  5. 编码转换: 内置多种编码转换功能。

简单来说:在 Qt 项目中,应该始终使用 QString 而不是 std::string 来处理用户可见的文本。


二、 创建与初始化

QString 有多种创建方式,使用前需要#include <QString>
通常使用 构造函数 初始化字符串。

常用:

#include <QString>
#include <QDebug>

int main(){
	// 1. 仅声明
	QString Str;
	
	// 方式一:调用构造函数
	QString qstr1 = QString("Hello from C-string");
	
	// 方式二:隐式调用构造函数(常见,有微小的开销)
	QString qstr2 = "Hello from C-string";
	return 0;
}

方法二中,编译器会在背后悄悄地调用了 QString(const char*) 这个构造函数,创建一个临时的 QString对象,然后再通过拷贝构造函数初始化 qstr,因此会有一点点开销。

补充:

#include <QString>
#include <QDebug>

int main(){
	// 1. 空字符串
	QString emptyStr;
	
	// 2. 隐式调用构造函数,类似于C风格的字符串(const char*)构造
	// Qt会自动将const char*从本地编码转换为UTF-16。
	// Tips:在源文件中直接写中文等非ASCII字符,需确保文件编码为UTF-8(推荐)。
	QString str1 = "Hello, 世界!";
	
	// 3. 从另一个QString构造(拷贝构造,受益于隐式共享,开销很小)
	QString str2 = str1;
	
	// 4. 重复一个字符
	QString str3(5, 'Q'); // "QQQQQ"
	
	// 5. 使用 QStringLiteral 宏(推荐用于编译期字符串常量)
	// 此宏在编译期将字符串字面量直接转换为QString的内部格式,避免运行时的转换开销,性能最佳。
	QString str4 = QStringLiteral("性能更好的字符串常量");
	
	// 6. 使用 u"" 字面量(C++11及以上)
	// 这是另一种高效的初始化方式,直接从Unicode字面量创建QString。
	QString str5 = u"Unicode字符串";
	return 0;
}

三、 常用操作与成员函数

1. 字符串拼接

支持以下语法:

  • +运算符
  • append() 函数:追加
  • arg() 函数:格式化拼接。 ✅推荐
  • sprintf() 函数:格式化

为什么推荐方法三:

  1. 类型安全:在编译时由编译器检查参数类型是否正确,杜绝了因类型不匹配导致的运行时崩溃。
  2. 多语种翻译的支持:通过编号占位符(%1, %2)而非顺序占位符,让翻译人员能根据目标语言的语法灵活调整句子结构,实现地道的本地化,无需程序员修改代码。

    简而言之,有利于翻译达到“信达雅”中的“达”,读起来不拗口。

具体用法案例:

#include <QString>
#include <QDebug>

QString str1 = "Hello";
QString str2 = "World";

int main(){
	// 方法1:使用 `+` 运算符
	QString result1 = str1 + " " + str2 + "!";
	
	// 方法2:使用 `append()` 函数(会修改原对象)
	str1.append(" ").append(str2).append("!");
	
	// 方法3:使用 `arg()` 函数(格式化拼接,推荐)
	// %1, %2 是占位符,会被后面的参数按顺序替换。
	QString name = "Alice";
	int age = 25;
	QString message = QString("Name: %1, Age: %2").arg(name).arg(age);
	// message = "Name: Alice, Age: 25"
	
	// 方法4:使用sprintf(不推荐,Qt5后建议使用arg)
	QString msg;
	msg.sprintf("Score: %d, Ratio: %.2f", 100, 0.95);
	return 0;
}

2. 字符串查询 与 判断

常用功能:

  • 获取长度
  • 是否空字符串
  • 子串检查
  • 子串开头或结束
  • 查找子串位置
  • 比较字符串是否相等
#include <QString>
#include <QDebug>

QString s = "Hello Qt";

int main(){
	// 获取长度
	int len = s.length(); // or s.size(), s.count()
	
	// 是否为空/为空字符串
	bool isEmpty = s.isEmpty();
	bool isNull = s.isNull(); // 注意:isEmpty返回true不一定isNull返回true
	
	// 判断是否包含子串
	bool hasQt = s.contains("Qt"); // true
	bool hasQtCase = s.contains("qt", Qt::CaseInsensitive); // true, 大小写不敏感
	
	// 判断是否以某子串开始/结束
	bool starts = s.startsWith("Hello"); // true
	bool ends = s.endsWith("World"); // false
	
	// 查找子串位置
	int index = s.indexOf("l"); // 2, 第一个'l'的位置
	int lastIndex = s.lastIndexOf("l"); // 3, 最后一个'l'的位置
	
	// 比较
	int cmp = s.compare("hello qt", Qt::CaseInsensitive); // 0,表示相等
	bool eq = (s == "Hello Qt"); // true
	return 0;
}

3. 字符串修改 与 截取

常用功能:

  • 替换子串
  • 去除空白
  • 大小写转换
  • 插入与删除
  • 截取子串:可配合 查找字串位置*IndexOf函数 一起使用。

具体用法案例:

#include <QString>
#include <QDebug>

QString s = "I like apples";
int main(){
	// 替换
	s.replace("apples", "oranges"); // "I like oranges"
	
	// 去除首尾空白字符(非常常用,处理用户输入)
	QString userInput = "  hello  \n";
	QString trimmed = userInput.trimmed(); // "hello" (不修改原对象)
	// userInput.trim(); // 这会修改原对象本身
	
	// 改变大小写
	QString lower = s.toLower(); // "i like apples"
	QString upper = s.toUpper(); // "I LIKE APPLES"
	
	// 插入与删除
	s.insert(2, "really "); // "I really like apples"
	s.remove(2, 7); // 从索引2开始删除7个字符 -> "I like apples"
	
	// 截取子串
	QString sub1 = s.mid(2); // "like apples" (从索引2到结尾)
	QString sub2 = s.mid(2, 4); // "like" (从索引2开始,截取4个字符)
	QString left = s.left(5); // "I lik"
	QString right = s.right(6); // "apples"
	return 0;
}

4. 字符串分割 与 列表拼接成字符串

常用功能:

  • 字符串的分割:返回给 QStringList对象。
  • 字符串列表的拼接:将字符串列表中的元素拼接成字符串。

具体用法案例:

#include <QString>
#include <QDebug>

QString path = "usr/local/bin";

int main(){
	// 分割:字符串 -> QStringList
	QStringList list = path.split('/');
	// list: ["usr", "local", "bin"]
	
	// 拼接:QStringList -> 字符串
	QString newPath = list.join('\\');
	// newPath: "usr\\local\\bin"
	return 0;
}

5. 数字与字符串的转换

QString 配备了数字与QString之间的转换函数。

#include <QString>
#include <QDebug>
int main(){
	// 数字 -> QString
	QString s1 = QString::number(42); // "42"
	QString s2 = QString::number(3.14, 'f', 2); // "3.14" (格式'f', 精度2位)
	
	// QString -> 数字
	bool ok;
	int i = s1.toInt(&ok); // i=42, ok=true
	double d = s2.toDouble(&ok); // d=3.14, ok=true
	float f = s2.toFloat(&ok);
	
	// 如果转换失败,ok会被设为false,数字值设为0
	int bad = QString("hello").toInt(&ok); // bad=0, ok=false
	return 0;
}

四、 与其他字符串类型的转换

其他的常见需求,主要包括:

  • QStringstd::string 之间
  • QByteArrayQString 之间

推荐规范示例:

#include <QString>
#include <QDebug>
int main(){
	QString qstr = "Hello, Qt";
	
	// 1. QString -> std::string
	// 注意:toStdString() 返回 UTF-8 编码的 std::string。
	std::string std_str = qstr.toStdString();
	
	// 2. std::string -> QString
	QString from_std = QString::fromStdString(std_str);
	
	// 3. QString -> const char* (QByteArray)
	// 注意:toUtf8() 返回一个临时的 QByteArray 对象。
	// 不要保存这个data()指针,它的生命周期只在当前行!
	const char* c_str = qstr.toUtf8().constData(); // 正确用法:立即使用
	QByteArray byteArray = qstr.toUtf8(); // 如果需要持久化,保存QByteArray
	const char* persistent_c_str = byteArray.constData();
	
	// 4. const char* -> QString
	QString from_c_str = QString::fromUtf8(c_str);
	
	// 5. QString -> 平台相关的本地编码 (Local 8-bit),依赖操作系统
	// 通常不推荐,除非与某些老旧的本地API交互。
	QByteArray localBytes = qstr.toLocal8Bit();
	
	// 6. 平台相关的本地编码 -> QString
	QString from_local = QString::fromLocal8Bit(localBytes);
	return 0;
}

五、 应用与实践

应用场景:

  1. 处理用户界面文本:所有 QLabelQPushButton 等控件设置的文本都应使用 QString
  2. 文件路径操作QFile, QDir 等类都使用 QString 表示路径,完美支持中文等 Unicode 路径名。
  3. 网络数据接收:从网络接收的 UTF-8/UTF-16 数据,可以方便地用 QString::fromUtf8() 等进行转换。
  4. 拼接复杂消息:使用 arg() 方法进行格式化,易于阅读和后续的国际化翻译。
  5. 处理用户输入:使用 trimmed() 去除用户输入的首尾空白字符。

实践建议:

  • 性能:对于编译期的字符串常量,优先使用 QStringLiteral("text")
  • 编码:确保源代码文件保存为 UTF-8 编码,以避免中文乱码问题。
  • 转换:与 C 库或系统 API 交互时,注意使用 toUtf8()toLocal8Bit() 进行正确的编码转换,并注意指针的生命周期。
  • 隐式共享:放心地以值传递方式返回或传递 QString,隐式共享机制保证了其高性能。

关于性能方面的补充:

  • 避免不必要的复制:使用 const引用传递 QString 参数
  • 预留空间:对于需要频繁追加操作的大字符串,使用 reserve() 预留空间
  • 使用 QStringView:对于只读操作,使用 QStringView 避免复制
  • 批量操作:尽量使用批量操作函数而不是多次调用单个操作
  • 避免中间对象:链式调用可以减少临时对象的创建
// 不好的做法:创建多个临时对象
QString result = str1 + str2 + str3 + str4;
// 好的做法:使用 append 或 reserve
QString result;
result.reserve(str1.size() + str2.size() + str3.size() + str4.size());
result.append(str1).append(str2).append(str3).append(str4);

高级用法

// 使用 QStringRef 避免复制(Qt 5)
QString longString = "This is a very long string...";
QStringRef subString(&longString, 5, 10);  // 不复制数据

// 使用 QStringView(Qt 5.10+)
QStringView view(longString);
QStringView subView = view.mid(5, 10);

// 迭代器访问
QString str = "Hello";
for (QChar ch : str) {
    qDebug() << ch;
}

// 使用反向迭代器
for (auto it = str.rbegin(); it != str.rend(); ++it) {
    qDebug() << *it;
}

// 内存管理
QString str1 = "Shared string";
QString str2 = str1;  // 共享数据
str2[0] = 's';        // 写时复制,现在数据分离

正则表达式

QString text = "My email is example@example.com and phone is 123-456-7890";

// 使用正则表达式查找
QRegularExpression emailRegex(R"(\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b)", 
                             QRegularExpression::CaseInsensitiveOption);
QRegularExpressionMatch match = emailRegex.match(text);
if (match.hasMatch()) {
    QString email = match.captured(0);  // "example@example.com"
}

// 使用正则表达式替换
QRegularExpression phoneRegex(R"(\d{3}-\d{3}-\d{4})");
QString result = text.replace(phoneRegex, "XXX-XXX-XXXX");
// "My email is example@example.com and phone is XXX-XXX-XXXX"

// 分割字符串
QString data = "apple,orange,banana";
QStringList fruits = data.split(QRegularExpression("\\s*,\\s*"));
// ["apple", "orange", "banana"]

六、 注意点

  1. constData() 指针的生命周期
    // 错误!temp对象在这一行结束后就被销毁,c_str成了悬空指针!
    const char* c_str = myQString.toUtf8().constData();
    
    // 正确!先将QByteArray保存到一个变量,延长其生命周期。
    QByteArray ba = myQString.toUtf8();
    const char* safe_c_str = ba.constData();
    
  2. “空”与“null”的区别
    • 默认构造的 QString() 既是 null 也是 empty
    • QString("")empty 但不是 null
    • 在大多数情况下,你应该使用 isEmpty() 来判断字符串是否有效。
  3. 跨线程操作QString可重入(Reentrant) 的,但不是线程安全(Thread-safe) 的。多个线程同时修改同一个 QString 对象需要外部同步机制(如互斥锁)。

持续更新中。。。。。。。


网站公告

今日签到

点亮在社区的每一天
去签到