• c++常用功能封装


    C++文件读写的封装

    在C++开发中,文件读写是很常用的功能,出于代码复用的考虑,将它们进行封装。

    其中,默认读写的文件是文本文件,并且需要按行处理。调用者只需要关注读取出来的每一行的处理方式。写文件只是简单的开关文件流。

    具体代码如下:

     /**
        * @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() << "
    ";
    				//LOG_ERROR_F( ModuleInfo,_T("FieldIndex: Lack of field [%s]."),CString(ele.first.c_str()).GetString() );
    			}
    		});
    		return (false == bFieldsInsufficient);
    	}
    

      函数FiledIndexLookup用来查找指定表头是否存在于第一行文件中。

    写文件的代码如下:

    /**
    	* @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() == '
    ' ?	ofd << title.c_str() :	ofd << title.c_str() << "
    ";		 
    		}
    
    		(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;
    	}
    

      第一个是一次写一个文件,第二个是写多个。

    调用举例如下:

    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++经常做的事情,可以提取成如下函数:

    /**
    	* @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;
    	}
    

      

    而对浮点型数四舍五入函数则可以使用下面方法:

    /**
    	* @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呢?

    可以像下面这样:

    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;
    		}
    	};


    }

    而像std::wstring和std::string之间的相互转化,则可以:

    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,所以把宏定义补充如下:

    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)
    

      

    就这些。以后再补充吧!;)

  • 相关阅读:
    hdu4277 暴力
    hdu4271 Find Black Hand 2012长春网络赛E题 最短编辑距离
    poj3356 字符串的最小编辑距离 dp
    HDU4267 A Simple Problem with Integers 线段树/树状数组
    树链剖分 模版
    SPOJ375 Query on a tree 树链剖分
    Orz_panda cup I题 (xdoj1117) 状压dp
    27号的十道离线线段树
    The 2015 "Orz Panda" Cup Programming Contest
    codeforces #274 C. Riding in a Lift dp+前缀和优化
  • 原文地址:https://www.cnblogs.com/envoy/p/8494226.html
Copyright © 2020-2023  润新知