简单的类型安全format输出

原创 2005年03月03日 12:53:00
boost就有一个实现类型安全format的类,不过类比较庞大,而且也比较复杂,用起来也不是很习惯。
所以还是自己简单实现一个了。
    先看看需求:format_string.format("[%] = %") % a % strName
    其实就是希望后面的a和strName分别替代格式化字符串"[%] = %"中的两个%号,相当于:
    stringstream format_string;
    format_string << "[" << a << "] = " << strName;

    当然,要做到通用,还是希望不单能输出到stringstream,最好还是能输出到std::ostream。
    class format_stream
    {
    public:
        explicit format_stream(std::ostream & outS) : m_output(outS),m_lpszFormat(&g_nEndFlags)
        {
        }

        inline format_stream & format(const char * lpszFormat)
        {
            flushFormat();
            m_lpszFormat = lpszFormat;
            return outputPrefix();
       }
       
        template<typename typeArg1>
        inline format_stream & arg(const typeArg1 & val) {
                getOutput() << val;
                return outputPrefix();
        }

       ~format_stream(void) {}
    protected:
        inline std::ostream & getOutput(void)
        {
                return m_output;
        }
        void    flushFormat(void)
        {
                if (*m_lpszFormat)
                {
                        getOutput() << m_lpszFormat;
                        m_lpszFormat = &g_nEndFlags;
                }      
        }
        format_stream & outputPrefixLoop(void);
        format_stream & outputPrefix(void);
    
        static  char    g_nEndFlags;
        std::ostream &  m_output;
        const char *    m_lpszFormat;
    };
       
    char format_stream::g_nEndFlags = char();

    format_stream & format_stream::outputPrefix(void)
    {
        char * lpPos = strchr(m_lpszFormat,'%');
        if (lpPos != NULL)
        {
                getOutput().write(m_lpszFormat, lpPos - m_lpszFormat);
                m_lpszFormat = lpPos + 1;
                if (*m_lpszFormat == '%' && *(m_lpszFormat + 1) != '%')
                        return outputPrefixLoop();
        } // if (lpPos != NULL)
        else
                flushFormat();
        return *this;
    }

    format_stream & format_stream::outputPrefixLoop(void)  
    {
        while (*m_lpszFormat == '%')
        {
                char * lpPos = strchr(m_lpszFormat + 1, '%');
                if (lpPos != NULL)
                {
                        getOutput().write(m_lpszFormat, lpPos - m_lpszFormat);
                        m_lpszFormat = lpPos + 1;
                        if (*m_lpszFormat != '%' || *(m_lpszFormat + 1) == '%')
                                break;
                } // if (lpPos != NULL)
                else
                {
                        flushFormat();
                        break;
                }
        } // while (*m_lpszFormat)
        return *this;
    }

    1、规定以'%'作为占位符,表示后续的变量替换的位置。
       
    2、两个连续'%'即 '%%'表示一个真正的'%'。不过需要注意的是:一般想输出百分数的时候,就是要写%%%,
分析程序发现三个连续'%',则认为第一个是占位符,后两个表示一个'%'。而发现四个'%'的时候,前两个都会被认
为是占位符,最后两个被认为是一个'%'。

    3、boost用%连接后面的多个变量,也有些类库使用逗号。个人都不是很喜欢:'%'用得太多,程序看起来不好
看;很多书都再三声明最好不要重载逗号运算符。所以还是使用函数比较稳妥,所以就用函数arg(a)的形式。如果真
的喜欢使用'%'或者逗号,只需要增加成员函数:
        template<typename typeArg1>
            inline format_stream & operator%(const typeArg1 & val) { return arg(val); }
        template<typename typeArg1>
            inline format_stream & operator,(const typeArg1 & val) { return arg(val); }
               
    4、arg还可以继续扩展,
        a、同一个占位符输出多个变量,只需要增加多几个成员函数:
        template<typename typeArg1,typename typeArg2>
        inline format_stream & arg(const typeArg1 & val1,const typeArg2 & val2)
        {
                getOutput() << val << val2;
                return outputPrefix();
        }
        template<typename typeArg1,typename typeArg2,typename typeArg3>
        inline format_stream & arg(const typeArg1 & val1,
                                const typeArg2 & val2,
                                const typeArg3 & val3)
        {
                getOutput() << val1 << val2 << val3;
                return outputPrefix();
        }
        例如有时候想输出一个范围:
       
        stream.format("range1:% range2:%").arg(lowerbound1,'-',upperbound1);
        stream.arg(lowerbound2,'~',upperbound2)
       
        b、格式化输出。printf那么多的格式化输出参数写在格式化字符串中,我老是会记错,一旦写错
        程序就容易出问题了(不单是显示错误,还有可能会coredump),另外发现不同平台格式还会有
        些不一样。还是写在arg里面比较稳妥,而且程序也容易阅读。
       
        为了和上面"同一个占位符输出多个变量"的函数区分,还是另外取一个函数名:
       
        enum    INT_BASE
                {BASE10=std::ios::dec, BASE8=std::ios::oct, BASE16=std::ios::hex};
        enum    CHAR_CASE
                {CHAR_UPCASE=0, CHAR_LOWCASE=1 };
        enum    BASE_FLAG
                {HIDE_BASE=0, SHOW_BASE=1 };
        enum    POS_FLAG
                {HIDE_POS=0, SHOW_POS=1 };
        enum    FIX_FLAG
                { FIXED=0, SCIENTIFIC=1 };
        enum    POINT_FLAG
                { HIDE_POINT=0, SHOW_POINT=1 };
        enum    ADJUSTFIELD_FLAG
        {
                ADJUST_LEFT=std::ios::left,
                ADJUST_RIGHT=std::ios::right,
                ADJUST_INTERNAL=std::ios::internal
        };

        template<typename typeInt>
                format_stream & argWithFormat(typeInt nVal,
                        INT_BASE nBase = BASE10,
                        CHAR_CASE bUpcase = CHAR_UPCASE,
                        POS_FLAG bShowPos = HIDE_POS,
                        BASE_FLAG bShowBase = HIDE_BASE,
                        ADJUSTFIELD_FLAG nAdjust= ADJUST_LEFT,
                        int nWidth = -1,char chFill = ' ')
        {
                std::ios::fmtflags nFlags = getOutput().flags();
                getOutput().setf((std::ios::fmtflags)nBase, std::ios::basefield);
                if (bShowPos == SHOW_POS)
                        getOutput().setf(std::ios::showpos);
                else
                        getOutput().unsetf(std::ios::showpos);
                if (bUpcase == CHAR_UPCASE)
                        getOutput().setf(std::ios::uppercase);
                else
                        getOutput().unsetf(std::ios::uppercase);
                if (bShowBase == SHOW_BASE)
                        getOutput().setf(std::ios::showbase);
                else
                        getOutput().unsetf(std::ios::showbase);
                getOutput().setf((std::ios::fmtflags)nAdjust, std::ios::adjustfield);
                if (nWidth != -1)
                        nWidth = getOutput().width(nWidth);
                chFill = getOutput().fill(chFill);

                getOutput() << static_cast<typeInt>(nVal);

                getOutput().flags(nFlags);
                if (nWidth != -1)
                        getOutput().width(nWidth);
                getOutput().fill(chFill);

                return outputPrefix();
        }
        还可以增加浮点数、字符串等等的格式处理。
               
    5、现在输入的格式字符串,在类里面只是使用const char * m_lpszFormat来保存,一旦传入的
lpszFormat所指的资源已经被释放,则会造成非法内存访问。例如:
    std::string     getString(void) { ..... }
    stream.format(getString().c_str());
    stream.arg(...);
    如果要避免这种情况,应该在format_stream里面增加一个std::string成员,记录字符串指针。
又或者应该写成:
    std::string strTemp = getString();
    stream.format(strTemp.c_str());
    stream.arg(...)
应该如何处理,还是看各人的习惯和需求了。
       
    6、当输入参数比占位符多的时候,则多余的参数都会输出到格式字符串的后面。
    7、当输入参数比占位符少的时候,输出则停留在第一个多余的占位符的前面。
    8、对于字符串,可以使用stringstream来辅助:
    stringstream str;
    format_stream fmt(str);
    fmt.format(".....").arg(...);
    myFun(str.str());
    当然,经过适当的改造,可以使类更方便使用,这里就不再多说,各位高手自己发挥了。

版权声明:本文为博主原创文章,未经博主允许不得转载。

相关文章推荐

关于格式化输出各类型的说明(format、printf)

printf的格式控制的完整格式: % - 0 m.n l或h 格式字符 下面对组成格式说明的各项加以说明: ①%:表示格式说明的起始符号,不可缺少。 ②-:有-表示左对齐输出,如省略表示右对...

Android自定义View的属性,attr format取值类型

原文链接 1. reference:参考某一资源ID。 (1)属性定义: declare-styleable name = "名称"> "background" format = "re...

attr format取值类型

为以后查找方便,这里直接引用网上的:地址 1. reference:参考某一资源ID。 (1)属性定义: declare-styleable name = "名称"> attr name = "...

如何为自定义属性指定format类型

如何为自定义属性指定format类型呢? 确实,在自定义组件时往往要为其提供一些属性,然后在布局文件或代码里根据自定义组件的属性格式类型设置初始值。起初,寄希望于在官方文档中寻找关于format支持哪...

用string.format()解决可空类型变量的格式化

故事的起因总是那么的突然,这不,人事系统又要改需求了,这次是要把人事代理合同书下面的甲方签字的时间改为档案的转入时间,这么小的一个问题处理起来应该相当的容易吧。         但是看到代码...

Android自定义属性,attr format取值类型

1. reference:参考某一资源ID。      (1)属性定义:                 ...

Android自定义attr中format取值类型

1. reference:参考某一资源ID。     (1)属性定义:                ...

Android自定义属性,attr format取值类型

原文链接:http://huangbo-2020.iteye.com/blog/1477611 1. reference:参考某一资源ID。      (1)属性定义:       &#...
  • zh_m8
  • zh_m8
  • 2012-08-30 21:40
  • 316

declare-styleable:自定义控件的属性,android自定义属性 format类型 .

做Android布局是件很享受的事,这得益于他良好的xml方式。使用xml可以快速有效的为软件定义界面。可是有时候我们总感觉官方定义的一些基本组件不够用,自定义组件就不可避免了。那么如何才能做到像官方...
  • ComTu
  • ComTu
  • 2014-04-24 16:29
  • 350
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)