Qt 技巧笔记 (六) 字符串 QString 容器简介

Qt 技巧笔记 (六) 字符串 QString 容器简介

​ 在Qt框架中,QString是一个强大而灵活的字符串容器,专门为处理Unicode 字符而设计。其提供了许多方便的方法来操作和处理字符串,使得在跨平台开发中能够轻松地进行文本操作。QString是Qt开发中不可或缺的一部分,它的灵活性和强大的功能使其成为处理文本与字符串操作的理想选择。本篇将深刻分析QString的各种用法,包括字符串的连接,追加与移除,格式化输出,统计字符串长度,去空格操作,字符串的切割与截取以及类型转化等。

1.1 QString的简介

​ QString 是Qt中的一个类,用于字符串,QString没有父类。QString 存储的是一串字符,每个字符是一个QChar类型的数据。QChar使用的是UTF-16编码,一个字符串是一个2字节数据。对于超过65535的Unicode编码,QString使用两个连续的QChar 字符表述。UTF-16 是一种Unicode 编码,能表述汉字,在QString 字符串中一个汉字是一个字符。QString类定义了大量的接口函数用于字符串操作。QString 在Qt类库中应用非常广泛,很多函数的参数是QString 类型。QString 使用隐式共享减少内存占用,也就是只有在修改一个字符串的时候,这个字符串才会被复制。 其同文件

