17

ofstreamオブジェクトに送られる出力をインデントする簡単な方法はありますか?nullで終了し、改行を含むC++文字配列があります。これをストリームに出力したいのですが、各行を2つのスペースでインデントします。ストリームへの特別なディレクティブを使用して整数出力のベースを変更できるように、ストリームマニピュレーターでこれを行う簡単な方法はありますか、または配列を手動で処理し、検出された改行ごとに手動で余分なスペースを挿入する必要がありますか?

string :: right()マニピュレーターが近いようです:

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

ありがとう。

-ウィリアム

4

7 に答える 7

21

これは、ファセットを使用するのに最適な状況です。

codecvtファセットのカスタムバージョンをストリームに組み込むことができます。

したがって、使用法は次のようになります。

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";
}

ファセットの定義は少し複雑です。
しかし、要点は、ファセットを使用している人はフォーマットについて何も知る必要がないということです。書式設定は、ストリームの使用方法に関係なく適用されます。

#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 != '\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
    }

};

参照:以下のトムのメモ

于 2009-09-09T04:52:42.567 に答える
2

このような機能を追加する方法は、フィルター操作の一部としてインデントを追加するフィルターストリームバフ(つまり、IO操作を別のストリームバフに転送するが転送されたデータを操作するストリームバフ)を作成することです。ここでstreambufを作成する例を示しましたが、boostはそのためのライブラリを提供します。

この場合、overflow()メンバーは単に'\ n'をテストし、必要に応じて直後にインデントを追加します(streambufのメンバーになることをindentedOuput除いて、関数で行ったこととまったく同じです)。newlineインデントサイズを増減する設定がある可能性があります(おそらくマニピュレータを介してアクセスできます。マニピュレータは、ストリームに関連付けられたstreambufが正しいタイプであることを確認するために、dynamic_castを実行する必要があります。ユーザーを追加するメカニズムがあります。ストリーミングするデータ(basic_ios :: xalloc、iword、pword)ですが、ここではstreambufを操作します)。

于 2009-09-08T06:53:35.973 に答える
2

これは私が探している答えではありませんが、そのような答えがない場合は、これを手動で行う方法があります。

void
indentedOutput(ostream &outStream, const char *message, bool &newline)
{
  while (char cur = *message) {
    if (newline) {
      outStream << "  ";
      newline = false;
    }
    outStream << cur;
    if (cur == '\n') {
      newline = true;
    }
    ++message;
  }
}
于 2009-09-08T03:26:18.900 に答える
2

Martinのcodecvtファセットベースの提案で成功しましたが、OSXのstd :: coutでの使用に問題がありました。これは、デフォルトで、このストリームが、埋め込まれたファセットを無視するbasic_streambufベースのstreambufを使用するためです。次の行は、std :: coutとその仲間を切り替えて、basic_filebufベースのstreambufを使用します。これは、埋め込まれたファセットを使用します。

std::ios::sync_with_stdio(false);

iostream標準ストリームオブジェクトが標準Cストリームとは独立して動作する可能性があるという関連する副作用があります。

もう1つの注意点は、このファセットには静的なstd :: locale :: idがないためです。つまり、ロケールでstd ::has_facet<IndentFacet>を呼び出すと常にtrueが返されます。std :: local :: idを追加すると、basic_filebufが基本クラステンプレートを検索するため、ファセットが使用されなかったことを意味します。

于 2011-07-09T02:04:05.393 に答える
2

Loki Astartiのソリューションを一般化して、任意のインデントレベルで動作させました。このソリューションには、使いやすい優れたインターフェイスがありますが、実際の実装は少し厄介です。githubで見つけることができます:https ://github.com/spacemoose/ostream_indenter

githubリポジトリにはもっと複雑なデモがありますが、次のようになっています。

#include "indent_facet.hpp"

/// This probably has to be called once for every program:
// http://stackoverflow.com/questions/26387054/how-can-i-use-stdimbue-to-set-the-locale-for-stdwcout
std::ios_base::sync_with_stdio(false);

// This is the demo code:
std::cout << "I want to push indentation levels:\n" << indent_manip::push
          << "To arbitrary depths\n" << indent_manip::push
          << "and pop them\n" << indent_manip::pop
          << "back down\n" << indent_manip::pop
          << "like this.\n" << indent_manip::pop;

}

次の出力が生成されます。

I want to push indentation levels:
    To arbitrary depths
        and pop them
    back down
like this.

コードの有用性に関するフィードバックをいただければ幸いです。

于 2015-09-11T13:23:04.227 に答える
1

簡単な方法はありませんが、これを実現するための複雑な方法について多くのことが書かれています。このトピックの適切な説明については、この記事をお読みください。残念ながらドイツ語の別の記事があります。しかし 、そのソースコードはあなたを助けるはずです。

たとえば、再帰構造をログに記録する関数を作成できます。再帰のレベルごとに、インデントが増加します。

std::ostream& operator<<(std::ostream& stream, Parameter* rp) 
{
    stream << "Parameter: " << std::endl;

    // Get current indent
    int w = format::get_indent(stream);

    stream << "Name: "  << rp->getName();
    // ... log other attributes as well

    if ( rp->hasParameters() )
    {
        stream << "subparameter (" << rp->getNumParameters() << "):\n";

        // Change indent for sub-levels in the hierarchy
        stream << format::indent(w+4);

        // write sub parameters        
        stream << rp->getParameters();
    }

    // Now reset indent
    stream << format::indent(w);

    return stream; 

}
于 2009-09-08T04:36:48.277 に答える
0

シンプルな空白マニピュレーター

struct Whitespace
{
    Whitespace(int n)
        : n(n)
    {
    }
    int n;
};

std::ostream& operator<<(std::ostream& stream, const Whitespace &ws)
{
    for(int i = 0; i < ws.n; i++)
    {
        stream << " ";
    }
    return stream;
}
于 2014-11-17T09:16:58.850 に答える