求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Modeler   Code  
会员   
要资料
 
追随技术信仰

随时听讲座
每天看新闻
 
 
Qt 编程指南
第1章 Qt 开发环境
1.1 Qt 介绍
1.2 Qt 下载
1.3 Qt 在 Windows 下安装
1.4 Qt 在 Linux 下安装
1.5 认识开发工具
1.6 常见的名词术语
第2章 从Hello World开始
2.1 Hello World
2.2 Hello Qt
2.3 Hello Designer
2.4 Hello Creator
2.5 Qt程序调试
2.6 Qt帮助文档
第3章 字符串和字符编码
3.1 字符编码方式
3.2 Qt 程序字符编码
3.3 使用 QString
3.4 使用 QByteArray
第4章 信号和槽函数
4.1 元对象系统
4.2 使用原有的信号和槽
4.3 自定义信号和槽
4.4 系统属性
4.5 扩展阅读:ui_*.h代码
4.6 扩展阅读:moc_*.cpp代码
第5章 简单控件的使用
5.1 按钮类的控件
5.2 单行编辑控件
5.3 丰富文本编辑控件
5.4 其他输入控件
5.5 显示类的控件
5.6 Qt 资源文件
第6章 控件布局
6.1 传统窗口调整技术
6.2 水平和垂直布局器
6.3 网格布局器
6.4 表单布局器
6.5 控件尺寸调整策略
6.6 分裂器
第7章 文件和数据流
7.1 文件系统概览
7.2 基本文件读写QFile
 

 
目录
使用 QString
59 次浏览
4次  

本节介绍 QString 的常见使用,包含 C++ 基本类型和 QString 的互相转换、QString 涉及的运算符、QString 子串查询和操作、利用 QTextStream 对 QString 做输入输出操作等,最后通过一个示例集成测试函数,展示 QString 用法。本节内容较多,可分几次尝试代码,凡是原理性质的内容需要理解,而罗列性质的内容不用死记的,可以到用的时候查看文档。本节示例代码下载地址在最后的小节 3.3.6 里,可以先下载示例对照学习。

1. QString和QChar简介

QString 是由一系列 16 bit 字符 QChar 组成的字符串,以 NULL 字符结尾(末尾的 NULL 不计入字符串长度)。QChar 是一个 Unicode 4.0 标准的字符,对于超过 16bit 范围的国际码字符,QString 里采用相邻的一对 QChar 来表示。QString 使用的其实是 UTF-16 的双字节编码,tr 函数就是将 UTF-8 变长编码的字符串转成 QString 运行时的内码。UTF-8 编码是属于通用的存储交换格式,但这种编码的缺点就是一个字符的长度不固定,这对字符串操作效率是有影响的,因为得先确定每个字符的长度。因此 QString 采用固定长度字符单元的 UTF-16 编码,这对程序运行时字符串比较、查询操作效率更高。上一节 3.2.4 表格中 utf16() 和 unicode() 函数都没有用 to 前缀,因为这两个函数没有做转换,它们返回的就是 QString 运行时的内码,同 data() 函数。tr 函数不仅可以用于支持国际化翻译,并且能自动将字符串的存储交换格式 UTF-8 转换成运行时的 UTF-16 内码,返回转换过后得到的 QString 对象。

字符串之间经常有手动复制或者通过函数参数、函数返回值等复制操作,QString 为了优化内存使用效率,避免出现大量相同内容的字符串副本,QString 对复制过程采用隐式共享机制(implicit sharing),比如执行字符串对象 str1 = str2 时,如果这两个对象字符串内容都没有后续改变,那么它们会指向同一块字符串数据,而如果其中之一发生改变,字符串数据块的复制过程才会发生,这样能最大程度地节省内存,而 且在传 QString 类型参数或返回值时,避免了大量数据块的复制过程,优化了程序运行效率。

QString 内码是 UTF-16,而标准 C++ 的字符串是 UTF-8 编码的,Qt 针对标准 C++ 字符串也提供了 QByteArray 类,用于操作 UTF-8 编码以及其他本地化字符串(如 GBK、Big5)、字节数组(不以 NULL 结尾的纯数据)等,QByteArray 类下一节讲解。

2. 基本类型与字符串互相转换

在编程时经常会出现把数值如 800 转成字符串 "800",或者反过来把字符串转成数值等情况,本小节罗列 C++ 基本的数值类型和 Qt 对这些类型的别称,然后展示这些基本类型和 QString 对象的互相转换,并编写一些测试函数来示范效果。

