如何轻松地将输出缩进到ofstream?

2022-01-07 00:00:00 stream c++ ofstream

是否有一种简单的方法可以将输出缩进到 ofstream 对象?我有一个空终止并包含换行符的 C++ 字符数组.我想将其输出到流中,但每行缩进两个空格.是否有一种简单的方法可以使用流操纵器来执行此操作,例如您可以使用流的特殊指令更改整数输出的基数,或者我是否必须手动处理数组并在检测到的每个换行符处手动插入额外的空格?

似乎 string::right() 操纵器很接近:

http://www.cplusplus.com/reference/iostream/manipulators/right/

谢谢.

-威廉

解决方案

这是使用构面的完美情况.

可以将 codecvt 方面的自定义版本注入到流中.

所以你的用法应该是这样的:

int main(){/* 在使用 std::cout 之前注入它 */std::ios::sync_with_stdio(false);std::cout.imbue(std::locale(std::locale::classic(), new IndentFacet()));std::cout <<"第一行
第二行
第三行
";/* 您必须在打开文件流之前注入它.*/std::ofstream 数据;data.imbue(indentLocale);data.open("PLOP");数据<<"Loki
使用语言环境
做一些愚蠢的事情
";}

facet 的定义有点复杂.
但重点是使用 facet 的人不需要了解有关格式的任何信息.格式的应用与流的使用方式无关.

#include #include <算法>#include #include <fstream>类 IndentFacet:公共 std::codecvt{上市:显式 IndentFacet(size_t ref = 0): std::codecvt(ref) {}typedef std::codecvt_base::result 结果;typedef std::codecvt父母;typedef parent::intern_type intern_type;typedef parent::extern_type extern_type;typedef parent::state_type state_type;内部&状态(state_type&s) const {return *reinterpret_cast<int*>(&s);}受保护:虚拟结果 do_out(state_type& tabNeeded,const intern_type* rStart, const intern_type* rEnd, const intern_type*&rNewStart,extern_type* wStart, extern_type* wEnd, extern_type*&wNewStart) 常量{结果 res = std::codecvt_base::noconv;for(;(rStart < rEnd) && (wStart < wEnd);++rStart,++wStart){//0 表示看到的最后一个字符是换行符.//因此我们将在它之前打印一个选项卡.忽略它下一个//字符也是换行符if ((state(tabNeeded) == 0) && (*rStart != '
')){res = std::codecvt_base::ok;状态(tabNeeded)= 1;*wStart = '	';++w开始;如果(wStart == wEnd){res = std::codecvt_base::partial;休息;}}//复制下一个字符.*wStart = *rStart;//如果复制的字符是 '
' 标记该状态如果 (*rStart == '
'){状态(tabNeeded)= 0;}}如果(rStart != rEnd){res = std::codecvt_base::partial;}rNewStart = rStart;wNewStart = wStart;返回资源;}//覆盖所以 do_out() 虚函数被调用.virtual bool do_always_noconv() const throw(){返回假;//有时我们会添加额外的标签}};

请参阅:Tom 下面的笔记

Is there an easy way to indent the output going to an ofstream object? I have a C++ character array that is null terminate and includes newlines. I'd like to output this to the stream but indent each line with two spaces. Is there an easy way to do this with the stream manipulators like you can change the base for integer output with special directives to the stream or do I have to manually process the array and insert the extra spaces manually at each line break detected?

Seems like the string::right() manipulator is close:

http://www.cplusplus.com/reference/iostream/manipulators/right/

Thanks.

-William

解决方案

This is the perfect situation to use a facet.

A custom version of the codecvt facet can be imbued onto a stream.

So your usage would look like this:

int main()
{
    /* Imbue std::cout before it is used */
    std::ios::sync_with_stdio(false);
    std::cout.imbue(std::locale(std::locale::classic(), new IndentFacet()));

    std::cout << "Line 1
Line 2
Line 3
";

    /* You must imbue a file stream before it is opened. */
    std::ofstream       data;
    data.imbue(indentLocale);
    data.open("PLOP");

    data << "Loki
Uses Locale
To do something silly
";
}

The definition of the facet is slightly complex.
But the whole point is that somebody using the facet does not need to know anything about the formatting. The formatting is applied independent of how the stream is being used.

#include <locale>
#include <algorithm>
#include <iostream>
#include <fstream>

class IndentFacet: public std::codecvt<char,char,std::mbstate_t>
{
  public:
   explicit IndentFacet(size_t ref = 0): std::codecvt<char,char,std::mbstate_t>(ref)    {}

    typedef std::codecvt_base::result               result;
    typedef std::codecvt<char,char,std::mbstate_t>  parent;
    typedef parent::intern_type                     intern_type;
    typedef parent::extern_type                     extern_type;
    typedef parent::state_type                      state_type;

    int&    state(state_type& s) const          {return *reinterpret_cast<int*>(&s);}
  protected:
    virtual result do_out(state_type& tabNeeded,
                         const intern_type* rStart, const intern_type*  rEnd, const intern_type*&   rNewStart,
                         extern_type*       wStart, extern_type*        wEnd, extern_type*&         wNewStart) const
    {
        result  res = std::codecvt_base::noconv;

        for(;(rStart < rEnd) && (wStart < wEnd);++rStart,++wStart)
        {
            // 0 indicates that the last character seen was a newline.
            // thus we will print a tab before it. Ignore it the next
            // character is also a newline
            if ((state(tabNeeded) == 0) && (*rStart != '
'))
            {
                res                 = std::codecvt_base::ok;
                state(tabNeeded)    = 1;
                *wStart             = '	';
                ++wStart;
                if (wStart == wEnd)
                {
                    res     = std::codecvt_base::partial;
                    break;
                }
            }
            // Copy the next character.
            *wStart         = *rStart;

            // If the character copied was a '
' mark that state
            if (*rStart == '
')
            {
                state(tabNeeded)    = 0;
            }
        }

        if (rStart != rEnd)
        {
            res = std::codecvt_base::partial;
        }
        rNewStart   = rStart;
        wNewStart   = wStart;

        return res;
    }

    // Override so the do_out() virtual function is called.
    virtual bool do_always_noconv() const throw()
    {
        return false;   // Sometime we add extra tabs
    }

};

See: Tom's notes below

相关文章