2.1 字符串类

一.引言

我们以前学过C语言的字符串(实际上C语言的字符串就是以'\0'结尾的字符数组所模拟出来的字符串)以及C++标准库提供的字符串类string;而Qt也有属于自己的字符串类QString类,并且Qt的QString字符串类比C++的标准库的string的功能更为强大,更为齐全.

二.Qt的字符串类-->QString类

  2.1 我们通过新建一个工程来学习Qt的一些常用字符串(我们将新建一个空白的Qt的控制台应用程序来完成这一部分的学习)

  2.2 Qt控制台程序的建立

    2.2.1 打开Qt Creator集成开发环境,点击菜单栏的:文件->新建文件或项目

    2.2.2 在这里选择:Qt Console Application(Qt的控制台应用程序)

    

    

    

    

    

  2.3 工程建立完成之后,我们需要在main.cpp中引入QString头文件和QDebug头文件

    注意:这个QDebug头文件中,我们需要使用到一个qDebug()的函数对象,这个函数对象非常的强大,它本身就重载了<<操作符,因此我们可以借助它来输出各种各样Qt的数据类型的结果,包括QString类型的字符串

    以下是main.cpp的全部内容

#include<QCoreApplication>
#include<QDebug>
#include<QString>
int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);
    qDebug()<<"Part_I QString字符串的常用操作";
    //1.和C++的字符串类一样,QString类也重载了+,+=这两种运算符来连接字符串
    QString s1("Hello,");
    s1 = s1 + "World!";
    qDebug()<<s1;//"Hello,World!"
    QString s2 = "Welcome!";
    s2 += s2;
    qDebug()<<s2;//"Welcome!Welcome!"

    //2.QString::append()方法也是一样的,它也可以在当前字符串的后面再追加一个字符串
    QString s3("Hello,I'm ");
    s3.append("蜡笔小新Pointer");
    qDebug()<<s3;

    //3.QString对标准的ASCII码的字符和非ASCII码的字符的处理方式
    ///3.1 实际上,当我们用一个纯英文字符串去初始化QString对象时,QString内部会调用一个QString::fromAscii()
    /// 方法转换为一个以Unicode编码的字符串去初始化QString类型的对象,并且对于大于了128的字符的ASCII码,这个
    /// 函数会将其转换为Latin-1编码进行处理(这似乎是一种很冷门的编码方式),因此少数情况下会出现传入中文字符
    /// 输出时乱码的问题,如果我们想要指定大于128的ASCII字符的编码为非Latin-1,我们可以调用
    /// QTextCodec::setCodecForCString()这个方法,这个方法会改变QString::fromAscii函数当ASCII码值大于
    /// 128时可能造成的乱码问题
    ///3.2 另外再编译程序时,可以通过直接定义QT_CAST_FROM_ASCII来屏蔽上面的那个大于128的字符的使用了Latin-1
    /// 的转换,如果程序员定义的字符串都必须经过QObject::tr()处理,那么屏蔽上面的Latin-1转换是非常有必要的
    /// 否则就会乱码!

    //4.QString格式化字符串的两种方式(使用QString::fprintf()方法和QString::arg()方法)
    QString s4;
    //QString::sprintf方法用法和C语言的格式化字符串是一样的(都是在内存中对字符串进行格式化)
    s4.sprintf("大家好!我是%s,今年%d岁","蜡笔小新Pointer",32);
    qDebug()<<s4;
    //QString::arg()方法大家必须要掌握,这是一个非常好用的格式化字符串的方式
    QString s5;
    s5 = QString("大家好,我是%1,今年%2岁了,现居%3").arg("蜡笔小新Pointer").arg(32).arg("成都市");
    qDebug()<<s5;
    /*
     * 为什么建议大家一定要掌握QString::arg方法来格式化字符串?
     * 1.相比于QString::sprintf方法,arg能够支持的不仅仅是C和C++基本数据类型的直接输出,并且还支持Qt独有
     *   的一些数据类型
     * 2.使用起来比sprinf方便,我们只需要在格式串中依次填写%1,%2,%3,...这种%+数字方式来就可以兼容所有的类型
     * 但sprinf还需要你去记那些数据类型具体得用哪一种格式控制符来输出,比较麻烦
    */

    //5.在字符串的指定位置插入字符串QString::insert()方法
    QString s6("Wow,That Is a man!");
    qDebug()<<s6.insert(14,"wo");

    //6.在字符串的开头插入其他的字符串
    QString s7(",World!");
    qDebug()<<s7.prepend("Hello");

    //7.移除字符串两边的空字符(这个是很有用的一个功能,记住它)
    QString s8 = "    ABCDEGF       ";
    QString s9 = s8.trimmed();//这个函数并不会影响到原来的字符串,它只是把原字符串两边的空白字符移除后,
    //重新返回一个新的字符串(它返回的并不是原字符串的引用)
    qDebug()<<s9;

    //8.移除字符串两边的空白字符,并使用单个的' '这种空白字符代替字符串中的非空白字符
    QString s10 = "    Hello,\n I'm \t 蜡笔小新   Pointer    ";
    qDebug()<<s10.simplified();

    qDebug()<<"Part_II 查询字符串中的数据";
    //1.判断字符串是否以某个字符或字符串开头,并且规定是否大小写敏感(QString::startWith)
    QString s11 = "Welcome to China!";
    qDebug()<<s11.startsWith("wel",Qt::CaseSensitivity::CaseSensitive);//是否以'wel'开头,并且大小写敏感(false)
    qDebug()<<s11.startsWith("wel",Qt::CaseSensitivity::CaseInsensitive);//是否以'wel'开头,大小写不敏感(true)
    //同样的,也有判断是否以某个字符串结尾的QString::endWith(),用法同理

    //2.判断某个字符或字符串是否在该字符串中出现过QString::contains()=>同样也可以指定大小写敏感
    QString s12("ABCDEFG");
    qDebug()<<s12.contains("Cd",Qt::CaseSensitivity::CaseSensitive);//false
    qDebug()<<s12.contains("cd",Qt::CaseSensitivity::CaseInsensitive);//true

    //3.比较两个字符串(这个只要是比较两个字符串的长度以及是否相同,因为QString重载了>,<,>=,<=,=,==,!=)
    QString s13 = "ABCDEF";
    QString s14 = "ERTYI";
    QString s15 = "ABCDEF";
    qDebug()<<(s13<s14);//false
    qDebug()<<(s13>s14);//true
    qDebug()<<(s13==s15);//true
    qDebug()<<(s13!=s14);//true

    qDebug()<<"Part_III 字符串的转换";

    //QString的字符串的转换函数是一系列以to开头的成员函数
    //1.字符串转换为整数QString::toInt(bool*isOk=nullptr,int base)
    QString s16 = "125";
    bool isOk = false;
    qDebug()<<s16.toInt(&isOk,16) << ":" << isOk;//将字符串"125"以16进制转换为整数,isOk表示本次转换是否成功
    qDebug()<<s16.toInt(&isOk,10) << ":" << isOk;//将字符串"125"以10进制转换为整数,看看是否转换成功
    ///一般情况下,基数都默认为10,如果指定为0,则会采用C语言的转换方式(即:如果改字符串以0x开头,则会以16进制去转换
    /// ;如果是以0开头,则以8进制转换,其他情况下,基数一律是10 [你无法转换为2进制,或者是3进制])(基数只能是8,10,16)
    //2.除了toInt之外,还有toFloat,toDouble,toLong这些也可以使用
    QString s17 = "3.1415926";
    qDebug()<<s17.toDouble();
    //3.基于本地字符集来比较两个字符串,如果前面的字符串小于后面的字符串,则返回负数,如果相同返回0,如果大于返回正数
    //这个是一个静态函数QString::localeAwareCompare()
    qDebug()<<QString::localeAwareCompare(QString("12345"),QString("123"));//1
    qDebug()<<QString::localeAwareCompare(QString("12345"),QString("12345"));//0
    qDebug()<<QString::localeAwareCompare(QString("123"),QString("12345"));//-1
    //还有一个QString::compare()函数,这个函数是基于Unicode码来比较的,可以指定大小写,比较的速度很快
    //返回值和localeAwareCompare()一个含义,前面的字符串小,返回负数,相同返回0,大于返回正数
    qDebug()<<QString::compare("123","ABCD",Qt::CaseSensitivity::CaseSensitive);
    //4.将QString转换为其他类型的字符串(比如UTF-8,ASCII,Latin-1)
    //最终转换的结果都是QByteArray类型,这种类型比传统C的char*类型更好用,它既可以存储原始字节(比如文件
    //以二进制形式打开读出的字节,还可以存储以\0结尾的典型的8位单字符
    QString s18("ABCDEFGHIJKL");
    const QByteArray& buf = s18.toUtf8();
    qDebug()<<buf<<s18.toLatin1();
    ///这里说明一下字符集
    ///1.ASCII:这是家喻户晓的典型的以8位二进制来表示一个字符的编码方式
    ///2.Latin-1:这是一种比较冷门的编码方式(它遵循Latin编码规范,也是使用8位二进制表示一个字符)
    ///3.UTF-8:当代最为流行的编码方式(它是ASCII的超集,它支持所有的Unicode字符集,它也是使用至少8位二进制
    /// 来表示一个字符)
    //在某些场合下,我们可能需要将字符集转换为本地的8位二进制表示的字符,使用QString::toLocal8Bit()方法即可
    QString s19("哈哈哈哈!");
    qDebug()<<s19.toLocal8Bit();//"\xE5\x93\x88\xE5\x93\x88\xE5\x93\x88\xE5\x93\x88!"
    //需要调用QByteArray的data方法获取到里面的数据
    qDebug()<<s19.toLocal8Bit().data();//"哈哈哈哈!"

    //可能问到的面试题(空字符串对象和空字符串有什么区别?)
    //空字符串对象是指在使用QString的构造函数时,不给其传入任何参数,类似下面这样子
    QString s20;
    qDebug()<<s20.isNull()<<s20.isEmpty();//isNull是判断该字符串对象是否是空对象,isEmpty则判断该字符串是否是
    //空字符串(这里结果都是true,true)==>因为空对象一定是空字符串
    //那么空字符串是不是空对象呢?(其实不是)
    //空字符串是指使用""来初识化一个QString字符串对象
    QString s21("");
    qDebug()<<s21.isNull()<<s21.isEmpty();//false,true

    qDebug()<<"QString对字符串的一些优化机制";
    /*Qt引入了隐式共享的概念,实际上这就是C++的深浅拷贝的问题,Qt规定,如果两个对象的在内存中的数据映像完全一致
     * 那么它将不会为第二个对象再单独新开一块内存空间去存和第一个对象一样的内存映像,而是直接将对象二的指针直接
     * 指到第一个对象的内存映像的地址上(这也就是所谓的浅拷贝),但是什么是深拷贝?还是前面那个例子,我们两个字符串
     * s1 = "abcd",s2 = "abcd",然后我们知道,他们两在内存上都是指向的同一个内存空间的,但是如果我修改了s2
     * 那么必然导致深拷贝,首先,程序会重新分配一块真正属于s2的地址,然后将s1的内存映像全部拷贝过去,然后按照你
     * 修改了s2去修改s2的值(这也就是深拷贝),实际上这也是Qt对字符串类的一种优化机制,因为我们知道,执行一次深
     * 拷贝的代价远大于浅拷贝,我们看一个案例来说明Qt对字符串的深浅拷贝的问题
    */
    QString s22 = "ABCDEFG";//a
    QString s23 = s22;//b
    s23[3] = 'e';//c
    s23[0] = 'f';//d
    s22 = s23;//e
    ///a.我们使用了一个const char*的字符串"ABCDEFG"去通过=操作符初始化QString对象s22,此时在内存中会开辟
    /// 一块新的栈空间来保存"ABCDEFG";
    ///b.我们将s22的值复制一份给s23,实际上这里并不会对s23单独开空间去保存s22的值,而是直接将s23指向s22所
    /// 指向的内存地址,这里发生了一次浅拷贝
    ///c.我们修改了s23,这必然导致一次深拷贝,程序内部会先分配一块独立的内存空间,然后将这块空间的地址给s23,然后
    /// 再修改s23[3]为e
    ///d.由于s23已经有了自己独立的内存空间了,此时,修改就修改吧,不会引起任何形式的复制
    ///e.此时,s22又会重新指向s23的内存空间(两个引用指向同一块内存空间),而刚刚为s23开辟的独立的内存空间会被释放
    ///实际上,不仅仅是QString支持这种隐式共享的机制,对于Qt其他的一些类(Qt的所有的容器类[QList,QMap,..];以及
    /// QVariant,QByteArray,QBrush,QPen,QPalette,QBitmap,QImage,QPixmap,QFont等都具有这种隐式共享的机制
    /// 在里面,大家一定要结合C++的深浅拷贝来理解这种隐式共享的本质

    //QString的内存分配机制
    //实际上,我们知道,QString所创建的字符串对象,如果我们这个字符串对象的字符串长度在不断的增加,实际上它会
    //以下面的方式来进行分配策略
    ///1.每次分配4字节,直到20为止
    ///2.当字节数大于20小于4084时,QString每次分配的内存以2倍的速度增长
    ///3.从4084开始,以后的每次分配都已4096字节作为分配大小继续往后分配
    return a.exec();
}

  附录(代码文件)

  https://files.cnblogs.com/files/blogs/792763/QStringDemo.zip?t=1688553580&download=true

    

posted @ 2023-07-05 18:40  蜡笔小新Pointer  阅读(36)  评论(0)    收藏  举报