基本类型 Qt别称 转入函数 转出函数 描述
short qint16 arg或setNum toShort 2 字节长度,有符号短整型。
unsigned short ushort、quint16 arg或setNum toUShort 2 字节长度,无符号短整型。
int qint32 arg或setNum toInt 4 字节长度,有符号整型。
unsigned int uint、quint32 arg或setNum toUInt 4 字节长度,无符号整型。
long arg或setNum toLong 有符号长整型,对于 32 位编程 long 是 4 字节长度,对于 64 位编程是 8 字节长度。
unsigned long ulong arg或setNum toULong 无符号长整型,对于 32 位编程 unsigned long 是 4 字节长度,对于 64 位编程是 8 字节长度。
long long qlonglong、qint64 arg或setNum toLongLong 8 字节长度,有符号长长整型。
unsigned long long qulonglong、quint64 arg或setNum toULongLong 8 字节长度,无符号长长整型。
float 默认情况下无 arg或setNum toFloat 4 字节长度,单精度浮点数。
double 默认情况对应 qreal arg或setNum toDouble 8 字节长度,双精度浮点数。

这些基本的数值类型转为 QString 对象都是使用重载的 arg 或 setNum 函数,而 QString 对象转出为其他类型使用单独命名的函数。Qt 对这些类型的别称都定义在头文件 <QtGlobal> 里面,由于其他绝大多数 Qt 头文件都包含了该全局头文件,所以通常不需要自己手动去包含它的。对于上表需要说明的两点:一是 long 和 ulong 长度是根据操作系统和编译器确定的,32 位编程就是 32 位,64 位编程就是 64 位;二是实数 qreal 默认情况下都是对应 double ,例外情况是在编译 Qt 类库本身时配置了 -qreal float 选项参数,这种例外情况极少,通常都不用管的。

首先来介绍一下转入函数,对于整数类型,setNum 函数声明是完全类似的,以 int 为例:

QString & setNum(int n, int base = 10)

第一个参数就是需要转换的整数,第二个参数是转换之后的目标字符串进制基数,比如转成十六进制字符串、八进制字符串等,默认是转成十进制的字符串。setNum 函数设置好字符串内容后返回 QString 对象自身的引用。

对于浮点数类型,setNum 函数声明有些区别,以 double 为例:

QString & QString::​setNum(double n, char format = 'g', int precision = 6)

第一个参数是需要转换的浮点数,第二个是转换之后的目标字符串格式('e', 'E', 'f', 'g' , 'G'),第三个是目标字符串显示的浮点数精度,默认是 6 。浮点数的格式是与 C 语言类似的,如下所述:

  • 'e':科学计数法,小写 e,如 [-]9.9e[±]999。
  • 'E':科学计数法,大写 E,如 [-]9.9E[±]999。
  • 'f':定点数显示,[-]9.9。
  • 'g':自动选择用科学计数法或定点数显示,哪种方式最简洁就用哪个,科学计数法的 e 小写。
  • 'G':自动选择用科学计数法或定点数显示,哪种方式最简洁就用哪个,科学计数法的 E 大写。

setNum 函数示范代码:

void Test_setNum()
{
QString strTest;
//to Hex string
short numHex = 127;
strTest.setNum(numHex, 16);
qDebug()<<"Hex: "<<strTest;
//to Oct string int numOct = 63; strTest.setNum(numOct, 8); qDebug()<<"Oct: "<<strTest;
//to normal Dec string long numDec = 800; strTest.setNum(numDec); qDebug()<<"Normal: "<<strTest;

//to float string float numFixed = 123.78999; strTest.setNum(numFixed, 'f', 3); qDebug()<<"Fixed: "<<strTest;

//to scientific double string double numScientific = 456.78999; strTest.setNum(numScientific, 'e', 6); qDebug()<<"Scientific: "<<strTest;
}

这个测试函数运行结果就不贴出来了,读者自己手动去试试看。

接下来重点介绍 arg 函数,这是最常用也是最具特色的。arg 函数无所不包,它的参数可以是数值类型、字符串类型,并且可以串联,格式化参数里还可以指定顺序、重复使用参数等等。对于数值类型,它的声明与 setNum 比较类似,以 int 和 double 为例:

QString arg(int a, int fieldWidth = 0, int base = 10, QChar fillChar = QLatin1Char( ' ' )) const

QString arg(double a, int fieldWidth = 0, char format = 'g', int precision = -1,
QChar fillChar = QLatin1Char( ' ' )) const

注意 arg 函数声明末尾的 const,这个函数不会改变字符串对象本身的内容,而是会返回一个全新的 QString 对象,所以使用这个函数时,必须用它的返回值。

对于整数类型,它的声明多出来两个:fieldWidth 是指生成的目标字符串宽度,0 表示自动设置长度,最后的 fillChar 是填充字符,如果设置的域宽比较大,多余的空位就会使用这个填充字符填满。

