golang固定宽度文本编码解码反射插件库go-fixedwidth的使用

golang固定宽度文本编码解码反射插件库go-fixedwidth的使用

Package fixedwidth 提供了对固定宽度格式数据的编码和解码功能。

安装

go get github.com/ianlopshire/go-fixedwidth

结构体标签

fixedwidth 使用的结构体标签格式为:fixed:"{startPos},{endPos},[{alignment},[{padChar}]]"

  • startPosendPos 参数控制行内的位置,必须是大于0的正整数,位置从1开始,区间是包含的
  • alignment 参数控制值在其区间内的对齐方式,有效选项为 defaultrightleftnone(可选)
  • padChar 参数控制写入值后用于填充区间内任何空字符的字符,默认填充字符是空格(可选)

没有标签的字段将被忽略。

编码示例

// 定义要编码的数据
people := []struct {
    ID        int     `fixed:"1,5"`
    FirstName string  `fixed:"6,15"`
    LastName  string  `fixed:"16,25"`
    Grade     float64 `fixed:"26,30"`
    Age       uint    `fixed:"31,33"`
    Alive     bool    `fixed:"34,39"`
}{
    {1, "Ian", "Lopshire", 99.5, 20, true},
}

data, err := Marshal(people)
if err != nil {
    log.Fatal(err)
}
fmt.Printf("%s", data)
// 输出:
// 1    Ian       Lopshire  99.5020 true

解码示例

// 定义格式
var people []struct {
    ID        int     `fixed:"1,5"`
    FirstName string  `fixed:"6,15"`
    LastName  string  `fixed:"16,25"`
    Grade     float64 `fixed:"26,30"`
    Age       uint    `fixed:"31,33"`
    Alive     bool    `fixed:"34,39"`
    Github    bool    `fixed:"40,41"`
}

// 定义要解析的固定宽度数据
data := []byte("" +
    "1    Ian       Lopshire  99.50 20 false f" + "\n" +
    "2    John      Doe       89.50 21 true t" + "\n" +
    "3    Jane      Doe       79.50 22 false F" + "\n" +
    "4    Ann       Carraway  79.59 23 false T" + "\n")

err := Unmarshal(data, &people)
if err != nil {
    log.Fatal(err)
}

fmt.Printf("%+v\n", people[0])
fmt.Printf("%+v\n", people[1])
fmt.Printf("%+v\n", people[2])
fmt.Printf("%+v\n", people[3])
// 输出:
//{ID:1 FirstName:Ian LastName:Lopshire Grade:99.5 Age:20 Alive:false Github:false}
//{ID:2 FirstName:John LastName:Doe Grade:89.5 Age:21 Alive:true Github:true}
//{ID:3 FirstName:Jane LastName:Doe Grade:79.5 Age:22 Alive:false Github:false}
//{ID:4 FirstName:Ann LastName:Carraway Grade:79.59 Age:23 Alive:false Github:true}

也可以增量读取数据:

decoder := fixedwidth.NewDecoder(bytes.NewReader(data))
for {
    var element myStruct
    err := decoder.Decode(&element)
    if err == io.EOF {
        break
    }
    if err != nil {
        log.Fatal(err)
    }
    handle(element)
}

UTF-8、码点和多字节字符

fixedwidth 支持编码和解码索引以unicode码点而非原始字节表示的固定宽度数据。数据必须是UTF-8编码。

decoder := fixedwidth.NewDecoder(strings.NewReader(data))
decoder.SetUseCodepointIndices(true)
// 现在可以正常解码了
buff := new(bytes.Buffer)
encoder := fixedwidth.NewEncoder(buff)
encoder.SetUseCodepointIndices(true)
// 现在可以正常编码了

对齐行为

对齐方式 编码 解码
default 字段左对齐 从值的左右两侧修剪填充字符
left 字段左对齐 从值的右侧修剪填充字符
right 字段右对齐 从值的左侧修剪填充字符
none 字段左对齐 不修剪值的填充字符。适用于嵌套结构体

注意事项

  1. {} 表示一个参数,[] 表示可选部分
  2. default 对齐方式类似于 left,但为了保持向后兼容性,行为略有不同

许可证

MIT


更多关于golang固定宽度文本编码解码反射插件库go-fixedwidth的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang固定宽度文本编码解码反射插件库go-fixedwidth的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


go-fixedwidth库使用指南

