美文网首页
StringBuilder的简单实现

StringBuilder的简单实现

作者: 长不胖的Garfield | 来源:发表于2016-12-23 15:59 被阅读0次

    QString.arg

    在Qt中QString有个arg方法,使用方式如下:

    QString i;           // current file's number
    QString total;       // number of files to process
    QString fileName;    // current file's name
    
    QString status = QString("Processing file %1 of %2: %3")
                    .arg(i).arg(total).arg(fileName);
    

    通过这种方式即可构造出一个字符串:arg(i)替换了%1arg(total)替换了%2arg(fileName)替换了%3

    StringBuilder

    参照QString.arg的接口,实现类似的使用方法:

    StringBuilder("Arg 1:{1};Arg 2:{2};Arg1:{1}").arg(参数).arg(参数2)
    

    实现思路为:

    1. 构造时记录format
    2. 记录参数;
    3. 通过接口返回字符串。

    构造函数

    class StringBuilder
    {
    private:
        std::string m_strFmt;
        std::vector<std::string> args;
    public:
        StringBuilder(const char* strVal):m_strFmt(strVal){};
        StringBuilder(const std::string& strVal):m_strFmt(strVal){};
    }
    

    记录参数

    为了简单起见,当记录参数时直接将其转换为字符串:

    std::vector<std::string> args;
    
    template<typename T>
    StringBuilder& arg(const T& argVal)
    {
        args.emplace_back(std::to_string(argVal));
        return *this;
    }
     
    StringBuilder& arg(const char* argVal)
    {
        args.emplace_back(std::string(argVal));
        return *this;
    }
     
    StringBuilder& arg(const std::string& argVal)
    {
        args.push_back(argVal);
        return *this;
    }
    
    • 通过使用std::to_string和模板来支持各种基础类型向字符串转换;
    • const char*const std::string定制实现;
    • 使用emplace_back避免重复构造std::string
    • 使用return *this返回自身以支持.arg().arg()写法。

    返回字符串

    通过仿函数返回字符串

    //演示实现
    std::string operator()()
    {
        std::string strResult;
        std::size_t offset = 0;
        while (true)
        {
            auto begin = m_strFmt.find_first_of('{',offset);
            auto end = m_strFmt.find_first_of('}',offset);
     
            if (begin >= m_strFmt.npos-1 || end >= m_strFmt.npos-1 || begin >= end)
            {
                break;
            }
     
            //追加结果
            strResult += m_strFmt.substr(offset, begin - offset);
     
            //填充参数
            auto strVal = m_strFmt.substr(begin+1,end-begin-1);
            auto nVal = std::atoi(strVal.c_str());
     
            strResult += args.at(nVal);
     
            offset = end+1;//更新offset
        }
     
        return strResult;
    }
    

    改进

    这样的接口使用还是比较复杂的,譬如:

    StringBuilder("This is a test! {0}{1},{2}").arg(100).arg("$").arg("please!")();
    

    可以使用可变参数模板改造成类似printf的方式:

    //无参实现,终止条件
    static inline void builder(StringBuilder& oBuilder)
    {
        ;
    }
     
    template<typename T, typename... Args>
    static void builder(StringBuilder& oBuilder, T first, const Args&... restArgs)
    {
        oBuilder.arg(first);
        builder(oBuilder, restArgs...);
    }
     
    template<typename... Args>
    static std::string build(const char* strFormat, const Args&... args)
    {
        StringBuilder oBuilder(strFormat);
     
        builder(oBuilder,args...);
        return oBuilder();
    }
    

    这样处理完成后就可以使用如下方式了:

    build("This is a test! {0}{1},{2}",100,"$","please!");
    

    如何实现类似printf的格式化字符串生成?

    实现兼顾易用性、效率、类型安全的printf,具体可以参考fmt,大体的思路为:

    1. 保存参数及类型
    2. 解析格式化字符串
    3. 使用可变参数模板等模板技术提供易用接口

    fmt中,保存参数使用的是一个union,将参数保存为一个列表,供解析格式化字符串使用,由于保存参数的同时也保存了类型,解析规格字符串时可以与类型互相验证,从而保证类型安全;

    相关文章

      网友评论

          本文标题:StringBuilder的简单实现

          本文链接:https://www.haomeiwen.com/subject/jupkvttx.html