QByteArray在串口通讯中经常被使用,通信处理过程中,经常会涉及到数据格式的转换,有一定必要较为全面详细的对QByteArray数据转换与处理部分进行阐述。本文通过以下几个部分加以介绍
一、 字符串发送与十六进制发送的区别
在使用串口发送数据时可以选择字符串发送或者十六进制发送,关于两者的区别,需要从计算机存储数据的格式说起。
在计算机中,数据是以二进制的形式存储的,例如十进制 1(10)在计算机中用 0000 0001(2)来表示。在使用字符串方式发送数据的时候,串口软件首先将待数据转换为对应的ASCII码,然后再将这些ASCII码按照二进制的方式一位一位的发送出去;而使用十六进制方式发送数据的时候,串口直接将数据以二进制的方式一位一位的发送出去。
例如我们在串口软件发送框输入 “FF012345”,分别以字符串和十六进制两种方式发送:
1.1 字符串发送
串口以字符串发送数据,首先将字符串转化为ASCII码对应的二进制,格式如下
串口发送了8个字节,若在接收端选择字符串接收,则会接收到FF0123456这个字符串;若在接收端选择十六进制接收,则会接收到:
0100 0110 | 0100 0110 | 0011 0000 | 0011 0001 | 0011 0010 | 0011 0011 | 0011 0100 | 0011 0101 这一串数字
1.2 十六进发送
串口以十六进制发送数据,首先将发送框中数据转化为如下格式:
串口发送了4个字节,若在接收端选择十六进制接收,则会在接收框显示FF 01 23 45;若在接收端选择字符串接收,则会接收到乱码
仔细对照接收结果就很容易明白字符串发送和十六进制发送的区别了。
二、QByteArray
1、初始化、访问与赋值
访问QByteArray主要有4中方式,分别为[]、at()、data[]和constData[]。其中[]和data[]为可读可写,at()和constData[]仅为可读。如果仅是读,则通过at()和constData[]访问速度最快,因可避免复制处理。示例如下:
QByteArray ba;
ba.resize(6);
ba[0] = 0x3c;
ba[1] = 0xb8;
ba[2] = 0x64;
ba[3] = 0x18;
ba[4] = 0xca;
ba.data()[5] = 0x31;
qDebug()<<"[]"<<ba[2]; //[] d
qDebug()<<"at()"<<ba.at(2); //at() d
qDebug()<<"data()"<<ba.data()[2]; //data() d
qDebug()<<"constData()"<<ba.constData()[2]; //constData() d
qDebug()<<"constData()"<<ba.constData()[5]; //constData() 1
2 QByteArray数据转换与处理
从串口读取到的QByteArray数据,一般需要进行提取和解析,此时就需要QByteArray转换为各类型数据。常用转换包括:
1、字符与十六进制转换,例如串口接收到的数据,用字符方式表达,或者将字符串对应的十六进制数据流转化为字符串;
2、转为不同进制数值并显示,如二进制、八进制、十进制和十六进制等;
3、转为整型、浮点型等数值类型;
4、大小写转换;
5、转为字符串类型;
2.1 十六进制------字符
把Hex编码的数据流转换为char存储到QByteArray:
QByteArray text = QByteArray::fromHex("517420697320677265617421");
text.data(); // "Qt is great!"
把QByteArray数据转换为字符串:
QByteArray ba;
ba.resize(3);
ba[0] = 0x30;
ba[1] = 0x31;
ba[2] = 0x32;
qDebug() << ba.toHex(); //return "303132"
2.2 数值转换与输出
尽管QByteArray是一个集合,但也可以作为一个特殊形式的数值用,其灵活的转换格式,可大大方便各种格式数据转换与显示的需求。如显示二进制和十六进制、显示科学计数和指定小数位的数值。示例如下:
把单个字符转为2-36进制数据格式:
int n = 63;
qDebug()<<QByteArray::number(n); // returns "63"
qDebug()<<QByteArray::number(n, 16); // returns "3f"
qDebug()<<QByteArray::number(n, 16).toUpper(); // returns "3F"
qDebug()<<QByteArray::number(n, 2); // returns "111111"
qDebug()<<QByteArray::number(n, 8); // returns "77"
按照指定进制格式直接复制,其中n可以是各类常见数值类型:
QByteArray ba;
int n = 63;
ba.setNum(n); // ba == "63"
ba.setNum(n, 16); // ba == "3f"
把数值按指定格式和小数位转换输出,小数位四舍五入:
QByteArray ba1 = QByteArray::number(12.3456, 'E', 3);
QByteArray ba2 = QByteArray::number(12.3456, 'f', 3);
qDebug()<<ba1; // returns "1.235E+01"
qDebug()<<ba2; // returns "12.346"
2.3 字符串数值转为各类数值
QByteArray strInt("1234");
bool ok0;
qDebug() << strInt.toInt(); // return 1234
qDebug() << strInt.toInt(&ok0,16); // return 4660, 默认把strInt作为16进制的1234,对应十进制数值为4660
QByteArray string("1234.56");
bool ok1;
qDebug() << string.toInt(); // return 0, 小数均视为0
qDebug() << string.toInt(&ok1,16); // return 0, 小数均视为0
qDebug() << string.toFloat(); // return 1234.56
qDebug() << string.toDouble(); // return 1234.56
QByteArray str("FF");
bool ok2;
qDebug() << str.toInt(&ok2, 16); // return 255, ok2 == true
qDebug() << str.toInt(&ok2, 10); // return 0, ok == false, 转为十进制失败
2.4 大小写转换
QByteArray若为带大小写的字符串,可通过toUpper()和toLower()方法实现大小写转换,示例如下:
QByteArray x("Qt by THE QT COMPANY");
QByteArray y = x.toLower();
// y == "qt by the qt company"
QByteArray z = x.toUpper();
// z == "QT BY THE QT COMPANY"
2.5 与字符串互转
QByteArray与QString互转极为简单,二者从本质上类似,都是连续存储,区别是前者可以存无法显示的字符,后者只存可显示的字符。如QByteArray可以存0x00-0x19,而QString则存储如0x30等可显示字符(0x20-0x7E)。可显示字符可参见ASCII表,链接如下:ASCII可显示字符
QByteArray转为QString示例:
QByteArray ba("abc123");
QString str = ba; //方法一
qDebug()<<str ;
//输出:"abc123"
QByteArray ba("abc123");
QString str;
str = str.prepend(ba); //方法二
qDebug()<<str ;
//输出:"abc123"
QString转为QByteArray示例:
QString str("abc123");
QByteArray ba = str.toLatin1(); //方法一
qDebug()<<ba;
QString str("abc123");
QByteArray ba = str.toUtf8(); //方法二
qDebug()<<ba;
QString crcstr = "5A 0A 80 06 00 03 00 01";
qDebug()<<"QString :"<<crcstr;
qDebug()<<"len0:"<<crcstr.size();
//方法一
QByteArray data = crcstr.toLocal8Bit(); //toUtf8()
qDebug()<<"str.toLocal8Bit():"<<data;
qDebug()<<"len1:"<<data.size();
data = QByteArray::fromHex(data); // 表示从十六进制数转成字符串。
qDebug()<<"QByteArray::fromHex:"<<data;
qDebug()<<"len2:"<<data.size();
QByteArray类同样不以’\0’为结尾:如
QByteArray bytes;
bytes.resize(5);
bytes[0] = '1';
bytes[1] = '2';
bytes[2] = '3';
bytes[3] = '\0';
bytes[4] = 'a';
cout << bytes << endl;
QByteArray转QString,主要用buf.toHex()即可
static QString ByteArrayToHexStr(QByteArray buf)
{
QString retStr;
retStr.append(buf.toHex());
return retStr;
}
应用举例:
QByteArray(const char *data, int size = -1) //QByteArray Str("1234") 内存中存储可能就是对应的ASSIC:0x31, 0x32, 0x33, 0x34
QByteArray QByteArray::fromHex(const QByteArray &hexEncoded) //将字符串的“1234”变为0x11 0x34,原本占用4字节内存,现在变为2字节。0x31, 0x32, 0x33, 0x34 ==> 0x12 0x34
QByteArray QByteArray::toHex() const //将0x12 0x34 变为 “1234” 原本占用2字节内存,现在变为4字节 0x12 0x34 ==> 0x31, 0x32, 0x33, 0x34
int QByteArray::toInt(bool *ok = Q_NULLPTR, int base = 10) const //Str.toInt(&Ok, 16)将字符串 “1234”变为int型 0x1234,, 转换的前提:Str内就是存的字符串,Str内存的不是“0”--“9” “a”--“f”,不能转换成功
QString(const QByteArray &ba) //可以将QByteArray转换为字符串,我认为内存占用,以及数据都是一样的,只是类型变了,原来QByteArray表示0x31 0x32 0x33 0x34, 还是同样的数据用QString表示了
QString fromUtf8(const QByteArray &); //将QByteArray转换为QSting。字符串的编码方式不同,内存占用以及数据就不同,但是都可以看成char[] 数组,在Qt中char 数组就是QByteArray,因此将不同编码方式的字符串转换为QString的函数入参都是QByteArray
QString number( A, B); //将数字转换为字符串 将A转变为B进制, A=10 B=16 转换为“a” : 10 = 0x0A ; A=10 B=10 转换为“10”
int QString::toInt(bool *ok = Q_NULLPTR, int base = 10) const //base = 16,将字符串 “12”变为 0x12
QByteArray QString::toLatinl() const; //将QString编码的字符串转换到 其他类型的字符串编码
QByteArray QString::toUtf8() const; //举例: 字符串“1”,在内存中就是0x31 (ASSIC 0x31表示1),只不过存储方式是QString的编码,假如Utf8与QString的编码一样,转换出来后,还是0x31
QByteArray QString::toLocal8Bit() const; //假如 Local8Bit中 “1” 用16进制0x88表示, 那么转换后,内存的数成了0x88,
QByteArray QString;:toUCS4() const;
Latin1:SO-8859-1编码是单字节编码,向下兼容ASCII,其编码范围是0x00-0xFF
utf8字符集以2个或以上的字节表示一个汉字,实际上具体的数值和unicode有很大的相关性
unicode字符集以2个或以上的字节表示一个汉字。
gb18030字符集兼容了gbk字符集,以两个字节表示一个文字。windows系统可能使用的就是这两种的一种。
UCS:通用字符集(Universal Character Set, UCS)是由ISO制定的ISO 10646(或称ISO/IEC 10646)标准所定义的标准字符集。UCS-2用两个字节编码,UCS-4用4个字节编码。
QByteArray Serial::sendHexCommand(QString cmd, int recvlen)
{
QByteArray data = cmd.toLocal8Bit(); //toUtf8()
data = QByteArray::fromHex(data); // 表示从十六进制数转成字符串。两位变一位
uint16_t wcrc = this->ModbusCRC16(data);
qDebug()<<"CRC low8:"<<QString("%1").arg(uint8_t(wcrc), 2, 16,QLatin1Char('0')) //不足4位补0
<<"CRC high8:"<<QString("%1").arg(uint8_t(wcrc>>8), 2, 16,QLatin1Char('0'));
//低位在前,高位在后
data.append(char(wcrc));
data.append(char(wcrc>>8));
qDebug()<<"cmdSend:"<<data.toHex(' ');
// m_serialPort->setReadBufferSize(recvlen);
m_serialPort->write(data);
m_serialPort->waitForBytesWritten(1000);
// QThread::msleep(100);
QByteArray buff;
qDebug()<<"waitForReadyRead before .......";
while(m_serialPort->waitForReadyRead(5000))
{
qDebug()<<"waitForReadyRead after1 .......";
QElapsedTimer t;
t.start();
while(t.elapsed()<100) QCoreApplication::processEvents();
qDebug()<<"waitForReadyRead after2 .......";
// buff = m_serialPort->read(recvlen);
buff = m_serialPort->readAll();
qDebug()<<"buff:"<<buff.toHex(' ');
if(buff.isEmpty())
{
qDebug()<<"buff is null !!!!!!!!";
}
break;
}
qDebug()<<"waitForReadyRead after3 .......";
if(buff.isEmpty())
{
qDebug()<<"buff is null timeout !!!!!!!!";
}
return buff;
}
//test.........................................................
QString crcstr = "5A 0A 80 06 00 03 00 01";
qDebug()<<"QString :"<<crcstr;
qDebug()<<"len0:"<<crcstr.size();
//方法一
QByteArray data = crcstr.toLocal8Bit(); //toUtf8()
qDebug()<<"str.toLocal8Bit():"<<data;
qDebug()<<"len1:"<<data.size();
data = QByteArray::fromHex(data); // 表示从十六进制数转成字符串。
qDebug()<<"QByteArray::fromHex:"<<data;
qDebug()<<"len2:"<<data.size();
uint16_t wcrc = this->ModbusCRC16(data);
qDebug()<<"CRC low8:"<<QString("%1").arg(uint8_t(wcrc), 2, 16,QLatin1Char('0')) ; //不足4位补0
qDebug()<<"CRC high8:"<<QString("%1").arg(uint8_t(wcrc>>8), 2, 16,QLatin1Char('0'));
// //方法二.........................................................................................
// QString str = "5A 0A 80 06 00 03 00 01";
// QByteArray ba2 = this->hexStringToByteArray(str);
// qDebug()<<"hexStringToByteArray():"<<ba2;
// uint16_t wcrc2 = this->ModbusCRC16(ba2);
// qDebug()<<"CRC low8:"<<QString("%1").arg(uint8_t(wcrc2), 2, 16,QLatin1Char('0')); //不足4位补0
// qDebug()<<"CRC high8:"<<QString("%1").arg(uint8_t(wcrc2>>8), 2, 16,QLatin1Char('0'));
//test.........................................................
QByteArray text;
text = QByteArray::fromHex("51 74 20 69 73 20 67 72 65 61 74 21"); //表示从十六进制数转成字符串。
qDebug()<<"QByteArray::fromHex:"<<text; //QByteArray::fromHex: "Qt is great!"
QByteArray ascii;
ascii = text.toHex(' '); //表示从字符串转成十六进制数。该十六进制编码使用数字0-9和字母a-f。
qDebug()<<"text.toHex:"<<ascii; //text.toHex: "51 74 20 69 73 20 67 72 65 61 74 21"
//test.........................................................
QByteArray array;
quint16 test16 = 0x1234;
quint32 test32 = 0x12345678;
//低位在前,高位在后
array.append(char(test16));
array.append(char(test16>>8));
char data2[2];
memcpy(&data2, &test16, sizeof(quint16));
array.append(data2[0]);
array.append(data2[1]);
//低位在前,高位在后
array.append(char(test32));
array.append(char(test32>>8));
array.append(char(test32>>16));
array.append(char(test32>>24));
char data3[4];
memcpy(&data3, &test32, sizeof(quint32));
array.append(data3[0]);
array.append(data3[1]);
array.append(data3[2]);
array.append(data3[3]);
qDebug()<<"array:"<<array.toHex(' ');