go-fixedwidth是一个用于处理固定宽度文本的Go语言库,它提供了编码(序列化)和解码(反序列化)功能,支持通过结构体标签定义字段位置。

安装

go get github.com/ianlopshire/go-fixedwidth

基本用法

1. 解码固定宽度文本

package main

import (
	"fmt"
	"strings"
	
	"github.com/ianlopshire/go-fixedwidth"
)

type Person struct {
	ID        int     `fixed:"1,5"`
	Name      string  `fixed:"6,15"`
	Age       int     `fixed:"16,18"`
	Salary    float64 `fixed:"19,25"`
	IsActive  bool    `fixed:"26,26"`
}

func main() {
	data := `00123John Doe     301234.561`
	
	var p Person
	err := fixedwidth.Unmarshal([]byte(data), &p)
	if err != nil {
		panic(err)
	}
	
	fmt.Printf("%+v\n", p)
	// 输出: {ID:123 Name:John Doe Age:30 Salary:1234.56 IsActive:true}
}

2. 编码为固定宽度文本

func main() {
	p := Person{
		ID:       456,
		Name:     "Jane Smith",
		Age:      28,
		Salary:   9876.54,
		IsActive: false,
	}
	
	encoded, err := fixedwidth.Marshal(p)
	if err != nil {
		panic(err)
	}
	
	fmt.Println(string(encoded))
	// 输出: 00456Jane Smith  289876.540
}

高级特性

1. 自定义对齐和填充

type Product struct {
	Code      string  `fixed:"1,10,left"`    // 左对齐,右侧填充空格
	Price     float64 `fixed:"11,20,right"`  // 右对齐,左侧填充空格
	Available bool    `fixed:"21,21,center"` // 居中对齐
}

func main() {
	prod := Product{
		Code:      "A123",
		Price:     19.99,
		Available: true,
	}
	
	encoded, _ := fixedwidth.Marshal(prod)
	fmt.Println(string(encoded))
	// 输出: A123         19.99   1
}

2. 嵌套结构体

type Address struct {
	Street string `fixed:"1,20"`
	City   string `fixed:"21,35"`
}

type Customer struct {
	Name    string  `fixed:"1,15"`
	Address Address `fixed:"16,50"`
	Phone   string  `fixed:"51,65"`
}

func main() {
	data := `John Doe      123 Main St    New York      555-1234`
	
	var c Customer
	_ = fixedwidth.Unmarshal([]byte(data), &c)
	
	fmt.Printf("%+v\n", c)
	// 输出: {Name:John Doe Address:{Street:123 Main St City:New York} Phone:555-1234}
}

3. 自定义类型处理

type YesNo bool

func (yn *YesNo) UnmarshalFixed(data []byte) error {
	*yn = string(data) == "Y"
	return nil
}

func (yn YesNo) MarshalFixed() ([]byte, error) {
	if yn {
		return []byte("Y"), nil
	}
	return []byte("N"), nil
}

type Survey struct {
	ID     int  `fixed:"1,5"`
	Agreed YesNo `fixed:"6,6"`
}

func main() {
	data := `00123Y`
	
	var s Survey
	_ = fixedwidth.Unmarshal([]byte(data), &s)
	
	fmt.Printf("%+v\n", s) // 输出: {ID:123 Agreed:true}
	
	s.Agreed = false
	encoded, _ := fixedwidth.Marshal(s)
	fmt.Println(string(encoded)) // 输出: 00123N
}

错误处理

func main() {
	data := `0012XJohn Doe     301234.561` // ID字段应该是数字但包含X
	
	var p Person
	err := fixedwidth.Unmarshal([]byte(data), &p)
	if err != nil {
		fmt.Println("解码错误:", err)
		// 输出: 解码错误: strconv.ParseInt: parsing "0012X": invalid syntax
	}
}

性能考虑

go-fixedwidth使用反射实现,对于高性能场景,可以考虑以下优化:

  1. 预编译编码器/解码器
  2. 避免在循环中频繁创建临时结构体
  3. 对于非常大的文件,考虑流式处理

总结

go-fixedwidth提供了简单而强大的方式来处理固定宽度文本数据,通过结构体标签可以方便地定义字段位置和对齐方式。它特别适合处理传统系统、银行文件、旧式数据交换格式等固定宽度文本场景。

对于更复杂的需求,可以结合自定义类型和编解码方法实现灵活的数据转换。

回到顶部