使用C++中的流操作符在固定宽度字段中居中文本

Kei*_*son 13 c++ string stringstream ostream manipulators

我正在重构一些遗留代码,这些代码使用printflongs字符串(没有任何实际的格式)来打印纯文本表标题,看起来像这样:

|  Table   |  Column  | Header  |
Run Code Online (Sandbox Code Playgroud)

目前正在制作如下:

printf("|  Table   |  Column  | Header  |");
Run Code Online (Sandbox Code Playgroud)

我想生成上面的代码,效果为1:

outputStream << "|" << std::setw(10) << std::center << "Table"
             << "|" << std::setw(10) << std::center << "Column"
             << "|" << std::setw(9) << std::center << "Header"
             << "|" << std::endl;
Run Code Online (Sandbox Code Playgroud)

不编译,因为<iomanip>有流操纵std::left,std::rightstd::internal,但似乎并没有什么std::center.在标准C++库中是否有一种干净的方法可以做到这一点,还是我必须手动计算必要的间距?


1 即使这比C代码更冗长,但由于printf语句数量和字符串中不加重复的数量,从长远来看它将更加冗长.它也将更具可扩展性和可维护性.

vit*_*aut 18

在 C++20 中,您将能够使用它std::format来执行此操作:

outputStream << std::format("|{:^10}|{:^10}|{:^9}|\n",
                            "Table", "Column", "Header");
Run Code Online (Sandbox Code Playgroud)

输出:

outputStream << std::format("|{:^10}|{:^10}|{:^9}|\n",
                            "Table", "Column", "Header");
Run Code Online (Sandbox Code Playgroud)

在此期间,您可以使用的{} FMT库std::format是基于。{fmt} 还提供了print使这更容易和更高效的功能(Godbolt):

fmt::print("|{:^10}|{:^10}|{:^9}|\n", "Table", "Column", "Header");
Run Code Online (Sandbox Code Playgroud)

免责声明:我是 {fmt} 和 C++20 的作者std::format


Lil*_*ard 10

这是一个帮助你完成你想要的东西:

#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;
}
Run Code Online (Sandbox Code Playgroud)

它只是通过调用使用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;
}
Run Code Online (Sandbox Code Playgroud)

  • 啊!!我喜欢.非常聪明. (2认同)

Mas*_*Man 6

没有 std::center操纵器。恐怕你必须自己做。您可以编写一个辅助函数来计算给定宽度和字符串的空间,以减少工作量。

下面是一个辅助函数可能是什么样子的示例。它需要一些工作才能使其更有效率,等等。

string helper(int width, const string& str) {
    int len = str.length();
    if(width < len) { return str; }

    int diff = width - len;
    int pad1 = diff/2;
    int pad2 = diff - pad1;
    return string(pad1, ' ') + str + string(pad2, ' ');
}
Run Code Online (Sandbox Code Playgroud)