配置项目 配置信息
头文件(Header) #include <QString>
CMake 文件 find_package(Qt6 REQUIRED COMPONENTS Core)
target_link_libraries(mytarget PRIVATE Qt6::Core
Qmake文件 QT += core

QString 是Qt框架内的一个字符串类,它提供了一种高效,可扩展的字符串处理方法。QString的内存模型主要基于以下几个方面:

  • 隐式共享(Implicit Sharing):QString使用隐式共享来实现内存管理。这意味着当你创建一个QString对象的副本时,实际上并不会复制原始字符串的内容。相反,新的QString对象会共享原始对象的内存。这种方法可以显著减少内存使用和提高性能,特别是在处理大量字符串时。当你对其中一个QString对象进行修改时,Qt会自动创建一个新的内存块来存储修改后的字符串,而原始字符串的内存仍然保持不变。
  • 字符编码:QString支持多种字符编码,如UTF-8UTF-16UTF-32。这使得QString能够处理各种语言和字符集。在内部,QString使用UTF-16编码来存储字符串。这种编码方式允许QString在处理大多数字符时保持高效,同时也支持包括表情符号在内的Unicode字符。
  • 内存分配:QString使用QByteArray作为其内部存储。QByteArray是一个可变大小的字节数组,它使用预分配策略来优化内存分配。当字符串增长时,QByteArray会预分配额外的内存,以减少内存重新分配的次数。这种策略有助于提高字符串操作的性能。
  • 字符串操作:QString提供了丰富的字符串操作方法,如拼接、截取、查找、替换等。这些操作通常都是高效的,因为它们利用了QString的内部表示和内存管理策略。在执行字符串操作时,QString会尽量避免不必要的内存分配和复制,从而提高性能。

总之,QString的内存模型主要基于隐式共享、字符编码、内存分配和字符串操作等方面。这些设计使得QString在处理字符串时具有高效、可扩展的性能。在使用QString时,请确保遵循Qt框架的最佳实践和建议,以充分利用其内存模型和性能优势。

QString 底层存储的是16 位QChar序列,采用写时复制(COW)技术。当执行QString s="你好"时,系统会:

  1. 分配内存存储UTF-16编码的字符串
  2. 将源码中的UTF-8转换为UTF-16
  3. 建立引用计数结构

这种设计在频繁修改字符串时很高效,但对常量字符串就太重了。我曾测试过,创建10万个QString("test")对象比创建QLatin1String("test")多消耗40%内存。

1.2 QString的创建,连接追加与移除操作

创建与初始化(最常用方式)

QString 是一个类,有多种构造函数,定义QString字符串的较简单方式是给它的构造函数传递一个const char类型的数据。例如,下面的代码定义了一个QString对象str,并且初始化其字符串内容为Hello Qt。在Qt Creator中,所有源程序文件都默认使用UTF-8 编码进行使用,所有,赋值语句右侧的Hello Qt 是一个C语言标准的const char * 类型的字符串,以\0作为结束符。QString 会使用其静态函数 fromUtf8()将这个 const char* 类型的数据转换为UTF-8编码的字符串。

方法 功能 示例
QString() 构造空字符串 QString str;
QString(const char *str) 从C字符串构造 QString str("Hello");
QString(int size, QChar ch) 构造包含size个ch的字符串 QString str(5, 'A'); // "AAAAA"
QString(const QString &other) 拷贝构造 QString str2(str1);
static QString fromUtf8(const char *str) UTF-8字节序列构造 QString str = QString::fromUtf8("中文");
int toInt(bool *ok = nullptr, int base = 10) 转换为整数,可指定进制 int i = str.toInt();
static QString number(int n, int base = 10) 整数转换为字符串,可指定进制 QString s = QString::number(255, 16); // "ff"
static QString number(double n, char format = 'g', int precision = 6) 浮点数转换为字符串 QString s = QString::number(3.14, 'f', 2); // "3.14"
std::string toStdString() const 转换为std::string std::string s = str.toStdString();

QString()           // 构造一个空串(null string)
QString(const QChar *unicode, int size = -1) // 构造QString类以QChar数组,以‘\0’为结尾
QString(QChar ch)  // 以一个QChar字符构造QString
QString(int size, QChar ch) // 以size个QChar字符构造QString类
QString(QLatin1String str)  //使用str(单字节编码)构造QString
QString(const QString &other)  // 使用其他的QString类other构造改QString类
QString(const char *str)       // 使用字符数组构造QString类
QString(const QByteArray &ba)  //使用字节数组ba构造QString类

QString有两种“空”的状态:

  1. Null(空): 通过默认构造函数QString() 创建,qs.isNull()trueqs.isEmpty()true。它表述未初始化或未赋值。
  2. Empty(空字符串):通过QString("")QString(QChar(),0)创建,qs.isNull()falseqs.isEmpty()true。它表述一个长度为零的字符串。

在定义字符串常量时,为了效率,应使用QStringLiteral。它能够确保字符串在编译时就被创建,避免运行时的解析和内存分配。

#include <QString>
#include <QDebug>

//  最佳实践:编译时创建,效率最高
const QString KEY_NAME = QStringLiteral("username"); 

// 避免使用:会在运行时构造 QString
const QString KEY_NAME_SLOW("username"); 

qDebug() << "Key Name:" << KEY_NAME;

除了上述的 C 字符串构造,QString 还提供了许多实用的构造函数和静态工厂方法。

要将数字转换为字符串,最好使用QString::number()arg()函数。

int number = 42;
double pi = 3.14159;

//  推荐方法 1: 静态工厂方法,用于简单的数字转换
QString s1 = QString::number(number);          // "42"
QString s2 = QString::number(pi, 'f', 2);      // "3.14" (保留两位小数)

//  推荐方法 2: 格式化字符串
QString s3 = QString("%1 = %2").arg(number).arg(pi); 
qDebug() << s3; // "42 = 3.14159"

QByteArray 通常用于从网络或文件中读取原始字节数据。可以通过指定编码从QByteArray 构造 QString

#include <QByteArray>

QByteArray byte_array("hello world", 11);

//  从 QByteArray 构造 (假设数据是 Latin-1)
QString s_latin1 = QString(byte_array); 
qDebug() << "Latin-1:" << s_latin1;

//  从 QByteArray 构造 (明确指定编码,例如 UTF-8)
QByteArray utf8_bytes = "你好"_qba; // Qt 5.10+ 的方便后缀
QString s_utf8 = QString::fromUtf8(utf8_bytes);
qDebug() << "UTF-8:" << s_utf8;

可以用一个字符重复N次来快速构造一个字符串。

// 构造一个包含 10 个星号的字符串 "*****..."
QString stars = QString(10, '*'); 
qDebug() << stars; // "**********"

字符串操作的转换

​ 在Qt中,字符串的定义可以使用QString str1的方式实现,我们可以使用简单的加号+ 或者 append 方法将两个字符串连接在一起。代码中的toStdString 则代表字符串转换为标准的‘std’格式, 其链接的方式:

  1. 使用加法运算符可以直接将两个QString 字符串连接起来;
  2. 使用函数append()在当前的字符串后面添加字符串;
  3. 使用函数prepend()在当前的字符串前面添加字符串;
方 法 功 能 示 例
QString &append(const QString &str) 追加字符串 str.append(" World");
QString &prepend(const QString &str) 在开头添加字符串 str.prepend("Hello ");
QStringList split(const QString &sep, ...) const 按分隔符分割字符串 str.split(" ");
QString section(const QString &sep, int start, int end, ...) const 获取由分隔符分隔的某一部分 str.section(' ', 0, 0); // 第一部分
QString &remove(int position, int n) 从position开始移除n个字符 str.remove(5, 6);
QString &remove(const QString &str, Qt::CaseSensitivity cs = Qt::CaseSensitive) 移除所有匹配的子串 str.remove("World");
int indexOf(const QString &str, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const 查找子串首次出现位置 str.indexOf("World");
QString repeated(int times) const 重复times次 str.repeated(3);
QString a = "Hello";
QString b = "Qt";

// 方式1:+ 运算符(最直观)
QString c = a + ", " + b + "!";           // "Hello, Qt!"

// 方式2:append(最高效,推荐频繁追加)
a.append(", ").append(b).append("!");     // a 变为 "Hello, Qt!"

// 方式3:arg(格式化,类似 printf)
QString formatted = QString("%1 loves %2").arg("Alice").arg("Qt"); // "Alice loves Qt"

QString提供了多种方式来追加与移除字符串,追加时可以用append/push_back()prepend() 提供了在头部追加的功能。

#include <QCoreApplication>
#include <QString>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QString Str = "hello ";

    Str.append("lyshark");
    Str.push_back("test");
    Str.remove("hello");
    Str.prepend("-->");

    std::cout << Str.toStdString().data() << std::endl;

    return a.exec();
}

字符串长度的统计

字符串长度统计有多种,可以使用count()也可以是size() 或是length()三者均可以。

#include <QCoreApplication>
#include <QString>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QString Str1;
    Str1.sprintf("%s %s","Welcome","to you !");
    std::cout << Str1.toStdString().data() << std::endl;

    // 实现统计字符串长度
    std::cout << Str1.count() << std::endl;
    std::cout << Str1.size() << std::endl;
    std::cout << Str1.length() << std::endl;

    return a.exec();
}

