Created
March 28, 2017 00:15
-
-
Save ifukazoo/e207c684d72f163c6ad8c965df545b6d to your computer and use it in GitHub Desktop.
crontab形式のパース
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 ( | |
"bufio" | |
"errors" | |
"fmt" | |
"os" | |
"sort" | |
"strconv" | |
"strings" | |
) | |
var ( | |
scanner *bufio.Scanner | |
) | |
func init() { | |
scanner = bufio.NewScanner(os.Stdin) | |
} | |
func eSplitLine() ([]string /*eof*/, bool) { | |
if !scanner.Scan() { | |
if err := scanner.Err(); err != nil { | |
os.Exit(1) | |
} | |
return []string{}, true | |
} | |
return strings.Split(scanner.Text(), " "), false | |
} | |
// CronTabFmt crontab フォーマット | |
type CronTabFmt interface { | |
GetValues() []int | |
} | |
// ScalFmt スカラーフォーマット | |
type ScalFmt int | |
// GetValues 単一の値を返す | |
func (i ScalFmt) GetValues() []int { | |
return []int{int(i)} | |
} | |
// RangeFmt 範囲を持つフォーマット | |
type RangeFmt struct { | |
begin int | |
end int | |
} | |
// GetValues 範囲内の値を返す | |
func (r RangeFmt) GetValues() []int { | |
var hours []int | |
for i := r.begin; i <= r.end; i++ { | |
hours = append(hours, i) | |
} | |
return hours | |
} | |
// IntervalFmt 範囲と間隔を持つフォーマット | |
type IntervalFmt struct { | |
begin int | |
end int | |
interval int | |
} | |
// GetValues 範囲内の有効な値を返す | |
func (interval IntervalFmt) GetValues() []int { | |
var hours []int | |
for i := interval.begin; i <= interval.end; i += interval.interval { | |
hours = append(hours, i) | |
} | |
return hours | |
} | |
func parseScal(input string) (int, error) { | |
var ( | |
i int | |
err error | |
) | |
if i, err = strconv.Atoi(input); err != nil { | |
return 0, errors.New("parseScal error:" + input) | |
} | |
return i, nil | |
} | |
func parseRange(input string) ([]int, error) { | |
if input == "*" { | |
return []int{0, 23}, nil | |
} | |
beginAndEnd := strings.Split(input, "-") | |
if len(beginAndEnd) != 2 { | |
return []int{}, errors.New("parseRange error:" + input) | |
} | |
var ( | |
begin, end int | |
err error | |
) | |
if begin, err = parseScal(beginAndEnd[0]); err != nil { | |
return []int{}, err | |
} | |
if end, err = parseScal(beginAndEnd[1]); err != nil { | |
return []int{}, err | |
} | |
return []int{begin, end}, nil | |
} | |
func parseInterval(input string) ([]int, error) { | |
numerAndDenom := strings.Split(input, "/") | |
if len(numerAndDenom) != 2 { | |
return []int{}, errors.New("parseInterval error:" + input) | |
} | |
var ( | |
beginAndEnd []int | |
interval int | |
err error | |
) | |
if beginAndEnd, err = parseRange(numerAndDenom[0]); err != nil { | |
return []int{}, err | |
} | |
if interval, err = strconv.Atoi(numerAndDenom[1]); err != nil { | |
return []int{}, err | |
} | |
return []int{beginAndEnd[0], beginAndEnd[1], interval}, nil | |
} | |
func parseScalFmt(input string) (ScalFmt, error) { | |
val, err := parseScal(input) | |
return ScalFmt(val), err | |
} | |
func parseRangeFmt(input string) (RangeFmt, error) { | |
var ( | |
val []int | |
err error | |
) | |
if val, err = parseRange(input); err != nil { | |
return RangeFmt{}, err | |
} | |
return RangeFmt{val[0], val[1]}, nil | |
} | |
func parseIntervalFmt(input string) (IntervalFmt, error) { | |
var ( | |
val []int | |
err error | |
) | |
if val, err = parseInterval(input); err != nil { | |
return IntervalFmt{}, err | |
} | |
return IntervalFmt{val[0], val[1], val[2]}, nil | |
} | |
func parseField(field string) ([]CronTabFmt, error) { | |
var ( | |
fmts []CronTabFmt | |
s ScalFmt | |
r RangeFmt | |
i IntervalFmt | |
err error | |
) | |
strs := strings.Split(field, ",") | |
for _, v := range strs { | |
// try IntervalFmt | |
if i, err = parseIntervalFmt(v); err == nil { | |
fmts = append(fmts, i) | |
continue | |
} | |
// try RangeFmt | |
if r, err = parseRangeFmt(v); err == nil { | |
fmts = append(fmts, r) | |
continue | |
} | |
// try ScalFmt | |
if s, err = parseScalFmt(v); err == nil { | |
fmts = append(fmts, s) | |
continue | |
} | |
// error! | |
return []CronTabFmt{}, err | |
} | |
return fmts, nil | |
} | |
func getHourValues(field string) ([]int, error) { | |
var ( | |
crontabFmt []CronTabFmt | |
err error | |
) | |
if crontabFmt, err = parseField(field); err != nil { | |
return []int{}, err | |
} | |
// map を setとして利用する | |
hourSet := make(map[int]int) | |
for _, fmt := range crontabFmt { | |
nums := fmt.GetValues() | |
for _, n := range nums { | |
if 0 <= n && n < 24 { // 0〜23の値にフィルター | |
hourSet[n] = 0 // 0は使用しない値 | |
} | |
} | |
} | |
// set から値を取り出してソートして返す | |
var values []int | |
for h := range hourSet { | |
values = append(values, h) | |
} | |
sort.Ints(values) | |
return values, nil | |
} | |
func main() { | |
for { | |
var ( | |
fields []string | |
eof bool | |
) | |
if fields, eof = eSplitLine(); eof { | |
break | |
} | |
var ( | |
err error | |
hours []int | |
) | |
if hours, err = getHourValues(fields[1]); err != nil { | |
os.Exit(1) | |
} | |
sep := "" | |
for _, h := range hours { | |
fmt.Printf("%v%v", sep, h) | |
sep = " " | |
} | |
fmt.Println("") | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment