Qt扫盲-QString使用总结


这里是我对帮助文档的一些理解,再复习复习一下之前的知识点。
我把文字分成了两部分,一半是给喜欢了解一下原理的,一部分是直接上手就行的。

一、概述

QString 是Qt 的一种基础数据类型,也是对字符串的一个封装。其实他的操作和标准库的std::string字符串的基本是一致的。但是还是有些区别。

QString 存储以数据长度为16位2字节 QChar类型的字符串,也是说每个字符类型是 QChar ,其中每个 QChar 对应于一个UTF-16 代码。(如果代码值高于 65535 的 Unicode 字符使用两个连续的 QChar来存储)这其实和 c++标准库的std::string使用一致,但是std::string的字符是char类型,8位一字节长度(用的Utf-8)。这里我们知道QString 和 std::string编码格式不一样,不能直接相互操作,得转码后才能用哦

Unicode 是一种国际标准,支持当今使用的大多数书写系统。是一种可变长度字符编码。它可以用来表示Unicode标准中的任何字符。会将一个码位编码为 1 到 4 个字节。它是 US-ASCII (ANSI X3.4-1986) 和 Latin-1 (ISO 8859-1) 的超集,所有 US-ASCII/Latin-1 字符都位于相同的代码位置。也就是说Unicode包括了 上述位置的编码

QString在内部实现的时候 使用隐式共享(写入时复制)来减少内存使用并避免不必要的数据复制。这也有助于减少存储 16 位字符而不是 8 位字符的固有开销(QChar 比 char 空间大)。
隐式共享可以参考我之前的博文: Qt扫盲–隐式共享基本原理

除了QString,Qt还提供了QByteArray类来存储原始字节和传统的8位“0”终止字符串(也即是char类型字符串)。在大多数情况下,QString 是您想要使用的类。它在整个Qt API中使用,Unicode支持确保您的应用程序易于翻译,如果您想在某个时候扩展应用程序市场。QByteArray 适合的两种主要情况是需要存储原始二进制数据时,以及内存保护至关重要时(在嵌入式系统中)。

二、初始化字符串

1、极速版

    //不带参构造
    QString str;
    
    //从const char * 字面量字符串构造
    QString str_1 = "dfdsfsd";
    
    //拷贝
    QString str_2 = str_1;

    //带参构造
    QString str_3();

    //从const char * 字面量字符串构造
    QString str_4("fdfsfs");
    
    //从QChar 数组构造
    static const QChar data[4] = { 'u', 'y', 0x10e3, 'd' };
    QString str_5(data, 4);

    //从QCahr构造
    QString str_6(QChar('&'));

    qDebug()<<str<<str_1<<str_2<<str_3<<str_4<<str_5<<str_6;

// 输出
// "" "dfdsfsd" "dfdsfsd" true "fdfsfs" "uy?d" "&"

2、原理版

初始化字符串有些方式?
当然看构造函数,QString 是类,那肯定就是构造函数初始化啦
在这里插入图片描述
帮助文档的推荐的三种写法

    1. 用常量字符串初始化 const char *
QString str = "Hello";
QString str1 = QString::fromUtf8("Hello"); //显然一般情况不用,第一种更简单

我们把 const char * 转成 QString 就可以用 fromUtf8() 方法。在所有采用 const char * 参数的 QString 函数中,const char * 被解释为以 UTF-8 编码的经典 C 样式“\0”结尾字符串。const char * 参数为 nullptr(空) 是合法的。但是尽量别用空指针。

    1. QChar 数组初始化
static const QChar data[4] = { 0x0055, 0x006e, 0x10e3, 0x03a3 };
QString str(data, 4);

QString 会用 QChar 数组的深拷贝,也就是说str和data没有共享内存(如果出于性能原因,您不想创建字符数据的深拷贝,请改用 QString::fromRawData() )

    1. 先分配再改值
QString str;
str.resize(4);

str[0] = QChar('U');
str[1] = QChar('n');
str[2] = QChar(0x10e3);
str[3] = QChar(0x03a3);

这种方法是使用 resize() 设置字符串的大小,并初始化每个字符的数据字符。QString 使用从 0 开始的索引,就像C++数组一样。要访问特定索引位置的字符,可以使用运算符 [ ] 在非常量字符串上。

三、操作字符串

1、极速版

1. 增加

对字符串的进行增操作,主要有下面这些

QString str = "and";
str.prepend("rock ");     	// str == "rock and"
str.append(" roll");        // str == "rock and roll"
str += " dsfsd";				// str == "rock and roll dsfsd"
str = str + QString(" fdsffsd") // str == "rock and roll dsfsd" fdsffsd

2. 删除

remove 函数还有好几种,支持正则表达式移除,下面是常用的几种

