Created
February 6, 2015 17:58
-
-
Save niratama/39409a3992d341bc2359 to your computer and use it in GitHub Desktop.
goで正規表現を使ってstructにparseする実験
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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