package console import ( "bytes" "fmt" "strings" "time" "unicode/utf8" ) type Table struct { Bordered bool Values [][]interface{} } func (table *Table) AddRow(vs ...interface{}) { table.Values = append(table.Values, vs) } func (table *Table) WithBordered() *Table { table.Bordered = true return table } func printBorder(w *bytes.Buffer, ws []int) { for _, l := range ws { w.WriteString("+") w.WriteString(strings.Repeat("-", l+2)) } w.WriteString("+\n") } func calcCharsetWidth(s string) int { bl := len(s) ul := utf8.RuneCountInString(s) if bl == ul { return bl } else { var wc int //计算中文词语数量,并且替换为2个长度显示 for wc = ((bl - ul) / 3); wc < ul; wc++ { if wc*3-wc == bl-ul { break } } wv := wc*2 + (ul - wc) return wv } } func (table *Table) toString(v interface{}) string { switch t := v.(type) { case float32, float64: return fmt.Sprintf("%.2f", t) case time.Time: return t.Format("2006-01-02 15:04:05") default: return fmt.Sprint(v) } } func (table *Table) Marshal() ([]byte, error) { columns := make([][]string, 0) var widths []int var width int var maxLength int calcWidth := calcCharsetWidth for _, value := range table.Values { if len(value) > maxLength { maxLength = len(value) } } widths = make([]int, maxLength) for _, vs := range table.Values { vl := len(vs) column := make([]string, vl) for i, val := range vs { str := table.toString(val) if vl > 1 { width = calcWidth(str) if width > widths[i] { widths[i] = width } } column[i] = str } columns = append(columns, column) } buffer := &bytes.Buffer{} if table.Bordered { printBorder(buffer, widths) } for index, column := range columns { cl := len(column) for i, w := range widths { if table.Bordered { buffer.WriteString("|") } var str string if cl > i { str = column[i] } if table.Bordered { buffer.WriteString(" ") } buffer.WriteString(str) cl := calcWidth(str) if widths[i] >= cl { buffer.WriteString(strings.Repeat(" ", w-cl)) } buffer.WriteString(" ") //key value options if len(widths) == 2 && i == 0 && len(column) == 2 && !table.Bordered { buffer.WriteString("\t") } } if table.Bordered { buffer.WriteString("|\n") } else { buffer.WriteString("\n") } if table.Bordered && index == 0 { printBorder(buffer, widths) } } if table.Bordered { printBorder(buffer, widths) } return buffer.Bytes(), nil } func NewTable() *Table { return &Table{ Bordered: false, Values: make([][]interface{}, 0), } }