QString str = "AAA BBB CCC DDD AABBCC";
str = str.remove("AA");     	// str == "A BBB CCC DDD BBCC"
str = str.remove(4,4);			// str == "A BBC DDD BBCC"
str.clear();					// str == ""
str = "ccs";
str = "";						//重置为空字符串
str.resize(0)					// str == "" 重置长度为0的字符串

//去除首尾所有空白字符,空白字符包括'\t', '\n', '\v', '\f', '\r', ' '.
QString str = "  lots\t of\nwhitespace\r\n ";
str = str.trimmed();
// str == "lots\t of\nwhitespace"

//去除 多余空白字符,首尾全部去掉,每个序列的内部空格多余空白符替换为一个空格号。
QString str = "  lots\t of\nwhitespace\r\n ";
str = str.simplified();
// str == "lots of whitespace";

3. 修改

常用的就是替换,也是支持正则匹配替换的

QString str = "AAA BBB CCC DDD AABBCC";

//replace 方法
str = str.replace("AA", "哈哈哈");     	// str == "哈哈哈A BBB CCC DDD 哈哈哈BBCC"
str = str.replace('B','~');			// str == "哈哈哈A ~~~ CCC DDD 哈哈哈~~CC"

//索引修改
str[2] = '+'				// str = "哈哈+A ~~~ CCC DDD 哈哈哈~~CC"

//大小写转换
QString str = "TeXt";
str = str.toUpper();        // str == "TEXT"
str = str.toLower();		// str == "text"

4. 插入

QString str = "Meal";
str.insert(1, QString("ontr"));
// str == "Montreal"

QString str;
str = "%1 %2";

str.arg("%1f", "Hello");        // returns "%1f Hello"
str.arg("%1f").arg("Hello");    // returns "Hellof %2"

str = "%1%2%3";
str.arg("Hello").arg(50).arg(20); // returns "Hello5020"

5. 转换

//数字转字符串
QString str;
str.setNum(1234);       // str == "1234"

long a = 63;
QString s = QString::number(a, 16);             // s == "3f"
QString t = QString::number(a, 16).toUpper();     // t == "3F"

//字符串转数字格式
QString str = "1234.56";
double val = str.toDouble();   // val == 1234.56

bool ok;
double d;

d = QString( "1234.56e-02" ).toDouble(&ok); // ok == true, d == 12.3456
d = QString( "1234.56e-02 Volt" ).toDouble(&ok); // ok == false, d == 0

2、原理版

如果你事先知道一个 QString 将包含多少个字符,你可以调用 reserve() 函数来预先分配一定量的内存。我们可以也可以调用 capacity() 来找出 QString 实际分配了多少内存。

replace()remove() 函数的前两个参数是开始擦除的位置和应该擦除的字符数。如果要将特定子字符串的所有匹配项替换为另一个子字符串,请使用双参数 replace() 的重载函数。

如果要删除 QString 两端的空格,请使用 trimmed() 函数。
如果要删除两端的空格,并将字符串中的多个连续空格替换为单个空格字符,请使用 simplified()

如果要查找 QString 中特定字符或子字符串的所有匹配项,请使用 indexOf()lastIndexOf() 函数。前者从给定的索引位置开始向前搜索,后者向后搜索。如果找到字符或子字符串,两者都返回它的索引位置;否则,它们返回 -1。例如,下面是一个典型的循环,用于查找特定子字符串的所有匹配项:

  QString str = "We must be <b>bold</b>, very <b>bold</b>";
  int j = 0;

  while ((j = str.indexOf("<b>", j)) != -1) {
      qDebug() << "Found <b> tag at index position" << j;
      ++j;
  }

QString 提供了许多函数,用于将数字转换为字符串和字符串转换为数字。setNum() 函数、number() 静态函数和 toInt()toDouble() 和类似函数toDouble() 这类转换函数还有校验是否检验成功的功能

要获取字符串的大写或小写形式,请使用 toUpper()toLower()

我们可以使用 split() 函数将字符串拆分为字符串列表 QStringList 形式,这个split函数还可以选择是否过滤空字符串之类的。并使用 QStringList::join() 将字符串列表连接到带有可选分隔符的单个字符串中。可以使用 QStringList::filter() 函数从包含特定子字符串或与特定 QRegExp 匹配的字符串列表中获取字符串列表。

  QString str = "a,,b,c";

  QStringList list1 = str.split(',');
  // list1: [ "a", "", "b", "c" ]

  QStringList list2 = str.split(',', QString::SkipEmptyParts);
  // list2: [ "a", "b", "c" ]

  QStringList list;
  list << "Bill Murray" << "John Doe" << "Bill Clinton";

  QStringList result;
  result = list.filter("Bill");
  // result: ["Bill Murray", "Bill Clinton"]