对于浮点数类型,多出来的 fieldWidth 也是生成的目标字符串宽度,fillChar 也是填充字符。默认的填充字符是空格,QLatin1Char 代表一个字节长度的拉丁字符,与 ASCII 码字符差不多。QLatin1Char 有对应的类 QLatin1String,因为仅支持单字节拉丁字符,不支持国际化,它应用的比较少。

arg 函数比 setNum 函数功能更强大,可以设置目标字符串宽度和填充字符。arg 函数还可以用字符串作为参数,可以将一个字符串填充到另一个里面,比如下面这个函数声明:

QString arg(const QString & a, int fieldWidth = 0, QChar fillChar = QLatin1Char( ' ' )) const

这个声明和数值类型声明差不多,也可以设置目标字符串宽度和填充字符。

函数声明介绍到这,下面看看这个函数该怎么用。arg 函数的使用方式很特别,它的串联方式也很灵活,来看看示例代码:

void Test_arg()
{
//使用 strResult 存储 arg 返回的新对象
QString strResult; //Dec
long numDec = 800; QString strMod = QObject::tr("Normal: %1"); strResult = strMod.arg(numDec); //%1是占位符,第一个arg函数参数变量转后的字符串填充到 %1 位置 qDebug()<<"Mod: "<<strMod<<" \t Result: "<<strResult; //Oct int numOct = 63; strResult = QObject::tr("Oct: %1").arg(numOct, 4, 8, QChar('0')); //numOct转换后为4字符域宽,8进制,填充0 qDebug()<<strResult;
//Hex short numHex = 127; QString strPrefix = QObject::tr("0x"); //占位符里可填充数值转的字符串,也可以直接填充原有的字符串 strResult = QObject::tr("Hex: %1%2").arg(strPrefix).arg(numHex, 0, 16); //串联:第一个arg函数参数填充到%1,第二个arg填充到%2 qDebug()<<strResult;
//double double numReal = 123.78999; strResult = QObject::tr("Fixed: %1 \t Scientific: %2").arg(numReal, 0, 'f').arg(numReal, 0, 'e', 3); qDebug()<<strResult;
//占位符可重复,也可乱序 int one = 1; int two = 2; int three = 3; strResult = QObject::tr("%1 小于 %2,%1 小于 %3,%3 大于 %2 。").arg(one).arg(two).arg(three); qDebug()<<strResult;
}

上面都是通过 tr 函数封装了一个临时的 QString 对象,然后调用该临时对象的 arg 函数实现数值类型转成格式化字符串,填充到占位符里面。这个工作原理与 sprintf 等 C 语言函数类似,sprintf 函数使用 %n 、%s 之类的格式占位符,QString 的实现方式不一样,它使用 % 加数字的占位方式,%1 对应后面串联的第一个 arg 函数,%2 对应后面串联的第二个 arg 函数,以此类推。具体的 %1 或 %2 等替换后的格式,由对应的 arg 函数来决定,QString 里有非常多的重载 arg 函数,每个 arg 函数对应一个类型,因此 %1 既可以填充数值类型转化后的格式化字符串,也可以填充其他原有的字符串。下面逐个解释一下各个 arg 函数意义:

long numDec = 800;
QString strMod = QObject::tr("Normal: %1");
strResult = strMod.arg(numDec); //%1是占位符,第一个arg函数参数变量转后的字符串填充到 %1 位置
qDebug()<<"Mod: "<<strMod<<" \t Result: "<<strResult;

这是最简单的形式,tr函数生成的 strMod 对象里面只有一个占位符 %1 ,arg 函数会将整数 numDec 转成十进制数字符串,然后根据 strMod 构造一个新的字符串对象,并将十进制数字符串填充到占位符 %1 位置。原本的 strMod 不会改变,arg 函数会返回全新的字符串对象,然后复制给了 strResult。qDebug 打印的结果就是:

int numOct = 63;strResult = QObject::tr("Oct: %1").arg(numOct, 4, 8, QChar('0'));  //numOct转换后为4字符域宽,8进制,填充0
qDebug()<<strResult;

这里 arg 函数是将普通数字 63 用八进制数来显示,要转换的数值是 numOct,设置 numOct 转换后的子串至少 4 字符宽度,用八进制显示,空位用字符 '0' 填充。qDebug 打印的结果就是:

short numHex = 127;
QString strPrefix = QObject::tr("0x");
//占位符里可填充数值转的字符串,也可以直接填充原有的字符串
strResult = QObject::tr("Hex: %1%2").arg(strPrefix).arg(numHex, 0, 16); //串联:第一个arg函数参数填充到%1,第二个arg填充到%2
qDebug()<<strResult;

