您的位置:首页 > 运维架构 > Linux

linux使用C++做到类似mysql的命令行表格输出(处理中英文在命令行对齐问题)

2018-01-17 19:38 1181 查看
最近工作需要,需要写一个查询工具,查询结果是一个表的结果,使用文本输出看起来比较混乱,就想着能否做成类似mysql的输出控制。
github找到一个可以现成来使用的工具。地址为:https://github.com/haarcuba/cpp-text-table
不过这个方案有个问题,就是当输出结果全部为英文或者数字的时候,可以正常工作。当输出结果含有中文的时候,则会出现问题。
问题如下:
1、使用string存储,一个中文字符,在linux下,它使用UTF8编码时,是3个字节大小。
2、一个中文字符,它占的屏幕宽度是普通英文或数字的2倍。
3、使用setw(n)这样来对齐的时候,比如说你想按4个英文字符的宽度来对齐,输出了一个中文的时候。那如果使用
    string str = "系统";
    string str1 = "元"; 
    string str2 = "abcd";
    cout << "|" << std::setw(4) << setiosflags(ios::right) << setfill('*') << str << "|" << endl;
    cout << "|" << std::setw(4) << setiosflags(ios::right) << setfill('*') << str1 << "|" << endl;
    cout << "|" << std::setw(4) << setiosflags(ios::right) << setfill('*') << str2 << "|" << endl;
执行结果如下所示:



我们看到,第二行的元,它预期是需要占四个宽度,但第二行中,只被填充了一个字符,这是为何?setw(n)并没有按我们预期的进行对齐,留出四个字符宽度。
解释:这得想一下 setw的原理。它实际上是在缓冲区中,帮你插入字节来实现对齐的。第二个字符“元”中,它占了三个字节,那setw(4)只会帮他插入一个字节,那这就是它无法对齐的原因了。知道了这个原理,那我们就可以对中英文的问题进行处理。
处理后可以兼容中英文字符的代码如下 (如果有BUG,欢迎与我反馈)
#include <iostream>
#include <map>
#include <iomanip>
#include <vector>
#include <string>

typedef std::vector< std::string > Row;
enum Alignment { LEFT, RIGHT };
class TextTable {

public:
TextTable( char horizontal = '-', char vertical = '|', char corner = '+' ) :
_horizontal( horizontal ),
_vertical( vertical ),
_corner( corner )
{}

unsigned Calsetw(unsigned show_width, const std::string& str) const {
unsigned width = GetWidth(str);
return str.size() + show_width - width;
}

void SetAlignment( unsigned i, Alignment alignment )
{
_alignment[ i ] = alignment;
}

Alignment GetAlignment( unsigned i ) const
{ return _alignment[ i ]; }

char Vertical() const
{ return _vertical; }

char Horizontal() const
{ return _horizontal; }

void Add( std::string const & content )
{
_current.push_back( content );
}

void EndOfRow()
{
_rows.push_back( _current );
_current.assign( 0, "" );
}

template <typename Iterator>
void AddRow( Iterator begin, Iterator end )
{
for(Iterator i = begin; i != end; ++i ) {
add( * i );
}
EndOfRow();
}

template <typename Container>
void AddRow( Container const & container )
{
AddRow( container.begin(), container.end() );
}

std::vector< Row > const & Rows() const
{
return _rows;
}

void Setup() const
{
DetermineWidths();
SetupAlignment();
}

std::string Ruler() const
{
std::string result;
result += _corner;
for( std::vector<unsigned>::iterator width = _width.begin(); width != _width.end(); ++ width ) {
result += Repeat( * width, _horizontal );
result += _corner;
}

return result;
}

std::vector<Row> GetRows() const {
return _rows;
}

int Width( unsigned i ) const
{ return _width[ i ]; }

private:
char _horizontal;
char _vertical;
char _corner;
Row _current;
std::vector< Row > _rows;
std::vector< unsigned > mutable _width;
//std::vector< unsigned > _width;
std::map< unsigned, Alignment > mutable _alignment;

static std::string Repeat( unsigned times, char c )
{
std::string result;
for( ; times > 0; -- times )
result += c;

return result;
}

unsigned GetWidth(const std::string &str) const {
unsigned width = 0;
size_t i = 0;
while(i < str.size()) {
if (str[i] >= 0 && str[i] <= 127) {
width += 1;
++i;
}
else {
width += 2;
i += 3;
}
}
return width;
}

unsigned Columns() const
{
if (_rows.size() > 0) {
return _rows[ 0 ].size();
}
else {
return 0;
}
}

void DetermineWidths() const
{
_width.assign( Columns(), 0 );
for ( std::vector<Row>::const_iterator rowIterator = _rows.begin(); rowIterator != _rows.end(); ++ rowIterator ) {
Row const & row = * rowIterator;
for ( unsigned i = 0; i < row.size(); ++i ) {
//_width[ i ] = _width[ i ] > row[ i ].size() ? _width[ i ] : row[ i ].size();
unsigned row_width = GetWidth(row[i]);
_width[ i ] = _width[ i ] > row_width ? _width[ i ] : row_width;
}
}
}

void SetupAlignment() const
{
for ( unsigned i = 0; i < Columns(); ++i ) {
if ( _alignment.find( i ) == _alignment.end() ) {
_alignment[ i ] = LEFT;
}
}
}
};

std::ostream & operator<<( std::ostream & stream, TextTable const & table )
{
table.Setup();
stream << table.Ruler() << "\n";
std::vector<Row> rows = table.GetRows();
for (size_t i = 0; i < rows.size(); ++i) {
Row row = rows[i];
stream << table.Vertical();
for (size_t j = 0; j < row.size(); ++j) {
//stream << std::setw( table.width( j ) ) << std::right << row[ j ];
stream << std::setw( table.Calsetw(table.Width(j), row[j])) << std::right << row[ j ];
stream << table.Vertical();
}
stream << "\n";
stream << table.Ruler() << "\n";
}

return stream;
}

/*
* demo code

int main() {
TextTable t( '-', '|', '+' );

t.add( "" );
t.add( "Sex" );
t.add( "Age" );
t.endOfRow();

t.add( "Moses" );
t.add( "male" );
t.add( "4556" );
t.endOfRow();

std::cout << t;

}
*/
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: