1. ホーム
  2. c++

[解決済み】C++のストリームマニピュレータで固定幅のフィールドのテキストを中央揃えにする

2022-02-04 05:43:08

質問

を使用しているレガシーコードをリファクタリングしています。 printf を長い文字列(実際の書式なし)と共に使用し、以下のようなプレーンテキストのテーブルヘッダーを出力します。

|  Table   |  Column  | Header  |

で、現在このように制作しています。

printf("|  Table   |  Column  | Header  |");

という趣旨のコードで、上記を演出したい。 1 :

outputStream << "|" << std::setw(10) << std::center << "Table"
             << "|" << std::setw(10) << std::center << "Column"
             << "|" << std::setw(9) << std::center << "Header"
             << "|" << std::endl;

のため、コンパイルされません。 <iomanip> は、ストリームマニピュレータ std::left , std::rightstd::internal を持つことはできませんが std::center . C++の標準ライブラリにこれを行うきれいな方法が既にあるのでしょうか、それとも必要な間隔を手動で計算しなければならないのでしょうか?


1 <サブ C言語のコードよりも冗長であっても、長い目で見れば、より冗長でなくなるのは printf ステートメントとその文字列の重複を抑制することができます。また、より拡張性、保守性が高くなります。

解決方法は?

ここに、あなたが望むことを実現するヘルパークラスがあります。

#include <string>
#include <iostream>
#include <iomanip>

template<typename charT, typename traits = std::char_traits<charT> >
class center_helper {
    std::basic_string<charT, traits> str_;
public:
    center_helper(std::basic_string<charT, traits> str) : str_(str) {}
    template<typename a, typename b>
    friend std::basic_ostream<a, b>& operator<<(std::basic_ostream<a, b>& s, const center_helper<a, b>& c);
};

template<typename charT, typename traits = std::char_traits<charT> >
center_helper<charT, traits> centered(std::basic_string<charT, traits> str) {
    return center_helper<charT, traits>(str);
}

// redeclare for std::string directly so we can support anything that implicitly converts to std::string
center_helper<std::string::value_type, std::string::traits_type> centered(const std::string& str) {
    return center_helper<std::string::value_type, std::string::traits_type>(str);
}

template<typename charT, typename traits>
std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& s, const center_helper<charT, traits>& c) {
    std::streamsize w = s.width();
    if (w > c.str_.length()) {
        std::streamsize left = (w + c.str_.length()) / 2;
        s.width(left);
        s << c.str_;
        s.width(w - left);
        s << "";
    } else {
        s << c.str_;
    }
    return s;
}

を呼び出すだけで使用できます。 centered("String") というように。

int main(int argc, char *argv[]) {
    std::cout << "|" << std::setw(10) << centered("Table")
              << "|" << std::setw(10) << centered("Column")
              << "|" << std::setw(9)  << centered("Header") << "|"
              << std::endl;
}