日志表格格式化输出,only text_table.h头文件

日志表格格式化输出

/**
 * \file sdk/load-and-run/src/text_table.h
 * MegEngine is Licensed under the Apache License, Version 2.0 (the "License")
 *
 * link: <https://zhuanlan.zhihu.com/p/397740790>
 *
 * Copyright (c) 2014-2021 Megvii Inc. All rights reserved.
 *
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT ARRANTIES OR CONDITIONS OF ANY KIND, either express or
 * implied.
 */

#pragma once

#include <array>
#include <iomanip>
#include <ostream>
#include <sstream>
#include <string>
#include <tuple>
#include <type_traits>
#include <vector>

namespace pipeline {

class TextTable
{
public:
    enum Level
    {
        Summary,
        Detail
    };
    enum class Align : int
    {
        Left,
        Right,
        Mid
    };
    TextTable() = default;
    explicit TextTable(const std::string& table_name)
        : m_name(table_name)
    {
    }
    TextTable& horizontal(char c)
    {
        m_row.params.horizontal = c;
        return *this;
    }
    TextTable& vertical(char c)
    {
        m_row.params.vertical = c;
        return *this;
    }
    TextTable& corner(char c)
    {
        m_row.params.corner = c;
        return *this;
    }
    TextTable& align(Align v)
    {
        m_row.params.align = v;
        return *this;
    }
    TextTable& padding(size_t w)
    {
        m_padding = w;
        return *this;
    }
    TextTable& prefix(const std::string& str)
    {
        m_prefix = str;
        return *this;
    }

    template<typename T>
    TextTable& add(const T& value)
    {
        m_row.values.emplace_back(value);
        if (m_cols_max_w.size() < m_row.values.size())
        {
            m_cols_max_w.emplace_back(m_row.values.back().length());
        }
        else
        {
            size_t i = m_row.values.size() - 1;
            m_cols_max_w[i] = std::max(m_cols_max_w[i], m_row.values.back().length());
        }
        return *this;
    }

    template<typename T, typename std::enable_if<std::is_floating_point<T>::value, bool>::type = 0>
    TextTable& add(const T& value)
    {
        std::stringstream ss;
        ss << std::setiosflags(std::ios::fixed) << std::setprecision(2);
        ss << value;
        m_row.values.emplace_back(ss.str());
        if (m_cols_max_w.size() < m_row.values.size())
        {
            m_cols_max_w.emplace_back(m_row.values.back().length());
        }
        else
        {
            size_t i = m_row.values.size() - 1;
            m_cols_max_w[i] = std::max(m_cols_max_w[i], m_row.values.back().length());
        }
        return *this;
    }

    template<typename T, typename std::enable_if<std::is_integral<T>::value, bool>::type = 0>
    TextTable& add(const T& value)
    {
        m_row.values.emplace_back(std::to_string(value));
        return *this;
    }

    void eor()
    {
        m_rows.emplace_back(m_row);
        adjuster_last_row();
        m_row.values.clear();
    }

    void reset()
    {
        m_row = {};
        m_cols_max_w.clear();
        m_padding = 0;
        m_rows.clear();
    }

