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() << "\n";
//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() == '\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;
}
第一个是一次写一个文件,第二个是写多个。
调用举例如下:
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)
就这些。以后再补充吧!;)
c++常用功能封装的更多相关文章
- js实现第一次打开网页弹出指定窗口(常用功能封装很好用)
js实现第一次打开网页弹出指定窗口(常用功能封装很好用) 一.总结 1.常用功能封装:之前封装的cookie的操作函数非常好用,我自己也可以这么搞 二.js实现第一次打开网页弹出指定窗口 练习1:第一 ...
- cocos2D-X LUA 常用功能封装和工作经验的一些解决方案
--[[ Packaging_KernelEngine.h 文件说明:所有对象在建立时位置是优先的,传入位置参数必须cc.p(X,Y) CurObj:表示要传入当前的对象 将3.10 lua api ...
- cocos2D-X 常用功能封装
Packaging_Kernel.h #pragma once #include <string> #include <map> #include <vector> ...
- echarts常用功能封装|抽象为mixin
目前已解锁以下功能: [x] 初始化echarts(initChart) [x] 获取echarts参数配置(getOption) [x] 生成echarts图表(setOption) [x] 监听r ...
- php一些常用功能封装
//二分查找 function bin_sch($array, $low, $high, $k) { if ($low <= $high) { $mid = intval(($low + $hi ...
- EXCEL2016 OLE/COM开发-常用功能封装代码
hpp #pragma once #include "stdafx.h" #include "CApplication.h" #include "CW ...
- JavaScript 常用功能总结
小编吐血整理加上翻译,太辛苦了~求赞! 本文主要总结了JavaScript 常用功能总结,如一些常用的JS 对象,基本数据结构,功能函数等,还有一些常用的设计模式. 目录: 众所周知,JavaScri ...
- Android android-common 常用功能和工具集合
本文内容 环境 android-common 项目结构 演示 android-common 参考资料 android-common 主要包括如下内容: 缓存,包括图片缓存.预取缓存.网络缓存. 公共 ...
- 开源三维地球GIS引擎Cesium常用功能的开发
Cesium是一个非常优秀的三维地球GIS引擎(开源且免费).能够加载各种符合标准的地图图层,瓦片图.矢量图等都支持.支持3DMax等建模软件生成的obj文件,支持通用的GIS计算:支持DEM高程图. ...
随机推荐
- BZOJ 1705: [Usaco2007 Nov]Telephone Wire 架设电话线 DP + 优化 + 推导
Description 最近,Farmer John的奶牛们越来越不满于牛棚里一塌糊涂的电话服务 于是,她们要求FJ把那些老旧的电话线换成性能更好的新电话线. 新的电话线架设在已有的N(2 <= ...
- bzoj 4994: [Usaco2017 Feb]Why Did the Cow Cross the Road III 树状数组_排序
Description 给定长度为2N的序列,1~N各处现过2次,i第一次出现位置记为ai,第二次记为bi,求满足ai<aj<bi<bj的对数 题解: 方法一: 搞一个KDtree, ...
- 09.正则表达式re-3.常用的匹配规则
模式 描述 \w 匹配字母.数字及下划线 \W 匹配不是字母.数字及下划线的字符 \s 匹配任意空白字符,等价于[\t\n\r\f] \S 匹配任意非空字符 \d 匹配任意数字,等价于[0-9] \D ...
- 33.bulk json格式的理解
bulk json格式的理解 一.常规格式 按常规理解,bulk中json格式可以是以下方式 [{ "action": { }, "data": { } }] ...
- Nginx面试中最常见的18道题 抱佛脚必备
Nginx的并发能力在同类型网页服务器中的表现,相对而言是比较好的,因此受到了很多企业的青睐,我国使用Nginx网站的知名用户包括腾讯.淘宝.百度.京东.新浪.网易等等.Nginx是网页服务器运维人员 ...
- js获取URL参数的函数
function GetQueryString(name) { var reg = new RegExp("(^|&)" + name + "=([^&] ...
- RubyMine快捷键
RubyMine快捷键 ctrl+shift+up/down 向上/向下移动代码段 alt+shift+up/down 向上/向下移动代码行 ctrl+y 删除一行 ctrl+shift+f10 运行 ...
- [寒江孤叶丶的Cocos2d-x之旅_32]微信输入框风格的IOS平台的EditBox
原创文章,欢迎转载.转载请注明:文章来自[寒江孤叶丶的Cocos2d-x之旅系列] 博客地址:http://blog.csdn.net/qq446569365 偶然看到一个游戏注冊账号时候,输入框是弹 ...
- Web端即时通讯、消息推送的实现
https://blog.csdn.net/DeepLies/article/details/77726823
- mac 下作流程图工具omnigraffle
omnigraffle:http://www.uzzf.com/soft/91710.html 含盖激活码: Name: mojado Serial: JYFE-JRJN-GSOT-GRAG-EVJI ...