这里使用了串联的两个 arg 函数,第一个 arg 函数是填充原有字符串 strPrefix 到 %1 位置,第二个 arg 函数填充 numHex 转换后的十六进制字符串到 %2 位置。第二个 arg 函数参数里的 0 是指不限制域宽,转换后的十六进制字符串该多长就多长,参数 16 是十六进制的意思。占位符本身是没有格式信息的,填充的具体内容由后面串联的 arg 函数决定,想填充原有字符串就填充原有的字符串,想填充转换后的数字字符串,那就填充数字字符串,非常方便。qDebug 打印的结果为:

double numReal = 123.78999;
strResult = QObject::tr("Fixed: %1 \t Scientific: %2").arg(numReal, 0, 'f').arg(numReal, 0, 'e', 3);
qDebug()<<strResult;

这里展示的是浮点数转成字符串,第一个 arg 函数将 numReal 以定点数形式('f')转成字符串,0 代表不限制宽度,并填充到 %1 位置,没有设 置显示精度(默认为 6 位)。第二个 arg 函数将 numReal 以科学计数法形式('e')转成字符串,0 代表不限制宽度,3 代表显示精度为 3 位。qDebug 打印的结果为:

int one = 1; int two = 2;
int three = 3;
strResult = QObject::tr("%1 小于 %2,%1 小于 %3,%3 大于 %2 。").arg(one).arg(two).arg(three);
qDebug()<<strResult;

最后一段示例比较有意思,如果是 C 语言的 sprintf 要填充 6 个整型数,那必须用 6 个 %n ,不管有没有重复的。这里仅仅用了 %1、%2、%3,后面对应三个 arg 函数,每个 arg 函数都将参数里的变量转成数字字符串,并填充到正确的位置,而且可以重复填充。占位符的顺序也可以是乱的,规律就是第一个 arg 函数填充所有的 %1 ,第二个 arg 函数填充所有的 %2 ,第三个 arg 函数填充所有的 %3 ,以此类推。因此 qDebug 打印的结果就是:

"1 小于 2,1 小于 3,3 大于 2 。"

这正是我们希望看到的结果,可见 arg 函数的灵活性是传统 C 语言 sprintf 等无法比拟的,而且也更安全。学会 arg 函数用法,可应对各种复杂的格式化字符串转换。

接下来简单看看 QString 的转出函数,可以将数字字符串转成各种类型的数值变量。对于整数类型,它们的函数声明都是类似的,以 int 为例:

int QString::​toInt(bool * ok = 0, int base = 10) const

函数第一个参数 ok 接收一个 bool 变量的指针,用于反馈转换过程是否成功,第二个参数 base 是字符串对象里数字的进制基数,默认的 10 代表十进制,也可以设置二进制、八进制和十六进制等等。如果将 base 设置为 0,那么 toInt 函数将自动识别字符串对象里面的进制标识,对于 "0" 打头的自动按八进制转换,对于 "0x" 打头的自动按十六进制转换,其他情况都按十进制转换。

如果转换出错,ok 指向的变量会设置为 false,返回值为 0 。

对于浮点数字符串的转换,函数声明有些差异:

double QString::​toDouble(bool * ok = 0) const

这个不能指定进制基数,都是十进制的,支持定点数字符串和浮点数字符串转成数值。参数 ok 接收一个 bool 变量的指针,用于反馈转换过程是否成功。

如果转换失败,ok 指向的变量会设置为 false,返回值为 0。

下面示范 QString 对象的转出函数:

void Test_toValue()
{
bool bok = false;
//dec
QString strDec = QObject::tr("800");
int nDec = strDec.toInt(&bok, 10);
qDebug()<<nDec<<"\t"<<bok; //成功
//Hex
QString strHex = QObject::tr("FFFF");
nDec = strHex.toInt(&bok, 10); //基数错误,转换失败
qDebug()<<nDec<<"\t"<<bok;
short nHexShort = strHex.toShort(&bok, 16); qDebug()<<nHexShort<<"\t"<<bok; //FFFF正整数太大,超出范围,转换失败,没有负号 - 的都算正数。
ushort nHexUShort = strHex.toUShort(&bok, 16); qDebug()<<nHexUShort<<"\t"<<bok;//成功 //自动转换
QString strOct = QObject::tr("0077");
int nOct = strOct.toInt(&bok, 0);
qDebug()<<nOct<<"\t"<<bok; //字符 0 打头自动按八进制转
QString strHexWithPre = QObject::tr("0xFFFF");
int nHexWithPre = strHexWithPre.toInt(&bok, 0);
qDebug()<<nHexWithPre<<"\t"<<bok; //字符 0x 打头自动按十六进制转
int nDecAuto = strDec.toInt(&bok, 0); //"800" ,自动按十进制
qDebug()<<nDecAuto<<"\t"<<bok;
//浮点数转换
QString strFixed = QObject::tr("123.78999"); double dblFixed = strFixed.toDouble(&bok); qDebug()<<fixed<<dblFixed<<"\t"<<bok; //科学计数法 QString strScientific = QObject::tr("1.238e-5"); double dblScientific = strScientific.toDouble(&bok); qDebug()<<scientific<<dblScientific<<"\t"<<bok;
}

上面代码的运行结果这里不贴出来了,读者自己动手去试试。对于两个浮点数打印的行,里面带有流操作子,类似标准 C++ 控制台输出对象 cout 的操作子,fixed 是指按定点数显示,scientific 是指按科学计数法显示。

3. 字符串运算符

QString 重载了多个对字符串有清晰意义的运算符,之前见过赋值运算符,可以将一个 QString 对象赋值给另一个 QString 对象。还有其他的比 较运算符和中括号运算符,先将其罗列如下:

operator 描述
= 赋值运算符,遵循隐式共享规则,在赋值的两个对象有变化时才真正复制数据块。
+= 追加。将运算符左边和右边字符串拼接后,赋值给左边对象。
< 小于号。左边字符串字典序比右边的靠前时,表达式为真。
<= 小于等于。左边字符串字典序比右边的靠前或相同时,表达式为真。
== 等于。二者字典序是一致的时候为真。
!= 不等于。二者字典序不一样的时候为真。
> 大于。左边字符串字典序比右边的靠后时,表达式为真。
>= 大于等于。左边字符串字典序比右边的靠后或相同时,表达式为真。
[] 类似数组取数的中括号,从指定位置取出 QChar 字符,另外还可以修改指定位置的 QChar 字符。
+ 拼接。这是个友元函数,将两个字符串拼接后返回全新的字符串对象。

上面运算符的意义是一目了然的,主要解释一下赋值运算符 = 的隐式共享(Implicit Sharing),在执行赋值时,真正的字符串数据拷贝没有发生,这是为了优化运行效率,避免大量数据的拷贝。隐式共享实现方式就是对数据块做引用计数,多一个对象赋值或 参数、返回值拷贝时,引用次数加 1,这个赋值过程只需要设置一下数据指针和增加引用计数,不会真的拷贝大量数据,这种拷贝称为浅拷贝(shallow copy)。

在赋值的一个字符串发生变化,要做写入修改时,这个要发生变化的字符串会重新分配一块内存,将旧的数据拷贝到新的内存空间,并对其做相应的写入修改,这个过程叫深 拷贝(deep copy),也可称为 copy-on-write(写时拷贝)。深拷贝会将旧的数据块引用计数减 1,然后将变化的字符串数据指向新空间,新空间引用计数加 1。

如果发生字符串超出生命期销毁或清空,那么对应的数据引用计数减 1,当引用计数减到 0 时,数据块空间才会真的被释放。

Qt 对象能够赋值或传参数、返回值的,一般都是采用隐式共享机制,所以 Qt 的参数和返回值传递运行效率是很高的。这也将信号和槽机制传递参数的效率大大提升了。面向对象的高级编程语言一般都支持类似的功能,比如 Java 和 Python 的垃圾回收机制,也是类似的。

下面通过简单示例展示运算符的使用:

void Test_operator()
{
// =
QString strE1, strE2, strE3;
strE1 = QObject::tr("abcd");
strE2 = strE1;
strE3 = strE2;
//打印数据指针
qDebug()<<strE1.data_ptr()<<"\t"<<strE2.data_ptr()<<"\t"<<strE3.data_ptr();
//改变字符串,追加
strE2.append( QObject::tr("1234") );
//再次打印数据指针,谁修改了数据,谁的数据指针就变
qDebug()<<strE1.data_ptr()<<"\t"<<strE2.data_ptr()<<"\t"<<strE3.data_ptr();
// += 和 append 函数类似 strE3 += QObject::tr("1234"); qDebug()<<strE2<<"\t"<<strE3;

//比较 1 vs 2
qDebug()<<"strE1 < strE2: "<<(strE1 < strE2); qDebug()<<"strE1 <= strE2: "<<(strE1 <= strE2); qDebug()<<"strE1 == strE2: "<<(strE1 == strE2); qDebug()<<"strE1 != strE2: "<<(strE1 != strE2); //2 vs 3
qDebug()<<"strE2 > strE3"<<(strE2 > strE3); qDebug()<<"strE2 >= strE3"<<(strE2 >= strE3); qDebug()<<"strE2 == strE3"<<(strE2 == strE3);
//类似数组取数 qDebug()<<strE1[0]; strE1[0] = QChar('?'); //修改 qDebug()<<strE1;

//拼接 QString strPlus; strPlus = strE1 + strE2 + strE3; qDebug()<<strPlus;
}