四、查询字符串

对于只是读取字符串的话,用 at() 方法 比 [] 更快,因为 [] 是深拷贝,at只是浅拷贝或者,使用 left()、right() 或 mid() 函数一次提取多个字符。

QString str;

for (int i = 0; i < str.size(); ++i) 
{
	if (str.at(i) >= QChar('a') && str.at(i) <= QChar('f'))
		qDebug() << "Found character in range [a-f]";
}

  QString x = "Pineapple";
  QString y = x.left(4);      // y == "Pine"
  
  QString x = "Pineapple";
  QString y = x.right(5);      // y == "apple"

  QString x = "Nine pineapples";
  QString y = x.mid(5, 4);            // y == "pine"
  QString z = x.mid(5);               // z == "pineapples"

QString 可以嵌入“0”字符 (QChar::Null)。size() 函数始终返回整个字符串的大小,包括嵌入的“0”字符。
调用 resize() 函数后,新分配的字符具有未定义的值。要将字符串中的所有字符设置为特定值,请使用 fill() 函数。
QString 提供了数十种重载,旨在简化字符串的使用。例如,如果要将 QString 与字符串文本进行比较,则可以编写如下代码,它将按预期工作:

QString str;

if (str == "auto" || str == "extern"
	|| str == "static" || str == "register")
{
	// ...
}

