Golang实现Map[]到TSV(制表符分隔文件)的转换
Golang实现Map[]到TSV(制表符分隔文件)的转换 我获取的数据是一个映射:
[
map[user_id:3 user_name:John Doe]
map[user_id:23 user_name:Donald Duck]
map[user_id:24 user_name:Jane Doe]
]
我希望它成为一个简单的 .tsv 文件(制表符分隔),像这样:
user_id -> user_name
3 -> John Doe
23 -> Donald Duck
24 -> Jane Doe
func main() {
Connect()
c := cron.New()
c.AddFunc("@every 30s", SendMail)
c.Start()
time.Sleep(time.Duration(1<<63 - 1))
}
func SendMail() {
data := getall("SELECT * FROM usr")
t := time.Now()
t.String()
file := "test_" + t.Format("2006-01-02T15-04-05.000Z") + ".tsv"
f, err := os.Create(file)
fmt.Println(file)
if err != nil {
fmt.Println(err)
}
defer f.Close()
_, err2 := f.WriteString(data)
}
这可以实现吗?
更多关于Golang实现Map[]到TSV(制表符分隔文件)的转换的实战教程也可以访问 https://www.itying.com/category-94-b0.html
这是一个切片,其元素类型为映射。切片中的每个元素都是映射类型。
更多关于Golang实现Map[]到TSV(制表符分隔文件)的转换的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
是的,CSV中的分隔符也可以是空格。
for k,v := range userMap {
fmt.Fprintf(f,"%d\t%s\n",k,v)
}
你好 Sibert,
函数 getall("SELECT * FROM usr") 的返回类型是什么?你可以像这样检查:
fmt.Printf("%T\n", getall("SELECT * FROM usr"))
ljh: 函数
getall("SELECT * FROM usr")的返回类型是什么?
[]map[string]interface{}
我假设您“将数据作为映射获取”,并以此为前提编写了示例,假设 userMap 是一个 map[int]string。请根据您的实际类型相应地调整示例。
func main() {
fmt.Println("hello world")
}
谢谢,但我不是很理解:
for key, val := range data {
fmt.Fprintf(f, "%d\t%s\n", key, val)
}
错误:
无法在 data 上进行遍历(类型为 interface {})
我做错了什么?如何从 data 中获取 userMap?
假设你“获取到的数据是一个映射”
data := getall("SELECT * FROM usr") 返回
[ map[user_id:3 user_name:John Doe] map[user_id:23 user_name:Donald Duck] map[user_id:24 user_name:Jane Doe] ]
Dean_Davidson:
你可以使用 encoding/csv 来写入 TSV 文件。
当然,当值中包含分隔符时,这种方法在引号处理方面也会更加健壮,并且能正确处理被引号包裹的值内部的引号字符。
mje:
你可以清楚地看到,
strings.Join确实在这里的每个字段之间都放置了一个制表符:
是的,我发现这个方法有效。但我该如何处理“多行”字段呢?
它们显示为单独的行。

你可以直接操作 data。通过 for ... := range data { 循环遍历 data 的元素。每个元素都是一个 map[string]interface{}。从你的打印输出中可知,该映射中的两个键是 “user_id” 和 “user_name”。打印这些键对应的值,并用制表符 ‘\t’ 分隔。
for _, entryMap := range data {
fmt.Fprintf(f, "%d\t%v\n", entryMap["user_id"], entryMap["user_name"])
}
Sibert:
keys := []string{}
{} 不正确
Sibert:
var vals []interface{} for _, row := range data { for _, val := range row { vals = append(vals, val) } }
这看起来像是 data 中所有值的一个扁平切片。我认为这不是你想要的。
此外,你不能指望对 row 的迭代在行与行之间保持相同的顺序,也不能指望它与你的 cols 匹配。为了获得一致的顺序,最好遍历你的 keys 切片。这可能相关也可能不相关,但你可能需要考虑某一行中某一列的值缺失的情况。
但是,如果你想要一个通用的TSV函数,这里有一个简单的实现:
谢谢!这几乎正是我想要的。我发现的唯一问题是标题中的制表符添加不正确:
fmt.Fprintln(w, strings.Join(headers, "\t"))
在 tsk_desc 和 tsk_id 之间缺少一个制表符(显示为空格而不是制表符):

有没有办法用缺失的制表符替换这个空格?
我首先要提醒你,对于你想要表示的数据,
[]map[string]interface{}可能不是理想的选择。
有没有更好的通用方法来实现这个?
是的,这是一种方法。谢谢!
但我的本意是想让它更通用。这里有一个新手尝试的简陋版本: https://play.golang.com/p/wNDDSiMcKJk
远非完美,但至少展示了我想要实现的效果。
package main
import (
"fmt"
)
func main() {
data := getall("SELECT * FROM usr")
tsv := map2csv(data)
fmt.Println(tsv)
}
// Simulate db call
func getall(sql string) []map[string]interface{} {
return []map[string]interface{}{
{"user_id": 3, "user_name": "John Doe"},
{"user_id": 23, "user_name": "Donald Duck"},
{"user_id": 24, "user_name": "Jane Doe"},
}
}
func map2csv(data []map[string]interface{}) (tsv []interface{}) {
cols := make(map[string]struct{})
for _, record := range data {
for key, _ := range record {
cols[key] = struct{}{}
}
}
keys := []string{}
for key := range cols {
keys = append(keys, key)
}
fmt.Println(keys)
var vals []interface{}
for _, row := range data {
for _, val := range row {
vals = append(vals, val)
}
}
return vals
}
这可行吗?使用 encoding/csv 可以实现吗?
首先需要提醒你,对于你想要表示的数据,使用 []map[string]interface{} 可能并不理想。但是,如果你想要一个通用的 TSV 函数,这里有一个简单的实现:
// writeTSV writes tab-separated data to w. It doesn't support jagged maps, so
// all maps must contain the same keys. It also doesn't support escaped tabs so
// it is up to caller to sanitize data.
func writeTSV(w io.Writer, items []map[string]interface{}) {
// Empty data
if len(items) == 0 {
fmt.Fprint(w, "Empty dataset")
return
}
var headers = make([]string, len(items[0]))
i := 0
// Iterate over first item in our array to get headers (AKA keys)
// These will not be in any specific order. See also:
// https://stackoverflow.com/questions/9619479/go-what-determines-the-iteration-order-for-map-keys
for key := range items[0] {
headers[i] = key
i++
}
// Since not in specific order, sort columns to make this predictable.
sort.Strings(headers)
// Print our header
fmt.Fprintln(w, strings.Join(headers, "\t"))
// Iterate over our maps and populate each row with data
for _, row := range items {
for i, column := range headers {
// Print our row value. This is the part that would break
// with jagged maps. TODO: support jagged maps?
fmt.Fprint(w, row[column])
// For the last column, we print newline
if i+1 == len(headers) {
fmt.Fprint(w, "\n")
} else {
// Otherwise, print our delimiter (tab in this case).
fmt.Fprint(w, "\t")
}
}
}
}
使用方法如下:
func SendMail() {
data := getall("SELECT * FROM usr")
t := time.Now()
fileName := "test_" + t.Format("2006-01-02T15-04-05.000Z") + ".tsv"
f, err := os.Create(fileName)
if err != nil {
fmt.Println(err)
}
defer f.Close()
writeTSV(f, data)
}
整合起来,这里有一个写入 os.stdout 的 Go Playground 链接:
https://play.golang.com/p/-Qsx6igk8-E
总之,类似这样的代码应该能让你更接近你想要实现的目标。希望如此。
是的,可以直接将Map切片转换为TSV格式。以下是完整的实现方案:
package main
import (
"fmt"
"os"
"strings"
)
func main() {
// 示例数据
data := []map[string]interface{}{
{"user_id": 3, "user_name": "John Doe"},
{"user_id": 23, "user_name": "Donald Duck"},
{"user_id": 24, "user_name": "Jane Doe"},
}
// 转换为TSV
tsvData := convertToTSV(data)
// 写入文件
file := "users.tsv"
f, err := os.Create(file)
if err != nil {
fmt.Println("创建文件失败:", err)
return
}
defer f.Close()
_, err = f.WriteString(tsvData)
if err != nil {
fmt.Println("写入文件失败:", err)
return
}
fmt.Println("TSV文件已生成:", file)
}
func convertToTSV(data []map[string]interface{}) string {
if len(data) == 0 {
return ""
}
var builder strings.Builder
// 写入表头
headers := make([]string, 0, len(data[0]))
for key := range data[0] {
headers = append(headers, key)
}
builder.WriteString(strings.Join(headers, "\t") + "\n")
// 写入数据行
for _, row := range data {
values := make([]string, 0, len(row))
for _, key := range headers {
values = append(values, fmt.Sprintf("%v", row[key]))
}
builder.WriteString(strings.Join(values, "\t") + "\n")
}
return builder.String()
}
针对你的具体场景,修改SendMail函数:
func SendMail() {
data := getall("SELECT * FROM usr")
// 转换为TSV格式
tsvContent := convertToTSV(data)
t := time.Now()
file := "test_" + t.Format("2006-01-02T15-04-05") + ".tsv"
f, err := os.Create(file)
if err != nil {
fmt.Println("创建文件失败:", err)
return
}
defer f.Close()
_, err = f.WriteString(tsvContent)
if err != nil {
fmt.Println("写入文件失败:", err)
return
}
fmt.Println("TSV文件已生成:", file)
}
如果getall函数返回的是[]map[string]interface{}类型,这个方案可以直接使用。如果返回类型不同,需要相应调整convertToTSV函数的参数类型。