我们来看看开头赋值运算符一段代码的运行结果,其他的比较简单就不贴了:

0x13725b98 0x13725b98 0x13725b98
0x13725b98 0x13741958 0x13725b98

可以看到三个字符串对象都没有修改时,它们的数据指针是一样的,这就是浅拷贝的过程。在 strE2 发生改变时,strE2 会重新分配一块内存空间,并将旧的数据拷贝到新空间然后做对应的追加字符串操作,修改之后,strE2 的数据指针变了,由于 strE1 和 strE3 这时没变化,它们数据指针还是一样的。在有写操作的时候,才会发生深拷贝,发生变化的字符串对象就会完全独立。后续的比较操作、中括号操作、拼接操作等代码读者可以自己去 试试,看看结果如何。

3 .子串查询与操作

在面对文本处理时经常会遇到子串查询和操作,QString 类拥有大量这方面的函数,并且重载的也比较多,详细的函数可以通过查阅帮助文档获知,索引栏输入 QString 就能找到该类说明。下面简略讲解一些比较实用的函数(一般每个函数名只列一个声明,还有其他同名重载的请查帮助文档),这些函数不用死记硬背的, 等到需要用的时候查一下文档就行了。

QString &    append(const QString & str)

append 追加子串到字符串尾部。

QString &    prepend(const QString & str)

prepend 将子串加到字符串头部。

