本篇将单独总结QString类。相比于C++ Std中的string来说,QString要好用的多。QString用来存储和处理字符串,其采用的是Unicode码,每个字符是一个16位的QChar(不是8位的char),所以QString同样可以处理中文字符,而且一个汉字算作一个字符。同时,QString应用了隐式共享以减少内存的使用,避免不必要的内存拷贝。
初始化
最简单和常见的QString初始化方法就是直接用const char *赋值:
QString str = "hello";
QString str("hello"); //str的size是5
也可以用QChar数组初始化,这实际上是从QChar到QString的一个深度拷贝(可参见拷贝构造函数):
static const QChar data[4] = { 0x0055, 0x006e, 0x10e3, 0x03a3 };
QString str(data, 4);
QString的本质也是一种容器(参见上篇笔记),这使得它也可以用list的方式获取下标并进行赋值:
QString str;
str.resize(4);
str[0] = QChar('U');
str[1] = QChar('n');
str[2] = QChar(0x10e3);
str[3] = QChar(0x03a3);
QString str;
for (int i = 0; i < str.size(); ++i) {
if (str.at(i) >= QChar('a') && str.at(i) <= QChar('f')) //用at获取值
qDebug() << "Found character in range [a-f]";
}
特别的,at()函数要比取下标符号[]更快,因此它不会进行深拷贝。可以用 left(), right(), 或者mid()函数来一次性获取多个字符,他们的速度也是很快的。这些操作实际上就是容器中列表的操作。
使用QString
QString提供了基本的函数来修改数据: append(), prepend(), insert(), replace(), and remove(). 比如:
QString str = "and";
str.prepend("rock "); // str == "rock and"
str.append(" roll"); // str == "rock and roll"
str.replace(5, 3, "&"); // str == "rock & roll"
一个常见的需求是去掉字符串中的空格,则可以用trimmed()来去掉字符串首尾的空格,用simplified()将中间连续的空格用一个空格替换,并去掉字符串首尾的空格。
QString str = " Are you OK? ", str1;
str1 = str.trimmed(); // str1 == "Are you OK? "
str1 = str. simplified (); // str1 == "Are you OK? "
如果要获取字符串中某个字符所在位置,可以用indexOf() or lastIndexOf()函数。前者是记录某字符串从前端数所在的位置,后者是从后端数所在的位置。其参数第一个待寻找的字符串,第二个是开始找的起始位置,默认为0,返回值是位置。如果找不到则返回-1.下面的代码将找到所有的<b>。
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 提供了很多函数来数字转换为字符串或将字符串转换为数字。如 arg(),setNum() ,number(), toInt(), toDouble()等等。
arg()函数用以将多个数字或字符串拼接起来,像是printf。它可以转换所有的数字类型和字符串类型。
int i; // current file's number
int total; // number of files to process
QString fileName; // current file's name
QString status = QString("Processing file %1 of %2: %3")
.arg(i).arg(total).arg(fileName);
setnum()默认以十进制的形式转换为字符串:
QString str;
str.setNum(1234); // str == "1234"
number用以转换数字到字符串,可指定进制,默认为10进制:
long a = 63;
QString s = QString::number(a, 16); // s == "3f"
toInt和toDouble函数用以将字符串转化为数字:
QString str = "FF";
bool ok;
int hex = str.toInt(&ok, 16); // hex == 255, ok == true
int dec = str.toInt(&ok, 10); // dec == 0, ok == false
toUpper() or toLower()函数将字符串中的字母进行大小写转换。
QString str = "TeXt";
str = str.toUpper(); // str == "TEXT"
str = "The Qt PROJECT";
str = str.toLower(); // str == "the qt project"
可以用split函数将字符串分割为若干个子字符串,并由QStringList保存。或者用join函数将若干字符串组合为一个字符串。
QString str = "a,,b,c";
QStringList list1 = str.split(',');
// list1: [ "a", "", "b", "c" ]
QStringList list2 = str.split(',', QString::SkipEmptyParts);
// list2: [ "a", "b", "c" ]
因为一些历史原因,判断一个QString是NULL还是Empty,是两种不同的方法:一个NULL QString 是已经用0执行了默认构造函数, Empty意思是QString的长度为0.所以,一个NULL string肯定是empty的,但一个empty string不一定是null string。
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
查询QString数据
如果想判断QString是否以某个字符串开头或者结尾的,可以用startsWith() or endsWith()函数,其返回值为bool。如果想判断字符串其中是否包含某字符串,可以用contains()函数;如果想统计该字符串出现的次数可以用count()函数。
QString str = "Bananas";
str.startsWith("Ban"); // returns true
str.startsWith("Car"); // returns false
QString str = "Peter Pan";
str.contains("peter", Qt::CaseInsensitive); // returns true
QString str = "banana and panama";
str.count(QRegExp("a[nm]a")); // returns 4
QString可以用重定义的operator<(), operator<=(), operator==(), operator>=()符号进行比较判断。这些重写的方法在执行时是非常快的。
可以通过调用data() or constData()来获取一个字符串的指针。该指针指向第一个QChar,这就像回到的最初char *。
QString str = "Hello world";
QChar *data = str.data();
while (!data->isNull()) {
qDebug() << data->unicode();
++data;
}
字符转换
在字符处理是经常遇到8位字符和Unicode之间的转换。QString 提供了三个函数来返回一个const char * :QByteArray: toUtf8(), toLatin1(), and toLocal8Bit().
- toLatin1() 返回一个 Latin-1 (ISO 8859-1) 8位编码字符串.
- toUtf8() 返回一个 UTF-8编码8-bit 字符串. UTF-8 是一个f US-ASCII (ANSI X3.4-1986) 的超级集合,提供了全部的Unicode 字符.
- toLocal8Bit() 返回一个用本地编码方式编码的8-bit 字符串.
相反,从这些8位编码字符转换为QString,则需要调用fromLatin1(), fromUtf8(), and fromLocal8Bit().
可以看到, QString 提供了大量的函数和方法使得处理 const char * strings变得简便. 但这也是把双刃剑: 如果字符串都是用US-ASCII or Latin-1编码的,则QString会更方便。但是经常会有一些const char *是8-bit encoding隐式转换而来,这样用QString处理就比较危险了。为了最小化这些风险, 你可以关闭隐式转换:
- QT_NO_CAST_FROM_ASCII 不允许C风格字符串或指针自动转换为Unicode.
- QT_RESTRICTED_CAST_FROM_ASCII 允许C风格字符串自动转换为Unicode,但不允许指针自动转换。.
- QT_NO_CAST_TO_ASCII 不允许QString自动转换为C风格字符串。
具体使用可以在pro文件中添加全局声明:
DEFINES += QT_NO_CAST_FROM_ASCII \
QT_NO_CAST_TO_ASCII
效率问题
最后来分析下QString的效率问题。首先是QStringLiteral的使用。QStringLiteral不是类名,而是一个宏定义。它的用法是QStringLiteral("hello").它到底有什么用呢?通常,我们用通常用QString("hello")构造QString,在运行时构造函数内需要分配内存,耗费时间。而 QStringLiteral("hello")的用法是在编译阶段就将hello字符串生成QString,成为静态数据。这样在运行阶段就不需要分配内存,在传参数时也不用深度拷贝了。所以,在使用时,对于const char* (不会修改的字符串)可以用QStringLiteral来修饰它以提升效率。
另一个问题是“+”的问题,采用“+”来拼接多个字符串很常见,但是实际上这意味着更多的内存分配,当连接n个字符串,则会有n-1次内存分配,这无疑会增加运行时间。从Qt4.6开始,内部增加了一个QStringBuilder模板类。QStringBuilder使用表达式模板重新解释“%”,当我们用“%”替换“+”时,就会待最终的整个字符串被确定,一次性分配内存空间,然后把字符串一个个拷贝进来。更方便的是,我们不必要把所有的“+”都用“%”替代,Qt提供了全局宏定义以使得所有的“+”都会被以“%”替代处理:
DEFINES += QT_USE_QSTRINGBUILDER
所以,我们可以在pro文件中声明这个宏,这将会大大提高QString的“+”操作效率。