    void show(std::ostream& os)
    {
        if (m_rows.empty()) return;
        auto& last_row = m_rows.front();
        bool first = true;
        for (auto& row : m_rows)
        {
            auto& lrow = (last_row.values.size() * char_length(last_row.params.horizontal)) > (row.values.size() * char_length(row.params.horizontal))
                             ? last_row
                             : row;
            // line before row
            if (lrow.params.horizontal)
            {
                if (!first) os << std::endl;
                os << m_prefix;
                if (lrow.params.corner) os << lrow.params.corner;
                size_t skip_size = 0;
                // table name
                if (first)
                {
                    os << m_name;
                    skip_size = m_name.length();
                }
                for (size_t i = 0; i < lrow.values.size(); ++i)
                {
                    auto max_w = m_cols_max_w.at(i) + m_padding * 2;
                    if (max_w + char_length(lrow.params.corner) <= skip_size)
                    {
                        skip_size = skip_size - max_w - char_length(lrow.params.corner);
                        continue;
                    }
                    size_t rest = max_w + char_length(lrow.params.corner) - skip_size;
                    skip_size = 0;
                    if (rest > char_length(lrow.params.corner))
                    {
                        os << std::string(rest - char_length(lrow.params.corner),
                                          lrow.params.horizontal);
                        rest = char_length(lrow.params.corner);
                    }
                    if (rest > 0 && lrow.params.corner) os << lrow.params.corner;
                }
            }
            else if (first)
            {
                os << m_prefix << ' ' << m_name;
            }
            first = false;
            os << std::endl
               << m_prefix;
            if (row.params.vertical) os << row.params.vertical;
            // row
            for (size_t i = 0; i < row.values.size(); ++i)
            {
                auto& str = row.values.at(i);
                auto max_w = m_cols_max_w.at(i) + 2 * m_padding;
                if (row.params.align == Align::Mid)
                {
                    mid(os, str, max_w);
                }
                else if (row.params.align == Align::Left)
                {
                    os << std::setw(max_w) << std::left << str;
                }
                else
                {
                    os << std::setw(max_w) << std::right << str;
                }
                if (row.params.vertical) os << row.params.vertical;
            }
            last_row = row;
        }
        if (last_row.params.horizontal)
        {
            os << std::endl
               << m_prefix;
            if (last_row.params.corner) os << last_row.params.corner;
            for (size_t i = 0; i < last_row.values.size(); ++i)
            {
                auto max_w = m_cols_max_w.at(i);
                std::string tmp(max_w + m_padding * 2, last_row.params.horizontal);
                os << tmp;
                if (last_row.params.corner) os << last_row.params.corner;
            }
        }
    }

private:
    void adjuster_last_row()
    {
        if (m_rows.empty()) return;
        auto& row = m_rows.back();
        if (row.params.horizontal == 0 || row.params.vertical == 0)
        {
            row.params.corner = 0;
        }
        if (row.params.horizontal != 0 && row.params.vertical != 0 && row.params.corner == 0)
        {
            row.params.corner = row.params.horizontal;
        }
    }

    inline void mid(std::ostream& os, const std::string& str, size_t max_w)
    {
        size_t l = (max_w - str.length()) / 2 + str.length();
        size_t r = max_w - l;
        os << std::setw(l) << std::right << str;
        if (r > 0) os << std::setw(r) << ' ';
    }
    inline size_t char_length(char c)
    {
        return c ? 1 : 0;
    }
    std::string m_name;
    std::vector<size_t> m_cols_max_w;
    size_t m_padding = 0;
    std::string m_prefix = "";
    struct Row
    {
        std::vector<std::string> values;
        struct Params
        {
            Align align = Align::Left;
            char horizontal = '-', vertical = '|', corner = '+';
        } params;
    };
    std::vector<Row> m_rows;
    Row m_row;
};

inline std::ostream& operator<<(std::ostream& stream, TextTable& table)
{
    table.show(stream);
    return stream;
}

} // namespace pipeline

如何使用呢?

#include <stdio.h>
#include "text_table.h"
#include <iostream>

int main(int argc, char const *argv[])
{
    const int a = 10;
    int b[a];
    for (size_t i = 0; i < a; i++)
    {
        b[i] = i+1;
    }

    // 定义表头
    pipeline::TextTable table("my test table");
    // 添加单元格填充变大
    table.padding(3);
    // 居中
    table.align(pipeline::TextTable::Align::Mid);
    // 添加数据,eor结尾
    table.add("preproc").add("inference").add("postproc").eor();
    table.add("12").add("4").add("66").eor();
    table.add("1.6").add("55").eor();

    std::cout << table << std::endl;
    return 0;
}
posted @ 2023-04-04 15:42  nanmi  阅读(13)  评论(0编辑  收藏  举报