做了通讯已经有好几年了,一直用QT做开发,对于C的很多东西都不是很训练的运用了,其实做程序开发,就是要讲究简单、高效、稳定,C做为C++的基础,是最容易体现这些特点的!
对于QT进行ASCII码的字符串解析,我们通常是怎样解析的呢?
以NMEA0183协议为例:
"$GPRMC,173843,A,3349.896,N,11808.521,W,000.0,360.0,230108,013.4,E*69\r\n"
首先我们要对这个协议进行分析
/*$GPRMC
字段0:$GPRMC,语句ID,表明该语句为Recommended Minimum Specific GPS/TRANSIT Data(RMC)推荐最小定位信息
字段1:UTC时间,hhmmss.sss格式
字段2:状态,A=定位,V=未定位
字段3:纬度ddmm.mmmm,度分格式(前导位数不足则补0)
字段4:纬度N(北纬)或S(南纬)
字段5:经度dddmm.mmmm,度分格式(前导位数不足则补0)
字段6:经度E(东经)或W(西经)
字段7:速度,节,Knots
字段8:方位角,度
字段9:UTC日期,DDMMYY格式
字段10:磁偏角,(000 - 180)度(前导位数不足则补0)
字段11:磁偏角方向,E=东W=西
字段16:校验值*/
然后想办法对它进行解析,这是一个GPRMC协议格式,用QT解析它的时候,我们第一个想到的是字符串分割,如下:
QString gprmcStr = "$GPRMC,173843,A,3349.896,N,11808.521,W,000.0,360.0,230108,013.4,E*69\r\n";
QStringList gprmcStrList = gprmcStr .split(QRegExp("[,*]"));
for(int i = 0;i< gprmcStrList.size();i++)
{
switch(i)
{
case 0:
QString headStr = gprmcStrList .at(i);
break;
case 1:
QString dateStr = gprmcStrList .at(i);
break;
case 2:
....
}
}
用C语言解析,我们怎么解析呢,这个方法以前经常用,但是C++用久了后,尤其QT平台下,我们会渐渐淡忘!
float GPS_MS = 0.0;
char GPS_FLAG;
double lat = 0.0;
char lat_dir;
double lon = 0.0;
char lon_dir;
double speed = 0.0;
double angle = 0.0;
long GPS_DAY = 0;
double M_dec = 0.0;
char M_dec_dir;
int crc = 0;
char str[] ="$GPRMC,173843,A,3349.896,N,11808.521,W,000.0,360.0,230108,013.4,E*69\r\n";
sscanf (str,"$GPRMC,%f,%c,%f,%c,%f,%c,%f,%f,%d,%f,%c*%d\r\n"
,&GPS_MS,&GPS_FLAG,&lat,&lat_dir,&lon,&lon_dir,&speed,&angle,&GPS_DAY,&M_dec,&M_dec_dir,&crc);
怎么样,是不是感觉这样子简单明了,还不会出错?对于有些东西数值类型我们是需要注意的,比如时间字段,还需要我们后面再格式转化成字符串!
这里我们用sscanf来解析字符串,我们也可以用sprintf来字符串格式化,进行数据协议重构!
上面是C语言中的用法,C++写C兼容,当然可以用于到C++里去!
不过QT里面,QString也重新实现了sprintf方法,所以在进行时间和日期解析时,我们可以用得到;上面以经说过在QT中解析的方法,但是那样解析不全面,尤其在解析日期和时间时会遇到问题,在开发过程中,我们会遇到两个因为开发平台不同,在数据解析过程中会使得数据缺损,比如时间和日期,A在读取时间和日期时,会直接把协议中的时间分别赋值给float和long,而在使用sprintf重组协议时没有进行格式控制,这样会导致前导0丢失。而在QT中接收并解析时,日期和时间在赋值给QDate和QTime时进行格式化赋值,QDate::fromString(str,"ddMMyy"),QTime::fromString(str,"hhmmss.zzz"),会导致无法解析的情况,我们在分割后只要再进行一下格式控制就可以了,如下:
QString dateStr;
dateStr.sprintf("%06d",wtdList.at(i).toInt());
mUTCDate = QDate::fromString(dateStr,"ddMMyy");
//mUTCDate = QDate::fromString(wtdList.at(i),"ddMMyy");
QString timeStr;
timeStr.sprintf("%010.3f",wtdList.at(i).toFloat());
//mUTCTime = QTime::fromString(wtdList.at(i),"hhmmss.zzz");
mUTCTime = QTime::fromString(timeStr,"hhmmss.zzz");
以上是我在开发过程中遇到的问题,并总结了一下,这样编写的程序耦合度比较好,程序不容易崩溃!
在解析数据完成后,我们需要对数据进行存储,存储的形式多种多样,绝大多数会采用数据库来存储数据!接下来我来说说如何来设计程序进行数据存储!
在解析数据后,大家一般会定义各个变量来存储各个指标值,上像上面的程序一样,然后组成SQL语句进行数据上传。这种方式不利于程序模块化!这个时候我们要对数据结构进行封装。
C++中的struct对C中的struct进行了扩充,它已经不再只是一个包含不同数据类型的数据结构了,它已经获取了太多的功能。
struct能包含成员函数吗? 能!
struct能继承吗? 能!!
struct能实现多态吗? 能!!!
我们定义了一个 struct GPRMC_DATA这样的结构体类型:
struct GPRMC_DATA
{
float GPS_MS;
char GPS_FLAG;
double lat;
char lat_dir;
double lon;
char lon_dir;
double speed;
double mAngle;
long GPS_DAY;
double M_dec;
char M_dec_dir;
int crc;
GPRMC_DATA();
GPRMC_DATA(const GPRMC_DATA &gprmcData);
GPRMC_DATA & operator=(const GPRMC_DATA &gprmcData);
std::string InsertSQL();
};
GPRMC_DATA::GPRMC_DATA()
{
GPS_MS = 0.0;
GPS_FLAG = ' ';
lat = 0.0;
lat_dir = ' ';
lon = 0.0;
lon_dir = ' ';
speed = 0.0;
mAngle = 0.0;
GPS_DAY = 0;
M_dec = 0.0;
M_dec_dir = ' ';
crc = 0;
};
GPRMC_DATA::GPRMC_DATA(const GPRMC_DATA &gprmcData)
{
GPS_MS = gprmcData.GPS_MS;
GPS_FLAG = gprmcData.GPS_FLAG;
lat = gprmcData.lat;
lat_dir = gprmcData.lat_dir;
lon = gprmcData.lon;
lon_dir = gprmcData.lon_dir;
speed = gprmcData.speed;
mAngle = gprmcData.GPS_DAY;
GPS_DAY = gprmcData.GPS_MS;
M_dec = gprmcData.M_dec;
M_dec_dir = gprmcData.M_dec_dir;
crc = gprmcData.crc;
};
GPRMC_DATA & GPRMC_DATA::operator=(const GPRMC_DATA &gprmcData)
{
GPS_MS = gprmcData.GPS_MS;
GPS_FLAG = gprmcData.GPS_FLAG;
lat = gprmcData.lat;
lat_dir = gprmcData.lat_dir;
lon = gprmcData.lon;
lon_dir = gprmcData.lon_dir;
speed = gprmcData.speed;
mAngle = gprmcData.GPS_DAY;
GPS_DAY = gprmcData.GPS_MS;
M_dec = gprmcData.M_dec;
M_dec_dir = gprmcData.M_dec_dir;
crc = gprmcData.crc;
return *this;
}
std::string GPRMC_DATA::InsertSQL()
{
std::ostringstream sql;
sql << "insert into residential_list(GPS_MS, GPS_FLAG, lat, lat_dir, lon, lon_dir, speed, angle,GPS_DAY,M_dec,M_dec_dir,crc) values("
<< "\'" << GPS_MS << "\',"
<< "\'" << GPS_FLAG << "\',"
<< setprecision(15)
<< "\'" << lat << "\',"
<< "\'" << lat_dir << "\',"
<< setprecision(15)
<< "\'" << lon << "\',"
<< "\'" << lon_dir << "\',"
<< "\'" << speed << "\',"
<< "\'" << mAngle << "\',"
<< "\'" << GPS_DAY << "\',"
<< "\'" << M_dec << "\',"
<< "\'" << M_dec_dir << "\',"
<< "\'" << crc << "\'"
<< ")";
return sql.str();
}
这样有利于程序模块化,如果要解析GPGGA或GPGLL这样的数据,我们再定义两个这样的数据类型就可以了,在赋完值后,只需执行InsertSQL功能函数便可以方便的获得sql语句。
欢迎大家指正,一起探讨!