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