QString
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
,并非重复造轮子,而是为了解决一些关键问题:
- Unicode 支持:
QString
内部使用 UTF-16 编码,这意味着它可以无缝地存储和显示世界上几乎所有书写系统的字符(如英文、中文、阿拉伯文、表情符号 😊)。这是它与std::string
(本质是字节数组)最根本的区别。 - 隐式共享(写时复制):
QString
使用了隐式共享技术来优化内存和性能。多个QString
对象可以共享同一份字符串数据,只有在某个对象需要修改数据时,才会真正执行复制操作。这对传递大型字符串非常高效。 - 丰富的 API:
QString
提供了大量便捷的成员函数,用于字符串拼接、分割、查找、替换、大小写转换等操作,大大简化了字符串处理。 - 与 Qt 框架的无缝集成:几乎所有 Qt 的 API 都接受和返回
QString
,这使得它在 Qt 生态中是无可替代的。 - 编码转换: 内置多种编码转换功能。
简单来说:在 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)而非顺序占位符,让翻译人员能根据目标语言的语法灵活调整句子结构,实现地道的本地化,无需程序员修改代码。
简而言之,有利于翻译达到“信达雅”中的“达”,读起来不拗口。
具体用法案例:
#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;
}
四、 与其他字符串类型的转换
其他的常见需求,主要包括:
QString
与std::string
之间QByteArray
与QString
之间
推荐规范示例:
#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;
}
五、 应用与实践
应用场景:
- 处理用户界面文本:所有
QLabel
、QPushButton
等控件设置的文本都应使用QString
。 - 文件路径操作:
QFile
,QDir
等类都使用QString
表示路径,完美支持中文等 Unicode 路径名。 - 网络数据接收:从网络接收的 UTF-8/UTF-16 数据,可以方便地用
QString::fromUtf8()
等进行转换。 - 拼接复杂消息:使用
arg()
方法进行格式化,易于阅读和后续的国际化翻译。 - 处理用户输入:使用
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"]
六、 注意点
constData()
指针的生命周期:// 错误!temp对象在这一行结束后就被销毁,c_str成了悬空指针! const char* c_str = myQString.toUtf8().constData(); // 正确!先将QByteArray保存到一个变量,延长其生命周期。 QByteArray ba = myQString.toUtf8(); const char* safe_c_str = ba.constData();
- “空”与“null”的区别:
- 默认构造的
QString()
既是null
也是empty
。 QString("")
是empty
但不是null
。- 在大多数情况下,你应该使用
isEmpty()
来判断字符串是否有效。
- 默认构造的
- 跨线程操作:
QString
是可重入(Reentrant) 的,但不是线程安全(Thread-safe) 的。多个线程同时修改同一个QString
对象需要外部同步机制(如互斥锁)。
持续更新中。。。。。。。