|簡體中文

比思論壇

 找回密碼
 按這成為會員
搜索



查看: 471|回復: 0
打印 上一主題 下一主題

(原创)实现一个简单的字符串格式化方法

[複製鏈接]

56

主題

0

好友

490

積分

中學生

Rank: 3Rank: 3

  • TA的每日心情

    6 天前
  • 簽到天數: 204 天

    [LV.7]常住居民III

    推廣值
    0
    貢獻值
    0
    金錢
    156
    威望
    490
    主題
    56
    樓主
    發表於 2014-7-18 20:53:12
    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的具体实现吧:
    [url=][/url]
    #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 != '\0')        {            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 (*p != '\0')    {        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;        }        p++;    }    string s = g_buf;    memset(g_buf, 0, buf - g_buf);    return s;}[url=][/url]

    再来看看测试代码:
    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*******************************/
    [url=][/url]
    #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 != '\0')        {            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 (*p != '\0')    {        if (*p == '{')        {            //copy content befor {            last = p - original;            memcpy(buf, original, last);            buf += last;            //format args            int index = GetIndex(p);            if (index >= 0)            {                detail::GetArgByIndex<0>(index, tp, buf);            }            //skip }            original = p + 1;        }        p++;    }    string s = g_buf;    memset(g_buf, 0, buf - g_buf);    return s;}[url=][/url]

    简化代码的实现,移除对Variant的依赖,直接通过重载函数来格式化参数。经简单测试效率比boost::format要高六倍以上。
    如果你发现还有更好的format请告诉我,如果没有请点一下推荐,谢谢。^_^

    重要聲明:本論壇是以即時上載留言的方式運作,比思論壇對所有留言的真實性、完整性及立場等,不負任何法律責任。而一切留言之言論只代表留言者個人意見,並非本網站之立場,讀者及用戶不應信賴內容,並應自行判斷內容之真實性。於有關情形下,讀者及用戶應尋求專業意見(如涉及醫療、法律或投資等問題)。 由於本論壇受到「即時上載留言」運作方式所規限,故不能完全監察所有留言,若讀者及用戶發現有留言出現問題,請聯絡我們比思論壇有權刪除任何留言及拒絕任何人士上載留言 (刪除前或不會作事先警告及通知 ),同時亦有不刪除留言的權利,如有任何爭議,管理員擁有最終的詮釋權。用戶切勿撰寫粗言穢語、誹謗、渲染色情暴力或人身攻擊的言論,敬請自律。本網站保留一切法律權利。

    手機版| 廣告聯繫

    GMT+8, 2024-11-1 07:32 , Processed in 0.018037 second(s), 25 queries , Gzip On.

    Powered by Discuz! X2.5

    © 2001-2012 Comsenz Inc.

    回頂部