Golang 操作TSV文件的实战示例
作者:梦想画家 发布时间:2023-07-18 06:46:01
本文介绍TSV文件类型及其应用,同时介绍Golang语句读取TSV文件并转为struct的实现过程。
认识TSV文件
也许你之前不了解TSV文件,无需担心,它很简单、很常用。TSV(tab-separated values)文件表示以tab分割值的文件格式,也就是说,TSV文件包括一系列数据信息,其中数据使用tab符(也称制表符,\t)进行分割。与CSV文件格式类似,CSV使用半角逗号(,)分割。
TSV文件和CSV文件一样,非常通用,被大多数平台或处理软件支持,但TSV文件采用不可见制表符作为分隔符,被用户误用的概率较低,相对CSV容错性更好。
Golang 读取TSV文件
golang 包encoding/csv提供了csv文件的读写功能,我们值得tsv和csv的差异仅为分隔符,因此下面代码可以很容易读取tsv:
package main
import (
"encoding/csv"
"fmt"
"log"
"os"
)
func main() {
f, err := os.Open("users.csv")
if err != nil {
log.Fatal(err)
}
r := csv.NewReader(f)
r.Comma = '\t'
r.Comment = '#'
records, err := r.ReadAll()
if err != nil {
log.Fatal(err)
}
fmt.Print(records)
}
解析为结构体
一般我们希望读取tsv文件并解析为struct,下面一起看一些开源代码实现。tsv文件可能包括标题行,同时字段增加tsv标签,示例如下:
type TestTaggedRow struct {
Age int `tsv:"age"`
Active bool `tsv:"active"`
Gender string `tsv:"gender"`
Name string `tsv:"name"`
}
因此定义Parse类型:
// Parser has information for parser
type Parser struct {
Headers []string // 标题数组
Reader *csv.Reader // 读取器
Data interface{} // 希望解析为结构体的类型
ref reflect.Value // 反射值
indices []int // indices is field index list of header array
structMode bool // 结构模式,结构体有tsv标签
normalize norm.Form // 解析UTF8方式
}
定义无标题行的机构函数:
// NewParserWithoutHeader creates new TSV parser with given io.Reader
func NewParserWithoutHeader(reader io.Reader, data interface{}) *Parser {
r := csv.NewReader(reader)
r.Comma = '\t'
p := &Parser{
Reader: r,
Data: data,
ref: reflect.ValueOf(data).Elem(),
normalize: -1,
}
return p
}
带标题行的解析构造函数:
// NewStructModeParser creates new TSV parser with given io.Reader as struct mode
func NewParser(reader io.Reader, data interface{}) (*Parser, error) {
r := csv.NewReader(reader)
r.Comma = '\t'
// 读取一行,即标题行;函数字符串数组
headers, err := r.Read()
if err != nil {
return nil, err
}
// 循环给标题数组赋值
for i, header := range headers {
headers[i] = header
}
p := &Parser{
Reader: r,
Headers: headers,
Data: data,
ref: reflect.ValueOf(data).Elem(),
indices: make([]int, len(headers)),
structMode: false,
normalize: -1,
}
// get type information
t := p.ref.Type()
for i := 0; i < t.NumField(); i++ {
// get TSV tag
tsvtag := t.Field(i).Tag.Get("tsv")
if tsvtag != "" {
// find tsv position by header
for j := 0; j < len(headers); j++ {
if headers[j] == tsvtag {
// indices are 1 start
p.indices[j] = i + 1
p.structMode = true
}
}
}
}
if !p.structMode {
for i := 0; i < len(headers); i++ {
p.indices[i] = i + 1
}
}
return p, nil
}
与上面无标题行相比,多了解析tsv标签的逻辑。
下面开始解析每行数据,我们看Next()方法:
// Next puts reader forward by a line
func (p *Parser) Next() (eof bool, err error) {
// Get next record
var records []string
for {
// read until valid record
records, err = p.Reader.Read()
if err != nil {
if err.Error() == "EOF" {
return true, nil
}
return false, err
}
if len(records) > 0 {
break
}
}
if len(p.indices) == 0 {
p.indices = make([]int, len(records))
// mapping simple index
for i := 0; i < len(records); i++ {
p.indices[i] = i + 1
}
}
// record should be a pointer
for i, record := range records {
idx := p.indices[i]
if idx == 0 {
// skip empty index
continue
}
// get target field
field := p.ref.Field(idx - 1)
switch field.Kind() {
case reflect.String:
// Normalize text
if p.normalize >= 0 {
record = p.normalize.String(record)
}
field.SetString(record)
case reflect.Bool:
if record == "" {
field.SetBool(false)
} else {
col, err := strconv.ParseBool(record)
if err != nil {
return false, err
}
field.SetBool(col)
}
case reflect.Int:
if record == "" {
field.SetInt(0)
} else {
col, err := strconv.ParseInt(record, 10, 0)
if err != nil {
return false, err
}
field.SetInt(col)
}
default:
return false, errors.New("Unsupported field type")
}
}
return false, nil
}
上面主要逻辑就是通过反射解析并存储每行数据,并填充结构体的过程。这里仅考虑了string、bool、Int三种类型,当然我们可以扩展支持更多类型。
下面通过main函数进行测试:
import (
"fmt"
"os"
)
type TestRow struct {
Name string // 0
Age int // 1
Gender string // 2
Active bool // 3
}
func main() {
file, _ := os.Open("example.tsv")
defer file.Close()
data := TestRow{}
parser, _ := NewParser(file, &data)
for {
eof, err := parser.Next()
if eof {
return
}
if err != nil {
panic(err)
}
fmt.Println(data)
}
}
打开文件,定义结构体对象,然后定 * 析器,传入文件和结构体对象作为参数。解析结果存储在结构体对象中。上面代码参考tsv开源项目:https://github.com/dogenzaka/tsv。还有咱们更强大的开源库:https://github.com/shenwei356/csvtk,不仅解析CSV/TSV文件,还能实现不同格式的转换。
来源:https://blog.csdn.net/neweastsun/article/details/129654924


