在实际应用中,我们可能会遇到处理固定长度数字的情况,比如像邮政编码、身份证号这样的编号,这些编号可能是由几段有意义的数字组成,在序列化为字符串时需要逐段输出,而在反序列化时又需要逐段读入。
这里假设我们有如下一种编码,它由8位数字组成,前2位为省份编号,接着2位为城市编号,最后4位为实例号,其数据结构为:
在C++中,我们可以使用ostream提供的一些方法来实现固定长度的输出,如下:
setw用于设置宽度,setfill用于设置填充字符。这两个设置都是一次性的,即完成一次输出后会被重置,因此我们不得不每次都进行设置。另外,上面将province_no和city_no转换为int型是为了避免将其作为字符类型输出。
对于反序列化,C++就不太方便了,setw对于istream的输入是没有效果的,我们可以尝试下面的代码,但结果会让人失望。
其实,我觉得istream也应该被赋予setw这一能力,这不是什么难事,可惜STL的设计者没有往这方面考虑。
由于直接使用STL并不能简洁的处理上述问题,因此以前我都采用C方式来实现序列化与反序列化:
char buf[16];
sprintf(buf, "%02d%02d%04d", code.province_no, code.city_no, code.inst_no); // 序列化
int province_no = 0, city_no = 0, inst_no = 0; // 注意变量类型均为int,否则会引起stack corrupt错误,当然也可以选用long类型
sscanf(buf, "%2d%2d%4d", &province_no, &city_no, &inst_no); // 反序列化
上述代码很简洁,但并不优雅,而且容错性不好,因此,我还是打算在C++范畴内实现。于是,我设计了一个模版类:
我先给出其用法,对于上述例子,我们可以这样处理:
#include "fixed_length_number.h"
#include <iostream>
#include <iomanip>
#include <sstream>
using namespace std;
typedef fixed_length_number<char, 2> ProvinceNo;
typedef fixed_length_number<char, 2> CityNo;
typedef fixed_length_number<short,4> InstNo;
struct SomeCode2
{
SomeCode2() {}
SomeCode2(ProvinceNo pn, CityNo cn, InstNo in)
{
province_no = pn;
city_no = cn;
inst_no = in;
}
ProvinceNo province_no;
CityNo city_no;
InstNo inst_no;
};
inline std::ostream& operator<<(std::ostream& os, const SomeCode2& code)
{
return os << code.province_no << code.city_no << code.inst_no;
}
inline std::istream& operator>>(std::istream& is, SomeCode2& code)
{
return is >> code.province_no >> code.city_no >> code.inst_no;
}
int main(int argc, char** argv)
{
SomeCode2 code2(1, 2, 3);
cout << code2 << endl; // 序列化,输出01020003
std::stringstream ss("12345678");
ss >> code2; // 反序列化
cout << code2 << endl; // 输出12345678
}
可以看出,这种方式是很完美的,下面给出fixed_length_number的具体源码:
/**
* Copyleft 2006-12-26 by freefalcon
*/
#ifndef __FIXED_LENGTH_NUMBER_H__
#define __FIXED_LENGTH_NUMBER_H__
#include <iosfwd>
#include <iomanip>
template<typename NumberType, size_t Length, bool Rollback = true>
struct fixed_length_number
{
public:
fixed_length_number(NumberType num = 0)
{
m_num = num;
}
operator NumberType&()
{
return m_num;
}
operator NumberType() const
{
return m_num;
}
private:
NumberType m_num;
};
template<typename NumberType, size_t Length, bool Rollback>
inline std::ostream& operator<<(std::ostream& os, const fixed_length_number<NumberType, Length, Rollback>& num)
{
return os << std::setw(Length) << std::setfill('0') << (unsigned long)(NumberType)num;
}
inline int char_to_int(char ch)
{
if(ch >= '0' && ch <= '9') return ch - '0';
if(ch >= 'A' && ch <= 'F') return ch - 'A' + 10;
if(ch >= 'a' && ch <= 'f') return ch - 'a' + 10;
return 0xff;
}
template<typename NumberType, size_t Length, bool Rollback>
inline std::istream& operator>>(std::istream& is, fixed_length_number<NumberType, Length, Rollback>& num)
{
if(Length == 0)
{
is >> (NumberType&)num;
}
else
{
int base = 10;
switch(is.flags() & std::ios_base::basefield)
{
case std::ios_base::dec: base = 10; break;
case std::ios_base::hex: base = 16; break;
case std::ios_base::oct: base = 8; break;
}
char ch;
(NumberType&)num = 0;
int length = Length;
int tmp;
while(length--)
{
if(!(is >> ch && (tmp = char_to_int(ch)) < base))
{
std::ios_base::iostate state = is.rdstate();
if(Rollback)
{
is.clear();
is.seekg(state ? length-Length+1 : length-Length, std::ios_base::cur);
num = 0;
}
is.setstate(state|std::ios_base::badbit);
return is;
}
(NumberType&)num = num*base + tmp;
}
}
return is;
}
#endif//__FIXED_LENGTH_NUMBER_H__
代码其实很简单,这里对三个模版参数作以说明,NumberType为数字类型,Length为数字长度,Rollback表示当从istream读取失败时是否对读指针进行“回滚”处理(类似于事务处理)。另外,上述代码还考虑了8进制和16进制的情况,应该能够满足我们的大部分需求。
联系客服