QByteArray简单入门

来源:互联网 发布:淘宝发布宝贝照片尺寸 编辑:程序博客网 时间:2024/05/22 13:47

Qt通过QByteArray为我们提供了一个字节数组容器。QByteArray既可以用来存储原始的字节,包括'\0',也可以用来存储传统的8-bit 的以'\0'结尾的字符串。使用QByteArray比使用普通的const char* 更方便。并且,在底层,它可以确保其中存储的数据以'\0'结尾,并且通过隐式共享(写时拷贝)策略去减少内存的使用和不必要的数据复制。

当然,除了QByteArray,Qt也提供了QString类去存储字符串。并且,在大多数情况下,我们都选择使用QString。QString 存储的是16位的Unicode字符,所以,在应用程序中使用QString会更容易存储 non-ASCII/non-Latin-1 字符。而且,QString在整个Qt库中也被大量使用到。而Qt库提供QByteArray的用处主要有两个方面:一是用来存储原始的二进制数据,二是在内存比较紧张的情况下,比如嵌入式设备上。

而具体使用QByteArray 的一种通用方式就是直接将const char* 传给它的构造函数。例如,下面的代码构造了一个大小为5的字符数组,其中包含的数据为"hello":

QByteArray ba("Hello");

虽然,对这个对象调用size() 成员函数会返回5,但QByteArray 仍然包含了一个'\0'在字符串最后,以此来确保如果一个函数需要一个指向该对象底层数据的指针时,该指针所指的数据以'\0'结束。另外,当你使用这种方式来创建QByteArray对象时,QByteArray 会采取深拷贝的动作,所以,在你创建完QByteArray的对象后,你还可以修改作为参数的字符串而不会对程序产生任何副作用。当然,如果你处于性能考虑,不想采用深拷贝的方式,那么你可以使用QByteArray的静态方法fromRawData()来创建QByteArray对象。

另一种创建QByteArray对象的方式是先使用resize() 函数设置对象的大小,即要存储的字节数,然后一个字节一个字节的初始化。因为QByteArray和c++数组一样,使用基于0的索引,并且提供了[]运算符,所以这种方式也是非常的方便的。例如:

  QByteArray ba;  ba.resize(5);  ba[0] = 0x3c;  ba[1] = 0xb8;  ba[2] = 0x64;  ba[3] = 0x18;  ba[4] = 0xca;

而对于访问其中的每一个字节,除了使用上面的[]运算符外,我们还可以使用它的at() 成员函数。并且,at() 通常都比[]运算符更快,因为它从不会发生深拷贝。比如,下面的代码:

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


而如果想一次提取多个字节的话,可使用left()、right()、mid()成员函数。

我们刚才说过,QByteArray通常用来存储原始的二进制数据。所以,可以在QByteArray中存储'\0'。而成员函数size() 还是可以返回整个数组的大小,包括中间的'\0',但不包括由QByteArray自动添加到最后的'\0'。例如:

  QByteArray ba1("ca\0r\0t");  ba1.size();                     // Returns 2.  ba1.constData();                // Returns "ca" with terminating \0.  QByteArray ba2("ca\0r\0t", 3);  ba2.size();                     // Returns 3.  ba2.constData();                // Returns "ca\0" with terminating \0.  QByteArray ba3("ca\0r\0t", 4);  ba3.size();                     // Returns 4.  ba3.constData();                // Returns "ca\0r" with terminating \0.  const char cart[] = {'c', 'a', '\0', 'r', '\0', 't'};  QByteArray ba4(QByteArray::fromRawData(cart, 6));  ba4.size();                     // Returns 6.  ba4.constData();                // Returns "ca\0r\0t" without terminating \0.

并且,我们可以使用qstrlen() 函数来得到第一个'\0'之前的字符串的长度。还有,上面讲QByteArray对象的创建时,我们提到了resize() 函数,但注意,当调用该函数后,那些新分配的字节是未定义的,为了给这些字节设置特定的值,可以使用fill() 函数。

除了使用operator[] 和 at() 函数进行访问外,我们还可以使用data() 和 constData() 直接得到数组底层的数据的指针。这个返回的指针会一直保持有效,直到下次non-const函数被调用;同时,也可以确保这些数据是以'\0'结尾的,除非该QByteArray对象是有raw data 创建的。返回的字符串中也会自动保护QByteArray添加在最后的'\0'。

对于QByteArray的修改,该类提供了大量的基础函数,如:append()、prepend()、insert()、replace()、remove()。例如:

  QByteArray x("and");  x.prepend("rock ");         // x == "rock and"  x.append(" roll");          // x == "rock and roll"  x.replace(5, 3, "&");       // x == "rock & roll"

其中,replace() 和 remove() 函数的前两个参数,是指开始删除的位置和要删除的字符的个数。