猜你喜欢
- 实例如下所示:>>> import pandas as pd>>> df = pd.DataFrame(
- 前言使用anaconda 命令安装pytorch包的问题,以下给出解决办法。状况描述:由于是第一次接触安装,看了很多篇文章,也遇到了一些坑,
- 目录1.根据默认的行列索引操作1.1行删除1.2列删除2.根据自定义的行列索引操作2.1行删除2.2列删除本文介绍Pandas中DataFr
- 经常看到说正则的文章,但说的只是方法,却很少有说以下几个基本概念:1.贪婪:+,*,?,{m,n}等默认是贪婪匹配,即尽可能多匹配,也叫最大
- 一、系统要求操作系统:Windows 2012 R2数据库:SqlServer2012web服务器:APACHE =>httpd-2.
- numpy作为python科学计算的基础模块,支撑起了pandas、matplotlib等使用。其中,ndarray作为numpy的重要
- python 远程统计文件#!/usr/bin/python#encoding=utf-8import timeimport osimpor
- 高效处理数据类型方法:处理数据In [1]: from random import randintIn [2]: data=[randint
- 文件可以传输,但是对比传输前后的文件:socket_test.txt,末尾有一些不一致服务端代码:#!/usr/bin/python# -*
- 实现原理PS的扩散效果可以产生类似毛玻璃质感的效果,使画面有些毛毛的感觉。其实现可通过操作像素三通道数值的方式实现,定义一个随机数器,将图像
- 背景一直以来,中式占卜都是基于算命先生手工实现,程序繁琐(往往需要沐浴、计算天时、静心等等流程)。准备工作复杂(通常需要铜钱等道具),计算方
- python数据结构之 列表和元组序列:序列是一种数据结构,它包含的元素都进行了编号(从0开始)。典型的序列包括列表、字符串和元组。其中,列
- mysql中的自增auto_increment功能相信每位phper都用过,也都知道如何设置字段为自增字段,但并不是所有phper都知道au
- 某日,路上收到用户咨询,为了清除空间,想删除某200多G大表数据,且已经确认此表不再有业务访问,于是执行了一条命令‘de
- 1.基本概念定义含义并发:concurrency,同一时刻只能有一条指令执行,但是多个线程的对应的指令被快速轮换地执行并行parallel,
- cooper谈到用户的视觉路径一般是:从上到下,从左到右。好的视觉设计路径应该是顺应这样的用户习惯,糟糕的设计会让用户无所适从,焦点到处都是
- 作为微软推出的网页与数据库解决方案,ASP由于有微软得天独厚的操作系统等技术后盾支持,因此得到了迅速的发展,并且正受到越来越多的欢迎,在目前
- Python操作注册表步骤之1.打开注册表对注册表进行操作前,必须打开注册表。在Python中,可以使用以下两个函数:RegOpenKey和
- 本文实例讲述了JS实现键值对遍历json数组功能。分享给大家供大家参考,具体如下:<!DOCTYPE html><html
- 安装jieba库教程jieba库是一款优秀的 Python 第三方中文分词库,jieba 支持三种分词模式:精确模式、全模式和搜索引擎模式,