QString str = "World";
int n = str.size();         // n == 5
str.data()[0];              // returns 'W'
str.data()[4];              // returns 'd'
  • startsWith()endsWith() 查看 字符串是不是以特定的子字符串开始还是结束。
  • contains() 函数来检查 字符串 是否包含特定字符或子字符串。用 count()来统计特定字符或子字符串在字符串中出现的次数。
  • 可以使用重载运算符(如运算符 < 、<= 、== 、>= 等来比较 字符串大小。请注意,比较基于字符的数字 Unicode 值大小。它非常快,但不是人类所期望的。
  • QString::localeAwareCompare() 静态函数来 排序用户界面字符串 其实就是本地字符串比较。
  • data()constData()。这些函数返回指向 QChar 数据开头的指针。但是指针是保证仍然有效,除非非 const QString函数被调用。这个就失效了。
QString str = "Bananas";
str.startsWith("Ban");     // returns true
str.startsWith("Car");     // returns false

五、字符串格式转换

QString 提供了以下三个函数,这些函数将字符串的 const char * 版本作为 QByteArray:toUtf8()toLatin1()toLocal8Bit()

  • toLatin1() 返回一个拉丁语-1 (ISO 8859-1) 编码的 8 位字符串。
  • toUtf8() 返回一个 UTF-8 编码的 8 位字符串。UTF-8 是 US-ASCII (ANSI X3.4-1986) 的超集,它通过多字节序列支持整个 Unicode 字符集。
  • toLocal8Bit() 使用系统的本地编码返回一个 8 位字符串。

要从这些编码之一进行转换,QString 提供了 fromLatin1()、fromUtf8() 和 fromLocal8Bit()。其他编码通过 QTextCodec 类受支持。

如上所述,QString 提供了许多函数和运算符,使得与 const char * 字符串的互操作变得容易。但此功能是一把双刃剑:如果所有字符串都是 US-ASCII 或 Latin-1,它会使 QString 更方便使用,但始终存在使用错误的 8 位编码完成从 const char * 到const char * 的隐式转换的风险。若要最大程度地降低这些风险,可以通过定义以下两个预处理器符号来关闭这些隐式转换:

  • QT_NO_CAST_FROM_ASCII:禁用从 C 字符串文本和指针到 Unicode 的自动转换。
  • QT_RESTRICTED_CAST_FROM_ASCII: 允许从 C 字符和字符数组自动转换,但禁用从字符指针到 Unicode 的自动转换。
  • QT_NO_CAST_TO_ASCII:禁用从 QString 到 C 字符串的自动转换。

为应用程序全局定义这些预处理器符号的一种方法是将以下条目添加到 qmake 项目文件中:

DEFINES += QT_NO_CAST_FROM_ASCII \
             QT_NO_CAST_TO_ASCII

然后,您需要显式调用 fromUtf8()、fromLatin1() 或 fromLocal8Bit() 来从 8 位字符串构造 QString,或使用轻量级 QLatin1String 类,例如:

QString url = QLatin1String(“http://www.unicode.org/”);

同样,您必须显式调用 toLatin1()、toUtf8() 或 toLocal8Bit() 才能将 QString 转换为 8 位字符串。(其他编码通过 QText编解码器类受支持。

六、Null 字符串和 Empty 字符串的区别

由于历史原因,QString区分一个空字符串和一个空字符串。一个空字符串的字符串初始化使用QString默认的构造函数或通过构造函数(const char *) 0。一个空字符串是任何字符串大小为0。 一个空字符串总是空的,但一个空字符串不一定是零:

  QString().isNull();               // returns true
  QString().isEmpty();              // returns true

  QString("").isNull();             // returns false
  QString("").isEmpty();            // returns true

  QString("abc").isNull();          // returns false
  QString("abc").isEmpty();         // returns false

所有的功能除了 isNull() 把null字符串一样空字符串。例如, toUtf8 () 、constData() 返回一个有效的指针(不是nullptr) ’ \ 0 '字符为空字符串。建议始终使用 isEmpty() 函数,避免 isNull ()

七、字符串参数格式化

在QString的成员函数里面,有很多需要字符串参数格式化 比如(arg(), number())函数,其实是就是把这些数字转字符串,有科学计数法的形式和浮点数那种形式。

格式化含义
eformat as [-]9.9e[±]999
Eformat as [-]9.9E[±]999
f把数字±9.9 转换成 ±9.9
guse e or f format, 看哪种更简洁
Guse E or f format, 看哪种更简洁

精度也是指定的参数格式。的“e”、“e”和“f”格式,精度表示小数点后的位数数。‘g’和‘g’格式、精度表示有效数字的最大数量。其实就是科学计数法那种形式。

八、更高效的字符串构造

许多字符串在编译时是已知的。但是琐碎的构造函数 QString(“Hello”)将复制字符串的内容,将内容视为 Latin-1。为了避免这种情况,可以使用QStringLiteral宏在编译时直接创建所需的数据。从文本构造 QString 不会在运行时产生任何开销。
效率稍低的方法是使用 QLatin1String。此类包装 C 字符串文本,在编译时预先计算其长度,然后可用于与 QString 进行比较并转换为 QString,而不是常规 C 字符串文本。使用 QString ‘+’ 运算符,可以轻松地从多个子字符串构造复杂的字符串。
你经常会写这样的代码:

QString foo;
QString type = "long";

foo->setText(QLatin1String("A") + type + QLatin1String("B"));

if (foo.startsWith("(" + type + ") dd"))
          ...

这两种字符串结构都没有错,但存在一些隐藏的低效率。从Qt 4.6开始,您可以消除它们。
首先,多次使用“+”运算符通常意味着多个内存分配。连接 n 个子字符串(其中 n > 2)时,可以有多达 n - 1 次对内存分配器的调用。
在Qt 4.6 中,添加了内部模板类 QStringBuilder 以及一些帮助程序函数。此类标记为内部,不会出现在文档中,因为您不应该在代码中实例化它。它的使用将是自动的,如下所述。该类可以在src/corelib/tools/qstringbuilder中找到.cpp如果你想看看它的话。QStringBuilder 使用表达式模板并重新实现“%”运算符,以便当您使用“%”而不是“+”进行字符串串联时,多个子字符串连接将被推迟,直到最终结果即将分配给 QString。此时,已知最终结果所需的内存量。然后调用一次内存分配器以获取所需的空间,并将子字符串逐个复制到其中。通过内联和减少引用计数来获得额外的效率(从 QStringBuilder 创建的 QString 通常具有 ref 计数 1,而 QString::append() 需要额外的测试)。有两种方法可以访问这种改进的字符串构造方法。简单的方法是在您想要使用它的任何位置包含 QStringBuilder,并在连接字符串时使用 ‘%’ 运算符而不是 ‘+’:就是推荐下面这种写法,用 % 代替 +

 #include <QStringBuilder>

QString hello("hello");
QStringRef el(&hello, 2, 3);
QLatin1String world("world");
QString message =  hello % el % world % QChar('!');

一种更全局的方法,也是最方便但不完全兼容源代码的方法,是在 .pro 文件中定义:

DEFINES *= QT_USE_QSTRINGBUILDER

并且“+”将在任何地方自动执行为QStringBuilder 的 “%”。

九、最大大小和出现内存不足的情况

这个了解了解就好。

QString 的当前版本的大小限制为略低于 2 GB(231 字节)。确切的值取决于体系结构,因为它取决于管理数据块所需的开销,但不超过 32 个字节。原始数据块还受到当前版本中使用 int 类型的限制为 2 GB 减去 1 字节。由于 QString 每个字符使用两个字节,因此在一个 QString 中转换为不到 2^30 个字符。
如果内存分配失败,QString 将抛出 std::bad_alloc 异常。Qt容器中的内存不足情况是Qt抛出异常的唯一情况。
请注意,操作系统可能会对持有大量已分配内存的应用程序施加进一步限制,尤其是大型连续块。此类注意事项、此类行为的配置或任何缓解措施都不在Qt API的范围之内。