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)

  

 

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

 

posted @   envoy  阅读(1112)  评论(0编辑  收藏  举报
努力加载评论中...
点击右上角即可分享
微信分享提示