golang正则表达式命名分组匹配与结构体自动解析插件库regroup的使用

Golang正则表达式命名分组匹配与结构体自动解析插件库regroup的使用

regroup是一个简单的库,可以使用结构体标签和自动解析将正则表达式命名分组匹配到Go结构体中。

安装

go get github.com/oriser/regroup

示例

命名分组映射

package main

import (
	"fmt"
	"github.com/oriser/regroup"
)

var re = regroup.MustCompile(`(?P<duration>.*?)\s+(?P<num>\d+)\s+(?P<foo>.*)`)

func main() {
	matches, err := re.Groups("5s 123 bar")
	if err != nil {
		panic(err)
	}
	fmt.Printf("%+v\n", matches)
}

输出:

map[duration:5s foo:bar num:123]

单次匹配

package main

import (
	"fmt"
	"github.com/oriser/regroup"
	"time"
)

var re = regroup.MustCompile(`(?P<duration>.*?)\s+(?P<num>\d+)\s+(?P<foo>.*)`)

type B struct {
	Str string `regroup:"foo"`
}

type A struct {
	Number        int           `regroup:"num"`
	Dur           time.Duration `regroup:"duration"`
	AnotherStruct B
}

func main() {
	a := &A{}
	if err := re.MatchToTarget("5s 123 bar", a); err != nil {
		panic(err)
	}
	fmt.Printf("%+v\n", a)
}

输出:

&{Number:123 Dur:5s AnotherStruct:{Str:bar}}

多次匹配

你可以获取所有匹配项并解析为给定的目标结构体。返回值在这种情况下将是一个接口数组,你应该将其转换为目标类型以访问其字段。

package main

import (
	"fmt"
	"github.com/oriser/regroup"
	"time"
)

var re = regroup.MustCompile(`\s*(?P<duration>.*?)\s+(?P<num>\d+)\s+(?P<foo>.*)`)

type B struct {
	Str string `regroup:"foo"`
}

type A struct {
	Number        int           `regroup:"num"`
	Dur           time.Duration `regroup:"duration"`
	AnotherStruct B
}

func main() {
	a := &A{}
	s := `5s 123 bar1
		  1m 456 bar2
		  10h 789 bar3`
	rets, err := re.MatchAllToTarget(s, -1, a)
	if err != nil {
		panic(err)
	}
	for _, elem := range rets {
		fmt.Printf("%+v\n", elem.(*A))
	}
}

输出:

&{Number:123 Dur:5s AnotherStruct:{Str:bar1}}
&{Number:456 Dur:1m0s AnotherStruct:{Str:bar2}}
&{Number:789 Dur:10h0m0s AnotherStruct:{Str:bar3}}

必填分组

你可以指定某个分组是必填的,意味着它不能为空。如果必填分组为空,将返回一个错误(*regroup.RequiredGroupIsEmpty)。

package main

import (
	"fmt"
	"github.com/oriser/regroup"
	"time"
)

var re = regroup.MustCompile(`(?P<duration>.*?)\s+(?P<num>\d+)\s+(?P<foo>.*)`)

type B struct {
	Str string `regroup:"foo,required"`
}

type A struct {
	Number        int           `regroup:"num"`
	Dur           time.Duration `regroup:"duration"`
	AnotherStruct B
}

func main() {
	a := &A{}
	if err := re.MatchToTarget("5s 123 ", a); err != nil {
		panic(err)
	}
	fmt.Printf("%+v\n", a)
}

将返回错误: required regroup "foo" is empty for field "Str"

存在性匹配分组

你可以使用带有exists标签的bool来检查可选分组是否存在。

package main

type Exist struct {
	IsAdmin bool `regroup:"is_admin,exists"`
}

func main() {
	r := MustCompile(`^(?P<name>\w*)(?:,(?P<is_admin>admin))?$`)
	parsed := &Exist{}
	if err := r.MatchToTarget("bob_smith", parsed); err != nil {
		panic(err)
	}
	fmt.Printf("%t\n", parsed.IsAdmin)
}

这个例子会打印false。但是如果输入是bob_smith,admin,它会打印true。当使用exists标签时,请确保你的正则表达式有一个可选分组并匹配所有预期的输入模式。

支持的结构体字段类型

  • time.Duration
  • bool
  • string
  • int
  • int8
  • int16
  • int32
  • int64
  • uint
  • uint8
  • uint16
  • uint32
  • uint64
  • float32
  • float64

指针和嵌套结构体也支持,包括单次匹配和多次匹配。


