zoukankan      html  css  js  c++  java
  • (原创)实现一个简单的字符串格式化方法

    c#中格式化字符串很简单,比如我们可以这样格式化一个字符串:

    string str = string.format("test {0}, {1}, {2}, {1}, {0} sample", 1, 2.3, "ok");
    Console.WriteLine(str);

    将输出:test 1, 2.3, ok, 2.3, 1 sample

    这个格式化方法用起来很简单,支持基本类型的参数,比如int、double和string等,用起来很方便。遗憾的是c++中目前还没有类似的格式化方法。boost库提供了一个format方法,但用起来没有c#的format方法简单和灵活。让我们来看看boost.format如何实现上面的格式化:

    string str = boost::format("test %1%, %2%, %3%, %4%, %5% sample")%1%2.3%"ok"%2.3%1;
    cout<<str<<endl;

      boost::format的问题是需要写很多%,用起来繁琐又不直观。c++还缺少一个类似于c#的format方法,要实现一个类似的简单的format也不难,我将实现一个简单的format,基本用法和c#一致,为了保持简单,不支持复杂的功能,只支持基本类型的转换,转换的格式控制就不支持了,简单够用就好。下面来看看format的具体实现吧:

    #include <tuple>
    #include <type_traits>
    #include <string>
    #include<cctype> using namespace std; #include "Variant.hpp" namespace detail { using Value = Variant<uint8_t, uint16_t, uint32_t, uint64_t, int8_t, int16_t, int32_t, int64_t, float, double, string, char*, const char*>; char g_buf[2000] = {}; template<size_t k, typename Tuple, typename F> typename std::enable_if < (k == std::tuple_size<Tuple>::value)>::type GetArgByIndex(size_t, Tuple&, F&, char*&) { throw std::invalid_argument("arg index out of range"); } template<size_t k = 0, typename Tuple, typename F> typename std::enable_if < (k < std::tuple_size<Tuple>::value)>::type GetArgByIndex(size_t index, Tuple& tp, F& f, char*& p) { if (k == index) { f(p, std::get<k>(tp)); } else { GetArgByIndex<k + 1>(index, tp, f, p); } } inline int GetIndex(char*& p) { char temp[3] = {}; int i = 0; while (*p != '}'&&*p != '') { if (i >= 2) throw std::invalid_argument("index is out of range."); if (std::isdigit(*p)) { //push digit temp[i++] = *p; char next = *(p + 1); if (std::isdigit(next)) { temp[i++] = next; p += 2; continue; } //validate arg if (!std::isspace(next) && next != '}') { throw std::invalid_argument("invalid argument."); } } p++; } return i == 0 ? -1 : std::atoi(temp); } inline void Fun(char*& buf, Value t) { t.Visit([&buf](int i) { _itoa(i, buf, 10);
           buf += strlen(buf);
    }, [&buf](double i) { auto r = sprintf(buf, "%.6f", i); buf += r; }, [&buf](int64_t i) { auto r = sprintf(buf, "%"PRId64, i); buf += r; }, [&buf](uint64_t i) { auto r = sprintf(buf, "%"PRIu64, i); buf += r; }, [&buf](const char* p) { int len = strlen(p); memcpy(buf, p, len); buf += len; }, [&buf](string& s) { memcpy(buf, s.data(), s.size()); buf += s.size(); }); } } template<typename... Args> inline string format(string& str, Args... args) { using namespace detail; char* buf = g_buf; auto tp = std::tuple<Args...>(args...); char* p = (char*) str.c_str(); char* original = p; int len = str.size() + 1; int last = 0; while (true) { if (*p == '{') { //copy content befor { last = p - original; memcpy(buf, original, last); buf += last; //format args int index = GetIndex(p); if (index >= 0) { GetArgByIndex<0>(index, tp, Fun, buf); } //skip } original = p + 1; }
         else if(*p=='')
    {        
          last = p - original;
          memcpy(buf, original, last);
          break;
         }  
    
            p++;
        }
    
        string s = g_buf;
        memset(g_buf, 0, buf - g_buf);
        return s;
    }

    再来看看测试代码:

    string str = "it is { 0 }, and {01}, {2}, {01}, {1}";
    cout<<format(str, 11, 2.1, "tt")<<endl;

    将输出:it is 11, and 2.1, tt, 2.1 1

      用法和c#的一致,比boost的format用起来更方便。这个format支持最多100个参数(0-99),格式化的最长的字符串为2K。

    /**********************更新,做简化和修改bug*******************************/

    #include <tuple>
    #include <type_traits>
    #include <string>
    #include <inttypes.h>
    #include <cctype> using namespace std; namespace detail { char g_buf[2000] = {}; inline void FormatArg(char*& buf, int i) { _itoa(i, buf, 10); buf += strlen(buf); } inline void FormatArg(char*& buf, double i) { auto r = sprintf(buf, "%.6f", i); buf += r; } inline void FormatArg(char*& buf, int64_t i) { auto r = sprintf(buf, "%"PRId64, i); buf += r; } inline void FormatArg(char*& buf, uint64_t i) { auto r = sprintf(buf, "%"PRIu64, i); buf += r; } inline void FormatArg(char*& buf, const char* p) { int len = strlen(p); memcpy(buf, p, len); buf += len; } inline void FormatArg(char*& buf, string& s) { memcpy(buf, s.data(), s.size()); buf += s.size(); } template<size_t k, typename Tuple> typename std::enable_if < (k == std::tuple_size<Tuple>::value)>::type inline GetArgByIndex(size_t, Tuple&, char*&) { throw std::invalid_argument("arg index out of range"); } template<size_t k = 0, typename Tuple> typename std::enable_if < (k < std::tuple_size<Tuple>::value)>::type inline GetArgByIndex(size_t index, Tuple& tp, char*& p) { if (k == index) { FormatArg(p, std::get<k>(tp)); } else { GetArgByIndex<k + 1>(index, tp, p); } } inline int GetIndex(char*& p) { char temp[3] = {}; int i = 0; while (*p != '}'&&*p != '') { if (i >= 2) throw std::invalid_argument("index is out of range."); if (std::isdigit(*p)) { //push digit temp[i++] = *p; char next = *(p + 1); if (std::isdigit(next)) { temp[i++] = next; p += 2; continue; } //validate arg if (!std::isspace(next) && next != '}') { throw std::invalid_argument("invalid argument."); } } p++; } return i == 0 ? -1 : std::atoi(temp); } } template<typename... Args> inline string format(const string& src, Args... args) { return format((char*) src.c_str(), args...); } template<typename... Args> inline string format(char* src, Args... args) { using namespace detail; char* buf = g_buf; auto tp = std::tuple<Args...>(args...); char* p = src; char* original = p; int len = strlen(src) + 1; int last = 0;
      while (true)
        {
            if (*p == '{')
            {
                //copy content befor {
                last = p - original;
                memcpy(buf, original, last);
                buf += last;
    
                //format args
                int index = GetIndex(p);
                if (index >= 0)
                {
                    GetArgByIndex<0>(index, tp, Fun, buf);
                }
    
                //skip }
                original = p + 1;
            }
         else if(*p=='')
    {        
    
    
          last = p - original;
    
    
          memcpy(buf, original, last);
          break;
         }  
    
            p++;
        }
      string s = g_buf; 
      memset(g_buf,
    0, buf - g_buf);
      return s;
    }

    简化代码的实现,移除对Variant的依赖,直接通过重载函数来格式化参数。经简单测试效率比boost::format要高六倍以上。

    如果你发现还有更好的format请告诉我,如果没有请点一下推荐,谢谢。^_^

    c++11 boost技术交流群:296561497,欢迎大家来交流技术。

  • 相关阅读:
    【学车笔记】皮卡科目二考前笔记
    【Java学习笔记】继承和多态
    【Java学习笔记】Java中定义宏
    【读书笔记】《世界上最伟大的推销员》
    《大话设计模式》重印公告
    《大话设计模式》第29章OOTV杯超级模式大赛—模式总结(四)
    《大话设计模式》第29章OOTV杯超级模式大赛—模式总结(六)
    《大话设计模式》第29章OOTV杯超级模式大赛—模式总结(五)
    阅读不懂,图书之过——《大话设计模式》创作历程
    岁月凶残,敬请珍惜——得知早已不能过五四节时之随想
  • 原文地址:https://www.cnblogs.com/qicosmos/p/3825612.html
Copyright © 2011-2022 走看看