bool startsWith(const QString & s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const

startsWith 判断字符串(如 "abcd")是否以某个子串(如 s 是 "ab")打头,cs 指判断时大小写是否敏感 ,返回 bool。

bool endsWith(const QString & s, Qt::CaseSensitivity cs = Qt::CaseSensitive) const

endsWith 判断字符串(如 "abcd")是否以某个子串(如 s 是 "cd")结尾,cs 指判断时大小写是否敏感,返回 bool。

bool contains(const QString & str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const

contains 判断字符串对象里是否包含子串 str ,参数 cs 指判断时大小写是否敏感,后面函数的 cs 都是一个意思,不重复说了。

int count(const QString & str, Qt::CaseSensitivity cs = Qt::CaseSensitive) const

对字符串对象里子串 str 出现的次数做统计,返回出现次数,如果没出现就返回 0。

int indexOf(const QString & str, int from = 0, Qt::CaseSensitivity cs = Qt::CaseSensitive) const

indexOf 从 from 指定的序号开始查询子串 str,返回查到的第一个 str 子串起始位置序号。查不到就返回 -1 。

QString &    insert(int position, const QString & str)

默认从字符串尾部开始向前查询,设置 from 之后,从 from 开始的位置向前查询子串 str,返回最先匹配的 str 子串起始位置序号(搜索区间 0 ~ from ,子串起始序号最接近 from)。查不到就返回 -1 。

QString &    remove(int position, int n)

insert 是将子串 str 插入到 position 序号位置,子串 str 插入后的起始序号就是 position 。

QString & remove(int position, int n) remove 从 position 开始的位置移除掉 n 个字符,如果 n 比 position 位置开始的子串长度大,后面的就会被全部移除。

QString & remove(const QString & str, Qt::CaseSensitivity cs = Qt::CaseSensitive)

这个重载的 remove 函数将匹配的所有子串 str 都从字符串里面移除掉,拿来消除空格之类的字符比较好使。

QString & replace(int position, int n, const QString & after)

replace 将从 position 序号开始的 n 个字符的子串替换成 after 字符串。

QString & replace(const QString & before, const QString & after, Qt::CaseSensitivity cs = Qt::CaseSensitive)

这个重载的 replace 将字符串里出现的所有子串 before 全部替换为新的 after。

QStringList split(QChar sep, SplitBehavior behavior = KeepEmptyParts, Qt::CaseSensitivity cs = Qt::CaseSensitive) const

split 用字符或子串 sep 切分当前字符串内容,然后将切分的所有子串以 QStringList 列表形式返回,可以从返回的列表提取各个子串。behavior 是分隔模式,是否保留空白字符区域等。

QStringList split(const QString & sep, SplitBehavior behavior = KeepEmptyParts, Qt::CaseSensitivity cs = Qt::CaseSensitive) const

QString section(QChar sep, int start, int end = -1, SectionFlags flags = SectionDefault) const

section 函数首先将字符串按照字符或子串 sep 分成段落,类似 split 划分,但 section 只返回第 start 段到第 end 段之间的内容。如果没指定 end 就一直包含到最后。flags 参数影响划分行为,如大小写敏感、是否忽略空白区域等。

QString    left(int n) const

left 返回字符串左边 n 个字符构成的子串。

QString    right(int n) const

right 返回字符串右边 n 个字符构成的子串。

QString    mid(int position, int n = -1) const

mid 返回从 position 位置开始的 n 个字符构成的子串。不设置 n 的话就包含到末尾。

QString &    fill(QChar ch, int size = -1)

fill 用字符 ch 填充当前字符串,如果不指定 size ,就把所有的字符都填成 ch 字符。如果指定正数 size,字符串长度被重置为 size 大小,里面依然全是 ch 字符。

QString    repeated(int times) const

将当前字符串重复拼接 times 次数,返回新的重复串。

QString    trimmed() const

trimmed 剔除字符串头部和尾部的空白字符,包括 '\t', '\n', '\v', '\f', '\r', ' ' 。 字符串中间的空白不处理。

QString    simplified() const

simplified 剔除字符串里出现的所有空白字符,包括 '\t', '\n', '\v', '\f', '\r', ' ' 。 两端和中间的都剔除。

void    truncate(int position)

truncate 是从 position 序号开始截断字符串,只保留 0 ~ position-1 位置的字符串,position 位置被设为 NULL,后面的全移除。

关于子串处理的函数就罗列上面这些,等到用的时候再查文档就行了。下面示范一个测试函数,挑几个函数出来用用,看看效果如何。

void Test_substring()
{
QString strOne = QObject::tr("abcd");
QString strThree = strOne.repeated(3); //abcd 重复三次
qDebug()<<strThree.isEmpty(); //是否为空
qDebug()<<strThree.length()<<"\t"<<strThree.size(); //都是长度
qDebug()<<strThree; //子串查询 qDebug()<<strThree.contains(strOne); //是否包含 qDebug()<<strThree.count(strOne); //包含几个 qDebug()<<strThree.startsWith(strOne); //打头的子串 qDebug()<<strThree.indexOf(strOne); //左边开始的子串位置 qDebug()<<strThree.lastIndexOf(strOne); //右边开始的子串位置 //剔除两端的空白 QString strComplexFileName = QObject::tr(" /home/user/somefile.txt \t\t "); QString strFileName = strComplexFileName.trimmed(); qDebug()<<strFileName; if(strFileName.endsWith( QObject::tr(".txt") )) { qDebug()<<"This is a .txt file"; } //分隔子串 QStringList subsList = strFileName.split(QChar('/')); for(int i=0; i<subsList.length(); i++) //打印各个子串 { qDebug()<<i<<"\t"<<subsList[i]; } //获取段落 QString subsections = strFileName.section(QChar('/'), 2, 3); qDebug()<<subsections;
}

测试函数前半截的代码意义是比较简单的,就不多说了,看看后面文件名部分的输出结果,strComplexFileName 剔除两端的空白区域之后,得到 strFileName:

"/home/user/somefile.txt"

这个新的 strFileName 以 ".txt" 结尾,所以判断为文本文件:

This is a .txt file

然后以字符 '/' 分隔文件名为子串:

0 ""

1 "home"

2 "user"

3 "somefile.txt"

注意文件名被拆成了四个子串,而不是三个,第一个 '/' 左边没有东西,也会被切分为一个独立的子串 "" ,就是空串。

切分后的子串保存在 QStringList 里面,这就像存储多个 QString 对象的数组,可以直接用 [] 操作各个子串。

最后的取段落函数,它分隔段落的方法和 split 函数类似的,取出序号从 2 到 3 的段落内容,这些段落内部之间的分隔符是保留的,段落函数返回的是一个 QString 对象,而不是列表:

"user/somefile.txt"

QString 还有其他的如字母大小写转换函数,toUpper() 函数是转成全大写,toLower() 函数是全部转成小写。clear() 函数是清空字符串,这些函数作用一目了然,就不多作介绍了。

5. QTextStream配合字符串使用

对于熟悉 C++ 里面 iostream 控制台输入输出流、fstream 文件数据流和 sstream 内存数据流的程序员,如何在 Qt 里面使用类似的流操作呢?之前示范过 qDebug() 有流操作子 fixed 和 scientific,这是调试输出流。对于控制台输入输出流、文件流、内存流,Qt 统一用强大的 QTextStream 来支持,本小节简单介绍利用 QTextStream 对 QString 做内存流的输入输出处理,以后的“文件和数据流”章节还会更多地介绍 QTextStream 。

QTextStream 配合 QString 使用的过程非常简单,QTextStream 构造函数接受 QString 对象的指针,并且支持类似 cout 流的操作子,我们直接来看示例代码:

void Test_QTextStream()
{
//内存输出流
QString strOut;
QTextStream streamOut(&strOut);
//打印多种进制数字
streamOut<<800<<endl;
streamOut<<hex<<127<<endl;
streamOut<<oct<<63<<endl;
//还原为十进制
streamOut<<dec;
//设置域宽和填充字符 streamOut<<qSetFieldWidth(8)<<qSetPadChar('0')<<800; //还原默认域宽和填充 streamOut<<qSetFieldWidth(0)<<qSetPadChar(' ')<<endl; //设置精度
streamOut<<qSetRealNumberPrecision(3)<<fixed<<123.789999<<endl; streamOut<<qSetRealNumberPrecision(6)<<scientific<<123.789999<<endl;
//打印字符串和数字混搭 streamOut<<QObject::tr("7*7 == ")<<7*7<<endl; //显示现在的字符串对象 qDebug()<<strOut;
//内存输入流
QString strIn = QObject::tr("800 abcd 123.789999"); QTextStream streamIn(&strIn); int numDec = 0; QString strSub; double dblReal = 0.0; //输入到变量里 streamIn>>numDec>>strSub>>dblReal; //显示 qDebug()<<numDec; qDebug()<<strSub; qDebug()<<fixed<<dblReal; //定点数显示
}

第一块代码是打印多种进制的数值,流操作子和 cout 是一样的,hex 是十六进制,oct 是八进制,dec 是十进制。需要注意的是用完各种进制之后,要记得把流的进制还原为 dec 十进制,否则之前设置的进制会一直持续生效,直到被重新设置为止。这部分显示的结果(双引号是字符串对象 strOut 起始标志):

"800

7f

77

第二块代码是设置显示域宽 qSetFieldWidth 、填充字符 qSetPadChar,用完之后要还原,否则域宽和填充字符会持续生效。这部分显示结果为:

00000800

第三块是设置精度 qSetRealNumberPrecision 并打印浮点数到字符串对象 strOut 里面,定点数操作子为 fixed,科学计数法操 作子为 scientific,这部分结果显示为:

123.790

1.237900e+02

第四块是打印字符串和数值混搭,都输出到字符串对象 strOut ,然后将 strOut 显示到调试输出面板里(双引号是字符串对象结尾):

7*7 == 49

"
该测试函数最后是将字符串 "800 abcd 123.789999" 作为输入源,然后把数据输入到整型 numDec、字符串对象 strSub 和浮点数 dblReal 里,得到的结果显示为:

800

"abcd"

123.789999

将字符串对象作为输入源使用也很方便,灵活运用 QTextStream 对于字符串操作也是很有益处的。

对于输出流,除了三个 q 打头的操作子 qSetFieldWidth 、qSetPadChar、qSetRealNumberPrecision ,其他的操作子名字和 cout 的操作子名字是基本一样的。

6. QString示例代码

QString 示例代码就是上面 6 个测试函数的合集,然后融入到一个简单 Qt 程序里。6 个测试函数分别为 Test_setNum()、Test_arg()、Test_toValue()、Test_operator()、Test_substring()、 Test_QTextStream(),这些代码不重复贴了,上面都有,测试这些函数时,每次只启用一个,其他的注释掉,慢慢试,不用全记住的,可以分几次学。示例代码下 载:

https://lug.ustc.edu.cn/sites/qtguide/QtProjects / ch03 / testqstring / testqstring.7z
照旧把代码解压到如 D:\QtProjects\ch03\testqstring 文件夹里。下面主要把包含头文件和 main 函数贴出来:

//testqstring.cpp
#include <QApplication>
#include <QTextBrowser>
#include <QDebug>
#include <QTextStream>
void Test_setNum() { ... }
void Test_arg() { ... }
void Test_toValue() { ... }
void Test_operator() { ... }
void Test_substring() { ... }
void Test_QTextStream() { ... }
int main(int argc, char *argv[]) { QApplication a(argc, argv); QString strText = QObject::tr("测试字符串类 QString"); QTextBrowser tb;
tb.setText(strText); tb.setGeometry(40, 40, 400, 300);
tb.show();
//setNum
Test_setNum();
//arg
//Test_arg();
//toValue
//Test_toValue();
//operator
//Test_operator();
//substring
//Test_substring();
//QTextStream
//Test_QTextStream();
return a.exec();
}

这个 main 代码和上一节的类似,主界面是文本显示框,测试函数的结果打印到 QtCreator 输出面板,将 6 个函数逐个测试一下,并注意观察输出面 板的信息,加深一下对本节知识的印象。


您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码: 验证码,看不清楚?请点击刷新验证码 必填



59 次浏览
4次