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;
}
*/
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;
}
*/
相关文章推荐
- linux 下连续使用多个scanf() 的问题和 fflush(stdin)的变通处理
- 使用__attribute__处理结构对齐问题
- linux下使用c++访问mysql数据库编译问题
- Linux下,使用C/C++一个简单的消息处理程序
- linux下使用libiconv进行汉字编码问题的处理-----libiconv静态库的编译
- Linux下Mysql 命令行显示乱码问题解决
- 转:使用__attribute__处理对齐问题
- web开发各种乱码问题处理全集 保证解决99%的乱码错误(包括mysql的乱码解决,使用struts2框架的乱码问题)
- linux下mysql使用select into outfile权限问题
- 使用 gperf 实现高效的 C/C++ 命令行处理
- C++使用mysql 库访问MYSQL数据库,mysql_init函数破坏内存的问题
- Linux下,使用C/C++编写的一个简单的信号处理例程
- 关于linux下使用phpmyadmin给mysql表关联外键的问题和解决办法
- Linux Mysql使用rpm安装的后的路径问题
- [转] 使用 gperf 实现高效的 C/C++ 命令行处理
- shell脚本输出对齐问题(使用printf)
- 使用Navicat连接Linux 下的MySQL遇到的问题
- DOS命令行窗口mysql显示乱码问题处理
- 取从textarea中存到MYSQL的数据以原格式显示问题以及解决MySQL日期处理和标签的使用
- linux下使用c++访问mysql数据库编译问题