字符串替换

字符串的替换可以使用repalce()函数,该函数接受两个参数第一个时需要替换的字符串,第二个是替换后字符串。

#include <QCoreApplication>
#include <QString>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QString str = "hello lyshark welcome admin";
    int index;
    bool ref;

    // 替换字符串中所有的lyshark为admin
    str = str.replace("lyshark","admin");
    std::cout << str.toStdString().data() << std::endl;

    return a.exec();
}

字符串的去空格

空格的去除有多种方式,使用trimmed()可以实现去掉字符串首位两端空格,使用simplified() 去掉所有空格,中间连续的只保留一个。

#include <QCoreApplication>
#include <QString>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 去空格
    QString Str1 = " hello  lyshark   welcome !  ";

    Str1 = Str1.trimmed();               // 去掉首尾空格
    Str1 = Str1.simplified();            // 去掉所有空格,中间连续的只保留一个
    std::cout << Str1.toStdString().data() << std::endl;

    return a.exec();
}

字符串的选取

使用QString类中的一些字符串的操作方法,通过mid()函数的可以截取区间参数,当然remove()也支持区间参数。

#include <QCoreApplication>
#include <QString>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QString str1 = "hello lyshark !";

    // 从索引2开始向后取10
    str1 = str1.mid(2,10);
    std::cout << str1.toStdString().data() << std::endl;

    //移除,1,3两个位置的字符
    std::cout << (QString("123456").remove(1,3)).toStdString().data() << std::endl;

    // 超过 11 个字符就保留 11 个字符,否则不足替换为 '.'
    std::cout << (QString("abcdefg").leftJustified(11,'.',true)).toStdString().data() << std::endl;

    return a.exec();
}

1.3 字符串的查询与替换

​ 在Qt中,QString类型提供了多种方法来操作字符串,包括查找,替换,切割等。替换是其中非常常用的一种,可以用来将字符串中的某些部分替换为新的内容。

方 法 功 能 示 例
indexOf(str) 查找子串位置 "Hello".indexOf("ll")2
lastIndexOf(str) 从后查找 "Hello".lastIndexOf("l")3
contains(str) 是否包含 "Hello".contains("ell")true
section(sep, start, end) 获取部分 "A-B-C".section('-',1,1)"B"
startsWith(str) 是否以开头 "Hello".startsWith("He")true
endsWith(str) 是否以结尾 "Hello".endsWith("lo")true

字符串截取

​ 字符串的截取可以使用自带的section()函数,该函数接受三个参数,第一个是截取字符的分隔符,第二和第三个是需要截取的字段,当然也可以通过灵活的利用left/mid/right/indexOf 实现对字符串的截取。

#include <QCoreApplication>
#include <QString>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 字符串的截取
    QString str1 = "uname,uage,usex";
    std::cout << str1.section(",",0,0).toStdString().data() << std::endl;
    std::cout << str1.section(",",1,1).toStdString().data() << std::endl;

    // 自己截取
    QString str2 ="192.168.1.10";
    std::cout << str2.left(str2.indexOf(".")).toStdString().data() << std::endl;
    std::cout << str2.mid(str2.indexOf(".")+1,3).toStdString().data() << std::endl;
    std::cout << str2.mid(str2.indexOf(".")+1,1).toStdString().data() << std::endl;
    std::cout << str2.right(str2.size() - (str2.lastIndexOf(".")+1)).toStdString().data() << std::endl;

    // 切割字符串
    std::cout << (QString("1,2,3,4,5,6").split(',')[2]).toStdString().data() << std::endl;
    return a.exec();
}

字符串空判断

判断一个字符串是否为空,这个功能可以直接使用isNull/isEmpty函数来实现,如下是这三个函数的具体区别。

#include <QCoreApplication>
#include <QString>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 判断字符串是否为空
    QString str4,str5="";
    std::cout << str4.isNull() << std::endl;    // 为空则为True
    std::cout << str5.isNull() << std::endl;    // \0不为空
    std::cout << str5.isEmpty() << std::endl;   // 为空则为False

    return a.exec();
}

判断开头结尾

开头结尾的判断可以使用startsWithendsWith,在判断开头时通过Qt::CaseInsensitive标志定义,而结尾则使用Qt::CaseSensitive标志。

#include <QCoreApplication>
#include <QString>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QString str = "hello lyshark welcome admin";
    int index;
    bool ref;

    // 判断是否以某个字符串开头或结束
    ref = str.startsWith("hello",Qt::CaseInsensitive);      // 判断是否hello开头
    std::cout << ref << std::endl;

    ref = str.endsWith("lyshark",Qt::CaseSensitive);        // 判断是否lyshark结尾
    std::cout << ref << std::endl;

    return a.exec();
}

字符串的替换

字符串的替换可以使用replace()函数,该函数接受两个参数第一个时需要替换的字符串,第二个是替换后的字符串。

#include <QCoreApplication>
#include <QString>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QString str = "hello lyshark welcome admin";
    int index;
    bool ref;

    // 替换字符串中所有的lyshark为admin
    str = str.replace("lyshark","admin");
    std::cout << str.toStdString().data() << std::endl;

    return a.exec();
}

查询字符串包含

​ 在一个字符串中查询是否包含一个子串,这里通过使用 Qt::CaseInsensitive 指定不区分大小写,通过Qt::CaseSensitive 指定为区分大小写,查询函数为contains()保持不变。

#include <QCoreApplication>
#include <QString>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QString str = "hello lyshark welcome admin";
    int index;
    bool ref;

    // 查询字符串中是否包含特定字符
    ref = str.contains("lyshark",Qt::CaseInsensitive);  // 不区分大小写
    std::cout << ref << std::endl;

    ref = str.contains("LYSHARK",Qt::CaseSensitive);    // 区分大小写
    std::cout << ref << std::endl;

    return a.exec();
}

字符串位置的查询

​ 位置查询也是很常见的需求,我们可以使用indexOf()来查询最早出现某个字符的位置,当然也可以使用lastIndexOf()查询最后一次出现的位置,这两个函数接收一个字符串用作过滤条件。

#include <QCoreApplication>
#include <QString>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QString str = "hello lyshark welcome admin";
    int index;
    bool ref;

    // 从字符串中取左边/右边多少个字符
    index = str.indexOf(" ");        // 第一个空格出现的位置
    std::cout << str.left(index).toStdString().data()<< std::endl;

    index = str.lastIndexOf(" ");    // 最后一个空格出现的位置
    std::cout << str.right(str.size() - index - 1).toStdString().data() << std::endl;

    index = str.indexOf("r");        // 第一个出现r的位置
    std::cout << str.left(index).toStdString().data()<< std::endl;

    return a.exec();
}

1.4 字符串类型转换

方法 功能 示例
toInt() 转换为int "123".toInt() → 123
toDouble() 转换为double "3.14".toDouble()3.14
toFloat() 转换为float "3.14".toFloat()3.14f
QString::number() 数字转字符串 number(123)"123"
toStdString() 转std::string "hello".toStdString()
fromStdString() 从std::string构造 fromStdString("str")
toUtf8() UTF-8字节数组 "中文".toUtf8()
fromUtf8() UTF-8字节数组构造 fromUtf8(bytes)

大小写转换

与标准C语言一致,小写转为大写同样可以调用toUpper()函数实现,小写的话可以使用toLower()函数实现。