更多关于golang正则表达式命名分组匹配与结构体自动解析插件库regroup的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang正则表达式命名分组匹配与结构体自动解析插件库regroup的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


Golang 正则表达式命名分组匹配与结构体自动解析 - regroup 库使用指南

在 Golang 中处理正则表达式时,命名分组匹配和自动解析到结构体可以大大提高代码的可读性和维护性。regroup 库正是为此而设计的优秀工具。

1. 基本概念

regroup 是一个 Golang 库,它允许你:

  • 使用命名分组正则表达式
  • 自动将匹配结果映射到结构体
  • 简化正则匹配结果的提取过程

2. 安装

go get github.com/oriser/regroup

3. 基本用法

3.1 简单示例

package main

import (
	"fmt"
	"github.com/oriser/regroup"
)

type UserInfo struct {
	Name    string `regroup:"name"`
	Age     int    `regroup:"age"`
	Country string `regroup:"country"`
}

func main() {
	re := regroup.MustCompile(`(?P<name>\w+) is (?P<age>\d+) years old from (?P<country>\w+)`)
	
	var user UserInfo
	err := re.MatchToStruct("Alice is 25 years old from Canada", &user)
	if err != nil {
		panic(err)
	}
	
	fmt.Printf("%+v\n", user)
	// 输出: {Name:Alice Age:25 Country:Canada}
}

3.2 更复杂的示例

package main

import (
	"fmt"
	"github.com/oriser/regroup"
)

type LogEntry struct {
	IP        string `regroup:"ip"`
	Timestamp string `regroup:"timestamp"`
	Method    string `regroup:"method"`
	Path      string `regroup:"path"`
	Status    int    `regroup:"status"`
}

func main() {
	re := regroup.MustCompile(
		`^(?P<ip>\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}) ` +
		`- - \[(?P<timestamp>[^\]]+)\] ` +
		`"(?P<method>\w+) (?P<path>[^"]+) HTTP/\d\.\d" ` +
		`(?P<status>\d{3})`,
	)
	
	logLine := `127.0.0.1 - - [10/Oct/2023:13:55:36 +0000] "GET /api/users HTTP/1.1" 200`
	
	var entry LogEntry
	err := re.MatchToStruct(logLine, &entry)
	if err != nil {
		panic(err)
	}
	
	fmt.Printf("%+v\n", entry)
	// 输出: {IP:127.0.0.1 Timestamp:10/Oct/2023:13:55:36 +0000 Method:GET Path:/api/users Status:200}
}

4. 高级特性

4.1 可选字段

type OptionalFields struct {
	Required string `regroup:"required"`
	Optional string `regroup:"optional,?optional"`
}

func main() {
	re := regroup.MustCompile(`(?P<required>\w+)(?: and (?P<optional>\w+))?`)
	
	var withOptional OptionalFields
	re.MatchToStruct("one and two", &withOptional)
	fmt.Printf("%+v\n", withOptional) // {Required:one Optional:two}
	
	var withoutOptional OptionalFields
	re.MatchToStruct("one", &withoutOptional)
	fmt.Printf("%+v\n", withoutOptional) // {Required:one Optional:}
}

4.2 自定义类型

type CustomType string

func (c *CustomType) UnmarshalText(text []byte) error {
	*c = CustomType("custom_" + string(text))
	return nil
}

type CustomStruct struct {
	Field CustomType `regroup:"field"`
}

func main() {
	re := regroup.MustCompile(`value: (?P<field>\w+)`)
	
	var s CustomStruct
	re.MatchToStruct("value: test", &s)
	
	fmt.Printf("%+v\n", s) // {Field:custom_test}
}

5. 性能考虑

regroup 在内部使用了标准库的 regexp 包,因此性能与标准正则表达式相当。主要开销在于反射操作,但在大多数应用场景中是可以接受的。

6. 与其他库的比较

  • 标准库 regexp: 需要手动处理匹配结果
  • go-re2: 提供命名捕获组但不自动映射到结构体
  • regroup: 提供最简洁的结构体映射方式

7. 最佳实践

  1. 为复杂的正则表达式添加注释
  2. 对频繁使用的正则表达式进行预编译
  3. 处理错误情况而不仅仅是 panic
  4. 为可选字段提供默认值

8. 总结

regroup 库为 Golang 的正则表达式处理提供了更加优雅和类型安全的方式,特别适合处理结构化文本数据。通过命名分组和结构体标签,它可以显著提高代码的可读性和可维护性。

对于需要从文本中提取结构化数据的场景,regroup 是一个非常值得考虑的工具。

回到顶部