Skip to content

Instantly share code, notes, and snippets.

@niratama
Created February 6, 2015 17:58
Show Gist options
  • Save niratama/39409a3992d341bc2359 to your computer and use it in GitHub Desktop.
Save niratama/39409a3992d341bc2359 to your computer and use it in GitHub Desktop.
goで正規表現を使ってstructにparseする実験
package main
import (
"fmt"
"reflect"
"time"
"strconv"
"regexp"
)
const (
LINE_FORMAT = `^(?P<TimeStamp>\S+ \S+) (?P<Name>.+)=(?P<Score>.+) (?P<Gender>.+)$`
TIME_FORMAT = "2006-01-02 15:04:05"
)
type Gender uint8
type Score struct {
TimeStamp time.Time
Name string
Score int
Level uint8
Gender Gender
}
type DataTmpl interface{}
type Parser struct {
re *regexp.Regexp
timeFormat string
dataType reflect.Type
}
func NewParser(dataTmpl DataTmpl, reFmt, timeFmt string) (*Parser, error) {
re, err := regexp.Compile(reFmt)
if err != nil {
return nil, err
}
return &Parser{re, timeFmt, reflect.ValueOf(dataTmpl).Elem().Type()}, nil
}
func (p *Parser) parseInt(v reflect.Value, s string) (err error) {
n, err := strconv.ParseInt(s, 10, v.Type().Bits())
if err == nil {
v.SetInt(int64(n))
}
return
}
func (p *Parser) parseUint(v reflect.Value, s string) (err error) {
n, err := strconv.ParseUint(s, 10, v.Type().Bits())
if err == nil {
v.SetUint(uint64(n))
}
return
}
func (p *Parser) parseTime(v reflect.Value, s string) (err error) {
t, err := time.Parse(p.timeFormat, s)
if err == nil {
v.Set(reflect.ValueOf(t))
}
return
}
func (p *Parser) Parse(line string) (interface{}, error) {
dataValue := reflect.New(p.dataType)
pve := dataValue.Elem()
fmt.Println(dataValue, pve)
match := p.re.FindStringSubmatch(line)
if len(match) == 0 {
return nil, fmt.Errorf("invalid string: %q", line)
}
for i, field := range p.re.SubexpNames()[1:] {
if field == "" {
continue
}
s := match[i + 1]
v := pve.FieldByName(field)
if ! v.IsValid() {
return nil, fmt.Errorf("field %q not found", field)
}
if ! v.CanSet() {
return nil, fmt.Errorf("field %q can't set value", field)
}
var err error
switch v.Kind() {
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
err = p.parseInt(v, s)
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
err = p.parseUint(v, s)
case reflect.String:
v.SetString(s)
case reflect.Struct:
switch v.Type().String() {
case "time.Time":
err = p.parseTime(v, s)
default:
return nil, fmt.Errorf("field %q: unsupported type %s", field, v)
}
default:
return nil, fmt.Errorf("field %q: unsupported type %s", field, v)
}
if err != nil {
return nil, fmt.Errorf("field %q: %s", field, err)
}
}
return dataValue.Interface(), nil
}
func main() {
scores := []string{
"2015-02-03 12:34:56 Niratama=100 1",
"2015-02-04 11:22:33 Kanitama=256 2516",
"2015-02-04 11:22:33 Kanitama=256 -16",
"2015-02-05 12:34:56 Niratama=ZZZ 1",
"9999-99-99 99:99:99 999=999 99",
}
parser, err := NewParser((DataTmpl)(&Score{}), LINE_FORMAT, TIME_FORMAT)
if err != nil {
fmt.Println(err)
}
for _, line := range scores {
fmt.Println(line)
data, err := parser.Parse(line)
if err == nil {
score := data.(*Score)
fmt.Println(score)
} else {
fmt.Println(err)
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment