How to easily indent output to ofstream?

前端 未结 7 1467
挽巷
挽巷 2020-12-09 04:34

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

7条回答
  •  庸人自扰
    2020-12-09 05:02

    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\nLine 2\nLine 3\n";
    
        /* You must imbue a file stream before it is opened. */
        std::ofstream       data;
        data.imbue(indentLocale);
        data.open("PLOP");
    
        data << "Loki\nUses Locale\nTo do something silly\n";
    }
    

    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 
    #include 
    #include 
    #include 
    
    class IndentFacet: public std::codecvt
    {
      public:
       explicit IndentFacet(size_t ref = 0): std::codecvt(ref)    {}
    
        typedef std::codecvt_base::result               result;
        typedef std::codecvt  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(&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 != '\n'))
                {
                    res                 = std::codecvt_base::ok;
                    state(tabNeeded)    = 1;
                    *wStart             = '\t';
                    ++wStart;
                    if (wStart == wEnd)
                    {
                        res     = std::codecvt_base::partial;
                        break;
                    }
                }
                // Copy the next character.
                *wStart         = *rStart;
    
                // If the character copied was a '\n' mark that state
                if (*rStart == '\n')
                {
                    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

提交回复
热议问题