如何在Golang中打印一个两列的表格?

huangapple go评论68阅读模式
英文:

How to print a 2-columns table in Golang?

问题

我有点困惑。我的想法是创建一个打印两列表格的函数。第一列是键,它有固定的宽度。第二列是值,可能是非常长的字符串,它的宽度取决于当前终端的宽度。

以下是我想要的示例:

Key1                                  Value1Value1Value1Value1
Key2                                  Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2
                                      Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2
                                      Value2Value2Value2Value2Value2Value2

到目前为止,我最好的实现是使用 lipgloss 来为第一列设置固定宽度。

func PrintMetadata(metadata map[string]string, color string) {
	style := lipgloss.NewStyle().Width(32).Foreground(lipgloss.Color(color))
	for k, v := range metadata {
		fmt.Println(style.Render(k) + v)
	}
}

其结果类似于:

Key1                                  Value1Value1Value1Value1
Key2                                  Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2
Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2

那么,我该如何按照我想要的方式格式化字符串呢?我可以使用标准库和外部库,所以欢迎任何建议。

英文:

Kind of stuck with this. My idea is having a function that prints a two-columns table. The first one is for keys, and it has a fixed width. The second one is for values, which may be very long strings, and its width depends on the current width of the terminal.

An example of what I'd like to have:

Key1                                  Value1Value1Value1Value1
Key2                                  Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2
                                      Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2
                                      Value2Value2Value2Value2Value2Value2

So far, the best I achieved is to have a fixed width for the first columns, using lipgloss.

func PrintMetadata(metadata map[string]string, color string) {
	style := lipgloss.NewStyle().Width(32).Foreground(lipgloss.Color(color))
	for k, v := range metadata {
		fmt.Println(style.Render(k) + v)
	}
}

The result of which is something like:

Key1                                  Value1Value1Value1Value1
Key2                                  Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2
Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2Value2

So, how can I format a string in the way I want? I can use both standard and external libraries, so any suggestions are welcome.

答案1

得分: 1

我为此编写了一个函数。这个函数有两个参数,第一个参数是用于列化的地图变量,第二个参数是每行要填充的字符数。它简单地将键的值内容与间距更改为新变量,然后打印该键和值。但是,如果您使用未修改的值进行操作,可以使用未修改的变量。

package main

import (
	"fmt"
	"errors"
	"strings"
	"sort"
)

func main() {
	a := map[string]string{
		"Key1": strings.Repeat("Value1", 50), 
		"Key2": strings.Repeat("Value2", 50), 
		"Key3": strings.Repeat("Value3", 50),
	}
	
	err := columner(a, 30)
	
	if err != nil {
		fmt.Println(err)
	}
	
}

func columner(m map[string]string, charAmount int) error{

	var keys []string
	
	var keyLens []int
	
	// 为了避免索引错误并收集以供以后使用的键
	for key, value := range m {
		if charAmount > len(value) || charAmount < 1{
			return errors.New("错误:charAmount 既不能大于键值的长度,也不能小于 1")
		}
		keys = append(keys, key)
		keyLens = append(keyLens, len(key))
	}

	sort.Ints(keyLens)
	
	for i := 0; i < len(keys); i++ {
		
		// 用于存储键的更新值
		var value2 string
		
		value := m[keys[i]]
		// 用于提取键值的子字符串的第一个索引
		firstI := 0
		
		// 用于从键值中提取子字符串的最后一个索引。子字符串的长度与 charAmount 相同
		charAmount2 := charAmount
		
		// 用于推进到键值的下一个子字符串
		advance := charAmount2
		
		// 键和值之间的空格
		// 键       值
		spacing := strings.Repeat(" ", 20 + (keyLens[0] - len(keys[i])))
		
		// 用于调整键和下一行的值之间间隔的空格的变量
		// 键        值
		//          值
		// 变为
		// 键        值
		//            值
		spacingU := spacing + strings.Repeat(" ", len(keys[i]) + 1)
		
		// 只要没有超过下一行的子字符串,此循环将一直运行
		for j := 0; j < len(value); j += advance {
			
			// 调整键和下一行的值之间间隔的空格
			if j > 0 {
				spacing = spacingU
			}
			
			// 在键和值之间添加空格,然后提取子字符串,然后在下一行的下一个子字符串的键值之间添加空格
			value2 += spacing + value[firstI:charAmount2] + "\n"
			
			// 当没有可以超过下一行的子字符串时结束循环
			if ((len(value) - charAmount2) < advance) || ((len(value) - charAmount2) == advance) {
				break
			}
	
			// 将第一个索引更改为下一个子字符串的起始索引
			firstI = charAmount2
			
			// 推进到键值的下一个子字符串
			charAmount2 += advance
		}	
		
		// 将键值的最后一个剩余子字符串添加到将显示为格式化的变量中。
		value2 += spacing + value[charAmount2:]

		// 显示格式化的键和值
		fmt.Println(keys[i], value2, "\n")
		
	}
	
	return nil
}

这是一个示例输出:

Key1                     Value1Value1Value1Value1Value1
Value1Value1Value1Value1Value1
Value1Value1Value1Value1Value1
Value1Value1Value1Value1Value1
Value1Value1Value1Value1Value1
Value1Value1Value1Value1Value1
Value1Value1Value1Value1Value1
Value1Value1Value1Value1Value1
Value1Value1Value1Value1Value1
Value1Value1Value1Value1Value1 
Key2                     Value2Value2Value2Value2Value2
Value2Value2Value2Value2Value2
Value2Value2Value2Value2Value2
Value2Value2Value2Value2Value2
Value2Value2Value2Value2Value2
Value2Value2Value2Value2Value2
Value2Value2Value2Value2Value2
Value2Value2Value2Value2Value2
Value2Value2Value2Value2Value2
Value2Value2Value2Value2Value2 
Key3                     Value3Value3Value3Value3Value3
Value3Value3Value3Value3Value3
Value3Value3Value3Value3Value3
Value3Value3Value3Value3Value3
Value3Value3Value3Value3Value3
Value3Value3Value3Value3Value3
Value3Value3Value3Value3Value3
Value3Value3Value3Value3Value3
Value3Value3Value3Value3Value3
Value3Value3Value3Value3Value3 

但请注意,每次执行时,键和值的顺序可能不同,因为 map 类型在使用键值对的 for 循环打印时是无序的。

英文:

I made a function for this. This function has two parameters first for the map variable for columning, second is for how many characters do you want to fill to per line. It simply change key's value contents with spacing into new variable then prints that key, value. But if you have works with unmodified value you can use unmodified variable.

package main
import (
&quot;fmt&quot;
&quot;errors&quot;
&quot;strings&quot;
&quot;sort&quot;
)
func main() {
a := map[string]string{
&quot;Key1&quot;: strings.Repeat(&quot;Value1&quot;, 50), 
&quot;Key2&quot;: strings.Repeat(&quot;Value2&quot;, 50), 
&quot;Key3&quot;: strings.Repeat(&quot;Value3&quot;, 50),
}
err := columner(a, 30)
if err != nil {
fmt.Println(err)
}
}
func columner(m map[string]string, charAmount int) error{
var keys []string
var keyLens []int
// to avoid index panics and gathering keys for later usage
for key, value := range m {
if charAmount &gt; len(value) || charAmount &lt; 1{
return errors.New(&quot;Error: charAmount neither be greather than length of key&#39;s value nor below 1&quot;)
}
keys = append(keys, key)
keyLens = append(keyLens, len(key))
}
sort.Ints(keyLens)
for i := 0; i &lt; len(keys); i++ {
// for storing updated value of key
var value2 string
value := m[keys[i]]
// will used while extracting substring of key&#39;s value as first index
firstI := 0
// last index for extract substring from key&#39;s value. the len of substring will be same as charAmount
charAmount2 := charAmount
// will be used to advance next substring of key&#39;s value
advance := charAmount2
// spaces between between key and value 
// key       value
spacing := strings.Repeat(&quot; &quot;, 20 + (keyLens[0] - len(keys[i])))
// var for adjusting spaces of gap between key and value of next line
// key        value
//          value
// to
// key        value
//            value
spacingU := spacing + strings.Repeat(&quot; &quot;, len(keys[i]) + 1)
// this loop will be run as long as there is no substring left which exceed next line
for j := 0; j &lt; len(value); j += advance {
// adjusting spaces of gap between key and value of next line
if j &gt; 0 {
spacing = spacingU
}
// add space between key and value, then extract substring, then add spaces to the next line of the
// next substring of key&#39;s value
value2 += spacing + value[firstI:charAmount2] + &quot;\n&quot;
// finish loop when there is no substring that can be exceed to next line
if ((len(value) - charAmount2) &lt; advance) || ((len(value) - charAmount2) == advance) {
break
}
// changing first index to start index of next substring of key&#39;s value
firstI = charAmount2
// advancing to next substring of key&#39;s value
charAmount2 += advance
}	
// add last remaining substring of key&#39;s value to variable which will be show as formatted.
value2 += spacing + value[charAmount2:]
// show formatted key and value
fmt.Println(keys[i], value2, &quot;\n&quot;)
}
return nil
}

This is a example output:

Key1                     Value1Value1Value1Value1Value1
Value1Value1Value1Value1Value1
Value1Value1Value1Value1Value1
Value1Value1Value1Value1Value1
Value1Value1Value1Value1Value1
Value1Value1Value1Value1Value1
Value1Value1Value1Value1Value1
Value1Value1Value1Value1Value1
Value1Value1Value1Value1Value1
Value1Value1Value1Value1Value1 
Key2                     Value2Value2Value2Value2Value2
Value2Value2Value2Value2Value2
Value2Value2Value2Value2Value2
Value2Value2Value2Value2Value2
Value2Value2Value2Value2Value2
Value2Value2Value2Value2Value2
Value2Value2Value2Value2Value2
Value2Value2Value2Value2Value2
Value2Value2Value2Value2Value2
Value2Value2Value2Value2Value2 
Key3                     Value3Value3Value3Value3Value3
Value3Value3Value3Value3Value3
Value3Value3Value3Value3Value3
Value3Value3Value3Value3Value3
Value3Value3Value3Value3Value3
Value3Value3Value3Value3Value3
Value3Value3Value3Value3Value3
Value3Value3Value3Value3Value3
Value3Value3Value3Value3Value3
Value3Value3Value3Value3Value3 

But please notice this, order of key and values may be different on each execution because map type is unordered when printed in for loop with key, value pair.

huangapple
  • 本文由 发表于 2023年4月2日 22:51:21
  • 转载请务必保留本文链接:https://go.coder-hub.com/75912747.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定