golang固定宽度文本编码解码反射插件库go-fixedwidth的使用
golang固定宽度文本编码解码反射插件库go-fixedwidth的使用
Package fixedwidth 提供了对固定宽度格式数据的编码和解码功能。
安装
go get github.com/ianlopshire/go-fixedwidth
结构体标签
fixedwidth 使用的结构体标签格式为:fixed:"{startPos},{endPos},[{alignment},[{padChar}]]"
。
startPos
和endPos
参数控制行内的位置,必须是大于0的正整数,位置从1开始,区间是包含的alignment
参数控制值在其区间内的对齐方式,有效选项为default
、right
、left
和none
(可选)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 |
字段左对齐 | 不修剪值的填充字符。适用于嵌套结构体 |
注意事项
{}
表示一个参数,[]
表示可选部分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使用反射实现,对于高性能场景,可以考虑以下优化:
- 预编译编码器/解码器
- 避免在循环中频繁创建临时结构体
- 对于非常大的文件,考虑流式处理
总结
go-fixedwidth提供了简单而强大的方式来处理固定宽度文本数据,通过结构体标签可以方便地定义字段位置和对齐方式。它特别适合处理传统系统、银行文件、旧式数据交换格式等固定宽度文本场景。
对于更复杂的需求,可以结合自定义类型和编解码方法实现灵活的数据转换。