在串口通讯、UDP/TCP通讯和其他形式数据传递时,通常需要将获取的各种类型数据转为连续的位数据,然后进行数据传递。在QT中通常将数据转化为QByteArray格式,格式转化过程中会碰到输入一个16进制数,然后发送该数据的情况,例如: EE 80 16 32,用QString获取这样一串数据(数据位之间也可能不包含空格),然后将其字面意思转化为4位16进制数,0xEE 0x80 0x16 0x32,将数据存入 QByteArray中并进行传递数据。
QString类型16进制数转QByteArray,主要思路为:
将QString数据去除所有空格(假设数据不包含非法字符,非法字符可考虑用正则表达式QRegExp过滤),保持数据纯粹性;
数据每两位之间增加空格或者“.”等易分辨标识符;
依据标识符,利用QString的split函数,将QString根据标识符分割为QStringList,每两位分割为一个QString,例如EE为一个QString,80为一个QString;
分割后的每个QString采用16进制的方式转化为Int,获得16进制的0xEE和0x80等数据;
将获得的数据存入到QByteArray中使用。
具体例子如下:
QString instr = ui->textEditIN->toPlainText(); //从界面的textEdit中获得QString16进制数据
QByteArray inarray; //定义QByteArray存储位数据
instr = instr.trimmed();
instr = instr.simplified(); //去除QString首尾空格,可不用
instr.remove(QRegExp("\\s")); //正则表达式,去除QString中所有的空格
int leng = instr.size();
for (int i = 2; i < leng/2*3-1;) //每两个字符中间增加一个空格,也可增加“.”等字符
{
instr = instr.insert(i," ");
i += 3;
}
QStringList sl = instr.split(" "); //根据标识字符,分割QString为QStringList,每个单独的QString为两位,可转化为一位16进制数
bool ok;
foreach(QString s, sl) //将QString循环转化为一位16进制数
{
if(!s.isEmpty())
{
char c = s.toInt(&ok,16)&0xFF;
if(ok)
{
inarray.append(c); //依次存入QByteArray中
}else
{
qDebug()<<"非法的16进制字符:"<<s;
QMessageBox::warning(NULL,tr("错误"),QString("非法的16进制字符:\"%1\"").arg(s));
}
}
}
下面切入正题CRC16校验(正题没几句话,算不算偷换概念),CRC16校验的原理网上资料很多,自己可以搜一搜,本人对具体原理理解的也不是很透彻,主要都是进行位运算,实现方法如下:
quint16 Crc16::crc16(const quint8 *data, quint16 len)
{
quint16 crc = 0xFFFF;
for (quint8 i = 0; i < len; i++)
{
crc ^= data[i];
for(int j = 0; j < 8; j++)
{
quint16 carry_flag = crc & 0x0001;
crc >>= 1;
if(carry_flag)
{
crc ^= 0xA001;
}
}
}
return crc;
}
获得的2位16进制数一般都要添加到QByteArray末尾进行数据传输,用memcpy就可以了。如果需要重新展示为QString类型,可以用下边方法,而且QString中每两位数据都需要增加空格展示。实现如下:
quint16 outint = crc16((quint8*)inarray.data(),inarray.size()); //CRC16函数使用方法
QByteArray outarray; //CRC16结果要存入的QByteArray
outarray.resize(2);
outarray.fill(0,2);
outarray[0] = (uchar)(0x00ff&outint);
outarray[1] = (uchar)((0xff00&outint)>>8); //转换-存入QByteArray
QString outstr;
for (int i = 0; i < outarray.count(); i++) //QByteArray转为QString展示,并增加空格
{
outstr.append(tr("%1 ").arg((quint8)outarray.at(i),2,16,QLatin1Char('0')).toUpper());
}
ui->textEditOUT->setText(outstr); //界面输出
时间有限,上边内容就完成了一个CRC16校验demo的制作,我们可以输入待转换的数据,然后获取结果,程序所有源码如下:
crc16.h
#ifndef CRC16_H
#define CRC16_H
#include <QWidget>
QT_BEGIN_NAMESPACE
namespace Ui { class Crc16; }
QT_END_NAMESPACE
class Crc16 : public QWidget
{
Q_OBJECT
public:
Crc16(QWidget *parent = nullptr);
~Crc16();
static quint16 crc16(const quint8*data, quint16 len);
private slots:
void on_ButtonCalculate_clicked();
void on_ButtonClear_clicked();
private:
Ui::Crc16 *ui;
};
#endif // CRC16_H
crc16.cpp
#include "crc16.h"
#include "ui_crc16.h"
#include <QDebug>
#include <QMessageBox>
#include <QRegExp>
Crc16::Crc16(QWidget *parent)
: QWidget(parent)
, ui(new Ui::Crc16)
{
ui->setupUi(this);
this->setWindowFlags(Qt::WindowMinimizeButtonHint|Qt::WindowCloseButtonHint);
this->setWindowTitle("CRC16校验");
}
Crc16::~Crc16()
{
delete ui;
}
quint16 Crc16::crc16(const quint8 *data, quint16 len)
{
quint16 crc = 0xFFFF;
for (quint8 i = 0; i < len; i++)
{
crc ^= data[i];
for(int j = 0; j < 8; j++)
{
quint16 carry_flag = crc & 0x0001;
crc >>= 1;
if(carry_flag)
{
crc ^= 0xA001;
}
}
}
return crc;
}
void Crc16::on_ButtonCalculate_clicked()
{
QString instr = ui->textEditIN->toPlainText();
QByteArray inarray;
instr = instr.trimmed();
instr = instr.simplified();
instr.remove(QRegExp("\\s"));
int leng = instr.size();
for (int i = 2; i < leng/2*3-1;)
{
instr = instr.insert(i," ");
i += 3;
}
QStringList sl = instr.split(" ");
bool ok;
foreach(QString s, sl)
{
if(!s.isEmpty())
{
char c = s.toInt(&ok,16)&0xFF;
if(ok)
{
inarray.append(c);
}else
{
qDebug()<<"非法的16进制字符:"<<s;
QMessageBox::warning(NULL,tr("错误"),QString("非法的16进制字符:\"%1\"").arg(s));
}
}
}
if(inarray.size() < 2) return;
quint16 outint = crc16((quint8*)inarray.data(),inarray.size());
QByteArray outarray;
outarray.resize(2);
outarray.fill(0,2);
outarray[0] = (uchar)(0x00ff&outint);
outarray[1] = (uchar)((0xff00&outint)>>8);
QString outstr;
for (int i = 0; i < outarray.count(); i++)
{
outstr.append(tr("%1 ").arg((quint8)outarray.at(i),2,16,QLatin1Char('0')).toUpper());
}
ui->textEditOUT->setText(outstr);
QString addstr;
addstr.append(instr + " ");
addstr.append(outstr);
ui->textEditADD->setText(addstr);
}
void Crc16::on_ButtonClear_clicked()
{
ui->textEditIN->clear();
ui->textEditADD->clear();
ui->textEditOUT->clear();
}
界面图如下:
![](https://i-blog.csdnimg.cn/blog_migrate/e1d0ffb5ccb87859588b311798ea9690.png)
程序运行界面:
![](https://i-blog.csdnimg.cn/blog_migrate/df70419a017a73f8f0638ca72c78fe3a.png)
就这些,主要描述了通过界面获取数据,实现CRC16校验的全过程,在程序内部直接进行数据转化比从界面获取数据在转换容易很多,所以记录一下这个过程,后边有需要的时候可以翻看。
源码下载:
https://download.csdn.net/download/qq_28178271/87383586?spm=1001.2014.3001.5503