#include <QCoreApplication>
#include <QString>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QString str = "uname,uage,usex";
    QString int_str = "100,200,300";

    // 大小写转换
    str = str.toUpper();            // 转为大写
    std::cout << str.toStdString().data() << std::endl;
    str = str.toLower();            // 转为小写
    std::cout << str.toStdString().data() << std::endl;

    return a.exec();
}

字符串与整数

​ 字符串与整数的转换同样可以使用标准函数实现,例如将字符串转换为整数,我们首先可以使用section()截取字符串中的特定整数,接着使用toInt()将其转换为十进制整数,当然如果是十六进制可以传入16,而将整数转换为字符串可以通过setNum()实现直接转换。

#include <QCoreApplication>
#include <QString>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QString str = "uname,uage,usex";
    QString int_str = "100,200,300";

    // 将字符串转为整数
    bool flag = false;
    QString x = int_str.section(",",0,0);   // 提取出第一个字符串

    int dec = x.toInt(&flag,10);              // 转为十进制整数
    std::cout << dec << std::endl;

    int hex = x.toUInt(&flag,16);            // 转为十六进制数
    std::cout << hex << std::endl;

    // 将整数转为字符串
    int number = 100;
    QString number_str;

    number_str = number_str.setNum(number,16);  // 转为十六进制字符串
    std::cout << number_str.toStdString().data() << std::endl;

    return a.exec();
}

当然了标准的QString容器内天生也自带转换功能,我们可以使用这些功能进行自定义转换,如下所示;

#include <QCoreApplication>
#include <QString>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 100 转16进制
    std::cout << (QString::number(100,16)).toStdString().data() << std::endl;

    // 转换为 16 进制,不足 8 位前面补 ‘0’
    std::cout << (QString("0%1").arg(123,8,16,QLatin1Char('0'))).toStdString().data() << std::endl;

    // 转为8进制
    std::cout << QString("0%1").arg(QString::number(100,8)).toStdString().data() << std::endl;
    std::cout << (QString("0%1").arg(QString::number(.777,'f',1))).toStdString().data() << std::endl;

    return a.exec();
}

格式化输出转换

​ 浮点数与字符串的转换可以使用sprintf()格式化,也可以使用asprintf()格式化,这两个函数的区别是,QString::sprintf是在原始字符串上操作, QString::asprintf 允许创建一个格式化的字符串,并返回一个新的 QString 对象,而不是直接在现有对象中进行修改。

#include <QCoreApplication>
#include <QString>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 格式化输出转换
    float total = 3.1415926;
    QString str_total;

    // 将浮点数转换为字符串
    str_total = str_total.sprintf("%.4f",total);
    std::cout << str_total.toStdString().data() << std::endl;

    // 将双精度浮点数转为字符串
    str_total = QString::asprintf("%2f",total);
    std::cout << str_total.toStdString().data() << std::endl;

    return a.exec();
}

在这里需要多说一下类型转换,一般StdString()可以直接使用ToUTF8()转换格式,而QByteArray也可以直接使用StdString()函数将其转换成QString格式。

#include <QCoreApplication>
#include <QString>
#include <iostream>

using namespace std;

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    // 编码之间的转换
    QString str_string = "welcome to you !";

    // 将StdString转换为UTF8格式
    QByteArray ba = str_string.toUtf8();
    std::cout << ba.toStdString().data() << std::endl;

    // 类型转换QByteArray转换QString
    QByteArray byte;

    byte.resize(2);
    byte[0]='1';
    byte[1]='2';
    QString strs = byte;
    std::cout << strs.toStdString().data() << std::endl;

    return a.exec();
}

1.5 QStringLiteral 的介绍

但是,函数只能接受QString类型的参数时,无论我们给一个字面字符串或QLatin1String,都会隐式构造一个临时的QString对象,构造这个对象需要在栈上申请一定的内存空间,然后把字符串拷贝过去,如果这样的调用比较多,那还是一笔不小的开销。此时,我们可以使用QStringLiteral来减小这个开销。

QStringLiteral其实是一个宏,从字符串常量创建QString对象的宏。

宏在编译时,从字符串文字生成QString数据,QString的内部数据将在编译时生成,在运行时不会发生任何转换或内存分配,使用QStringLiteral来代替C++中的双重数值传递将会在编译的时候显著的提升运行效率。

优点:使用QStringLiteral而不是双引号的普通C++的字符串可显著加快从编译时已知的数据创建QString示例的速度,相对来说有QLatin1String参数重载的比使用QStringLieral更高效。

posted @ 2026-02-08 15:39  GeoFXR  阅读(74)  评论(0)    收藏  举报