c++常用功能封装
C++文件读写的封装
在C++开发中,文件读写是很常用的功能,出于代码复用的考虑,将它们进行封装。
其中,默认读写的文件是文本文件,并且需要按行处理。调用者只需要关注读取出来的每一行的处理方式。写文件只是简单的开关文件流。
具体代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 | /** * @brief 读文件 * @author env0y * @param [in] const std::string& filename,文件名(绝对路径) [in] T& fn,对读出的数据进行逐行操作的函数.fn(std::string,...) [in/out] std::map<std::string,int>& fieldIdx,表头字段及其索引值 [in] const char Separator = ',',表头字段分隔符,默认',' * @param [out] none * @return bool * @note 1. 参数fieldIdx带入表头字段值,带出字段值对应的索引 2. 参数fn的第一个参数必须是std::string类型 */ template < typename T> static inline bool ReadFile( const std::string& filename, T& fn, std::map<std::string, int >& fieldIdx, const char Separator = ',' ) { typedef std::map<std::string, int > FieldMap; ifstream fd(filename,std::ios::in); fd.imbue(std::locale(std::locale::empty(), new std::codecvt_utf8< wchar_t >)); if (fd.fail()) { //LOG_ERROR_F( ModuleInfo,_T("ReadFile:Open [%s] failure."),CString(filename.c_str()).GetString()); return false ; } auto Raii = [&](){ fd.close(); }; ON_SCOPE_EXIT(Raii); //LOG_DEBUG_F( ModuleInfo,_T("ReadFile:file [%s] size [%lld] Bytes."),CString(filename.c_str()).GetString(),CalcFileSz(fd)); std::string oneline; ( void )getline(fd,oneline); if ( false == FieldIndexLookup(oneline,fieldIdx,Separator) ) { return false ; } do { ( void )fn( oneline ); //每读一行,传递给用户定义的函数 oneline.clear(); ( void )getline(fd,oneline); } while ( !fd.eof() ); return true ; } /** * @brief 字段索引查找确认 * @author env0y [2015/12/24 11:09:49] * @param [in] const std::string& tableHeader,表头 [in/out] std::map<std::string,int>& fieldIdx,字段为入参,索引为出参 char separator = ',',表头字段分隔符,默认为',' * @param [out] none * @return bool,查找是否成功 * @note none */ static inline bool FieldIndexLookup( const std::string& tableHeader,std::map<std::string, int >& fieldIdx, char separator /*= ','*/ ) { if (fieldIdx.empty()) { return true ; } typedef std::map<std::string, int > FieldMap; const int FieldNotExist = -1; ( void )std::for_each( fieldIdx.begin(),fieldIdx.end(),[=](FieldMap::value_type& ele){ ele.second = FieldNotExist;}); int TmpIdx = FieldNotExist; std::string subStr = tableHeader; string::size_type curpos = std::string::npos; do { curpos = subStr.find_first_of(separator); std::string fieldStr = subStr.substr(0,curpos); subStr = subStr.substr(curpos+1); ++TmpIdx; auto it = fieldIdx.find( fieldStr ); if ( it != fieldIdx.end()) { it->second = TmpIdx; } } while ( std::string::npos != curpos ); bool bFieldsInsufficient( false ); //- 字段是否缺失 ( void )std::for_each(fieldIdx.begin(),fieldIdx.end(),[&]( const FieldMap::value_type& ele) { if ( ele.second == FieldNotExist ) { bFieldsInsufficient = true ; std::cout << "FieldIndex: Lack of field:" << ele.first.c_str() << "\n" ; //LOG_ERROR_F( ModuleInfo,_T("FieldIndex: Lack of field [%s]."),CString(ele.first.c_str()).GetString() ); } }); return ( false == bFieldsInsufficient); } |
函数FiledIndexLookup用来查找指定表头是否存在于第一行文件中。
写文件的代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 | /** * @brief 计算文件大小 * @author env0y [2015/10/19 17:25:37] * @param [in] fstream& fd,文件描述符 * @param [out] none * @return unsigned __int64,文件大小 单位字节 * @note none */ template < typename FileStream> static inline unsigned __int64 CalcFileSz(FileStream& fd) { ( void )fd.seekg(0,ios::end); streampos uSz = fd.tellg(); ( void )fd.seekg(0,ios::beg); return (unsigned __int64 )uSz; } /** * @brief 写文件 * @author env0y [2015/10/19 9:14:33] * @param [in] const std::string& filename,要生成的文件(绝对路径) [in] T& fn,函数指针/成员函数/函数对象/仿函数/Lambda表达式 [in] int mode = std::ios::out,文件操作模式,默认ios::out [in] const std::string & title = "",生成的文件表头,默认空 * @param [out] none * @return bool * @note fn的参数(wofstream&,...) */ template < typename T> static inline bool WriteFile( const std::string& filename,T& fn, int mode = std::ios::out, const std::string & title = "" ) { ( void )SHCreateDirectoryEx( nullptr , CString(filename.substr(0,filename.find_last_of( "\\" )).c_str()) , nullptr ); //LOG_DEBUG_F( ModuleInfo,_T("output [%s]."),CString(filename.c_str()).GetString()); wofstream ofd(filename,mode); // ofd.imbue(std::locale(std::locale::empty(), new std::codecvt_utf8<wchar_t>)); if ( ofd.fail() ) { //LOG_ERROR_F( ModuleInfo,_T("WriteFile:Open or Create [%s] failure."),CString(filename.c_str()).GetString()); return false ; } if ( title.length() ) { title.back() == '\n' ? ofd << title.c_str() : ofd << title.c_str() << "\n" ; } ( void )fn(ofd); ofd.close(); return true ; } template < typename T, size_t X> static inline bool WriteFile( const std::string(&filename)[X],T& fn, int mode = std::ios::out, const std::string & title = "" ) { std::wofstream arrofds[X]; size_t ulCnt = 0; bool bRst = std::any_of(std::begin(filename),std::end(filename),[&]( const std::string& name)-> bool { ( void )SHCreateDirectoryEx( nullptr , CString(name.substr(0,name.find_last_of( "\\" )).c_str()) , nullptr ); arrofds[ulCnt].open(name,mode); if ( arrofds[ulCnt].fail() ) { LOG_ERROR_F( ModuleInfo,_T( "WriteFile:Open or Create [%s] failure." ),CString(name.c_str()).GetString()); return true ; } arrofds[ulCnt++] << title.c_str(); return false ; }); if (bRst) { return false ; } ( void )fn(arrofds); std::for_each(std::begin(arrofds),std::end(arrofds),[](std::wofstream& fd) { fd.close(); }); return true ; } |
第一个是一次写一个文件,第二个是写多个。
调用举例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | bool COutputPDFGridULSINRFreqBand::LoadCurrentBHGridInfo() { CString CurrentBHGridInfoPath = JoinPath(m_prjPath, CString(_T( "GridLevel\\Total\\CurrentBHGridInfo.csv" ))); m_FileHeadRel.emplace(std::make_pair( "GridID" ,-1)); m_FileHeadRel.emplace(std::make_pair( "Longitude" ,-1)); m_FileHeadRel.emplace(std::make_pair( "Latitude" ,-1)); m_FileHeadRel.emplace(std::make_pair( "Site ID" ,-1)); m_FileHeadRel.emplace(std::make_pair( "Cell ID" ,-1)); m_FileHeadRel.emplace(std::make_pair( "FrequencyBand" ,-1)); m_FileHeadRel.emplace(std::make_pair( "BandNum" ,-1)); m_FileHeadRel.emplace(std::make_pair( "CellULSINR" ,-1)); m_FileHeadRel.emplace(std::make_pair( "CellPrivateUEMRNum" ,-1)); return ReadFile(CString2String(CurrentBHGridInfoPath),std::bind(&COutputPDFGridULSINRFreqBand::ReadLine, this ,std::placeholders::_1),m_FileHeadRel); } |
第二个参数是模板参数,支持lambda表达式、c全局函数、c++成员函数等。
在实际项目中可以把它们封装到一个命名空间,可以更安全方便的使用。 :)
除零保护也是c++经常做的事情,可以提取成如下函数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | /** * @brief 除法保护 * @author env0y * @param [in] Dividend dividend : 分子 [in] Divisor divisor : 分母 * @param [out] none * @return : 如果分母为0(除数为0),返回0; 否则返回正常除数值 */ template < typename Dividend, typename Divisor> static inline double DivisionProtection(Dividend dividend,Divisor divisor) { double dResult(0.0); static const char * Integers[] = { "char" , "unsigned char" , "short" , "unsigned short" , "int" , "unsigned int" , "long" , "unsigned long" , "__int64" , "unsigned __int64" , "long long" , "unsigned long long" }; static const char * Floats[] = { "float" , "double" }; static const int iSz = _countof(Integers); static const int fSz = _countof(Floats); const char * typeDivisor = typeid (divisor).name(); auto fCmp = [&]( const char * str){ return 0 == strcmp (str,typeDivisor);}; if ( (Floats+fSz) != std::find_if(Floats,Floats+fSz,fCmp) ) { bool bIsZero = ( fabs (( double )divisor) < 1e-15 ) || ( fabs (( float )divisor) < 1e-6 ); dResult = bIsZero ? 0.0 : dividend/divisor ; } else if ( (Integers+iSz) != std::find_if(Integers,Integers+iSz,fCmp) ) { dResult = (0 == divisor) ? 0 : 1.0*dividend/divisor; } return dResult; } |
而对浮点型数四舍五入函数则可以使用下面方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | /** * @brief 根据所需精度四舍五入 * @author env0y [2015/8/29] * @param [in] const double& x,原始值 [in] int precision,精度值 * @param [out] none * @return double,四舍五入之后的值 * @note 默认精度为1 */ static inline double RoundByPrecision( const double & x, int precision /* = 1*/ ) { if ( precision < 0 ) { return x; } double temp = x * pow (( double )10, precision); temp = floor (temp + 0.5); return (temp * pow (( double )10, -precision)); } |
我们知道c++11已经有std::hash方法,那如何哈希一个std::pair呢?
可以像下面这样:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | template < class T> inline void hash_combine(std:: size_t & seed, const T & v) { std::hash<T> hasher; seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); } namespace std { template < typename S, typename T> struct hash<pair<S, T>> { inline size_t operator()( const pair<S, T> & v) const { size_t seed = 0; ::hash_combine(seed, v.first); ::hash_combine(seed, v.second); return seed; } };<br><br><br>} |
而像std::wstring和std::string之间的相互转化,则可以:
1 2 3 4 5 6 7 8 9 10 11 | static inline std::string ws2s( const std::wstring& wstr ) { std::wstring_convert<std::codecvt_utf8< wchar_t >, wchar_t > converterX; return converterX.to_bytes(wstr); } static inline std::wstring s2ws( const std::string& str ) { std::wstring_convert<std::codecvt_utf8< wchar_t >, wchar_t > converterX; return converterX.from_bytes(str); } |
写到这里发现,读文件那里用到了RAII,所以把宏定义补充如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class ScopeGuard { public : explicit ScopeGuard(std::function< void ()> onExitScope):onExitScope_(onExitScope){}; ~ScopeGuard(){onExitScope_();}; private : std::function< void ()> onExitScope_; // noncopyable private : ScopeGuard(ScopeGuard const &); ScopeGuard& operator=(ScopeGuard const &); }; /*lint -restore*/ #define SCOPEGUARD_LINENAME_CAT(name, line) name##line #define SCOPEGUARD_LINENAME(name, line) SCOPEGUARD_LINENAME_CAT(name, line) //#define ON_SCOPE_EXIT(callback) ScopeGuard<decltype(callback)> SCOPEGUARD_LINENAME(EXIT, __LINE__)(callback) #define ON_SCOPE_EXIT(callback) ScopeGuard SCOPEGUARD_LINENAME(EXIT, __LINE__)(callback) |
就这些。以后再补充吧!;)
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步