当向一个非空的数组追加数据时,数组会重新分配空间然后将数据拷贝进去。如果考虑的性能问题,也可以事先调用一次reserve() 函数,为数组预分配一部分空间,这样可以省去每次追加数据都再分配的开销。可以使用capacity() 函数取得QByteArray当前实际分配了多少内存。

向一个空的数组追加数据不会发生拷贝。

在我们实际开发使用中,经常会需要从字节数组的两端去掉空白字符,如'\n'、'\t'、'  ' 等,此时,可以使用trimmed() 函数;如果你在去掉两端空白字符的同时,还想把中间连续的多个空白字符替换成一个,可以使用simplified() 函数。

同样,QByteArray的查找也是提供了indexOf() 和 lastIndexOf() 。顾名思义,一个从前往后查找,一个从后往前查找。如果查找成功,都返回字符或子串的下标位置,否则返回-1。如果只是想检测是否包含某个字符或子串,可以简单的使用contains() 函数;如果想知道一个特定的字符或子串在字节数组里出现了几次,可以使用count() 函数。替换某个字符或子串也是类似的,可以使用replace() 函数。

最后需要说明的是,由于历史原因,QByteArray区分字节数组为null和字节数组为空。null表示一个字节数组是使用QByteArray的默认构造函数创建的;而一个空的字节数组是大小为0 的字节数组。它们的关系是,一个null字节数组一定是个空数组,但是反过来不成立。切记!

  QByteArray().isNull();          // returns true  QByteArray().isEmpty();         // returns true  QByteArray("").isNull();        // returns false  QByteArray("").isEmpty();       // returns true  QByteArray("abc").isNull();     // returns false  QByteArray("abc").isEmpty();    // returns false


但是,在QByteArray的类成员函数中,除了isNull() ,其他的成员函数,都将这两种数组同等看待。例如,data() 函数会为null字节数组也返回一个指向'\0'字符测指针,而不是一个空指针;并且,QByteArray() 等同于 QByteArray("")。所以,我们一般推荐使用isEmpty() ,而不要使用isNull()。


上面说了这么多关于QByteArray的信息和用法,接下来,我们在一并简单说下和QByteArray相关的另外两个类,一个是QByteArrayList,一个是QByteArrayMatcher。

QByteArrayList顾名思义就是QByteArray的链表,类似于QList<QByteArray>。所以,所有QList的函数都可以应用到QByteArrayList。例如,你可以使用isEmpty() 来测试链表是否为空,使用append()、prepend()、insert()、replace()、等待函数去修改一个QByteArrayList。除了这些QList的函数外,QByteArrayList额外提供了几个join() 函数,可以将QByteArrayList中的元素连接成一个单独的QByteArray。

还有,Qt库提供QByteArrayList的目的和QStringList是完全不同的。比如,QStringList有很多成员函数可以用来操作其中的元素,而QByteArrayList没有。通常情况下,当我们想表示可打印类字符串的列表时,优先选用QStringList。QByteArrayList通常用来处理高效的连接大量二进制数据,比如,从一个顺序的io设备中接收数据时。

QByteArrayMatcher类用来在QByteArray中高效的反复查找一个字节序列。在反复查找的情况下,通过使用该类的对象和其成员函数indexIn() 比直接用QByteArray的indexOf() 进行查找更高效。但是,该类对于单字节的查找情况没有更高的效率。其使用方法是,使用要查找的QByteArray序列创建一个QByteArrayMatcher的对象,然后通过indexIn()成员函数在目标字节数组中进行查找。indeIn() 的具体声明如下:

int QByteArrayMatcher::indexIn(const QByteArray &ba, int from = 0) constint QByteArrayMatcher::indexIn(const char *str, int len, int from = 0) const

如果查找成功,返回子字节数组在目标数组的下标位置;如果查找失败,返回-1。

若在构造QByteArrayMatcher的对象时,没有为其传入要查找的pattern,也可以在以后通过setPattern() 来设置。

QByteArray QByteArrayMatcher::pattern() constvoid QByteArrayMatcher::setPattern(const QByteArray &pattern)

对于这上类,我们建立一个控制台程序,来为大家简单的演示一下:

新建项目的步骤就略过了,启动Qt Creator,点击文件->新建文件或项目->选择 Qt Console Application 即可。

下面,给出测试代码:

#include <QCoreApplication>#include <QByteArray>#include <QByteArrayMatcher>#include <QByteArrayList>#include <QDebug>int main(int argc, char *argv[]){    QCoreApplication a(argc, argv);    QByteArray array("Hello ");    array.append("world ");    array.prepend("you ");    qDebug() << "Origin byte array: " << array;    QByteArrayMatcher matcher("world");    int pos = matcher.indexIn(array);    if(pos == -1)    {        qDebug() << "Search failed";    }    else    {        qDebug() << "The position is " << pos;    }    QByteArrayList byteList;    byteList << "I" << "like" << "Qt";    qDebug() << "byteList: " << byteList;    QByteArray joinArray = byteList.join("-");    qDebug() << "joinArray: " << joinArray;    return a.exec();}


0 0