Golang解析日期来自字符串中的子字符串

时间:2014-02-17 13:37:15

标签: parsing time go

我正在编写一个日志文件解析器,并编写了一些测试代码以在C中解析它。

要解析的字符串如下所示:

s := `10.0.0.1 Jan 11 2014 10:00:00 hello`

在C中,解析这个很容易。首先,我找到指向字符串中日期的指针,然后使用strptime()尽可能多地使用它。这是可能的,因为strptime()将在调用后返回字符串中的位置。

最终我决定使用Golang而不是C,但在移植代码时我遇到了一些问题。据我所知,time.Parse()没有给我任何选项来解析现有的字符串(虽然这可以通过切片解决)或指示它在解析日期时消耗了多少原始字符串从字符串中。

Go中是否有任何优雅的方式我可以直接解析字符串中的日期/时间,而无需先将日期时间提取到精确的切片中,例如通过返回解析后提取的字符数?

2 个答案:

答案 0 :(得分:3)

不幸的是,time.Parse方法无法告诉您解析了多少个字符,因此我们需要调查其他优雅的解决方案。在您解析日志语句的示例中,正如@ rob74建议的那样使用正则表达式是一种相当优雅的策略。以下示例为简洁起见忽略了错误:

var r = regexp.MustCompile(`^((?:\d{1,3}\.){3}\d{1,3}) ([a-zA-Z]{3} \d{1,2} \d{4} \d{1,2}:\d{2}:\d{2}) (.*)`)
const longForm = "Jan 02 2006 15:04:05"

func parseRegex(s string) (ip, msg string, t time.Time) {
    m := r.FindStringSubmatch(s)
    t, _ = time.Parse(longForm, m[2])
    ip, msg = m[1], m[3]
    return ip, msg, t
}

基准测试显示上面的正则表达式比我机器上的@ rob74示例效率高两倍左右,每秒解析大约100,000行:

BenchmarkParseRegex           100000         17130 ns/op
BenchmarkParseRegexRob74       50000         32788 ns/op

但是,如果我们使用strings.SplitN,我们可以保持解决方案的简短和高效。例如:

func parseSplit(s string) (ip, msg string, t time.Time) {
    parts := strings.SplitN(s, " ", 6)
    t, _ = time.Parse(longForm, strings.Join(parts[1:5], " "))
    ip, msg = parts[0], parts[5]
    return ip, msg, t
}

这会将字符串拆分为前5个空格,并将剩余的字符串(消息部分)放在最终的parts切片元素中。这不是很优雅,因为我们依赖于日期格式中的空格数,但我们可以通过编程方式计算日期格式字符串中的空格,以获得更通用的解决方案。让我们看看这与我们的正则表达式解决方案相比如何:

BenchmarkParseRegex   100000         17130 ns/op
BenchmarkParseSplit   500000          3557 ns/op
事实证明,它相当不错。使用SplitN比使用正则表达式快大约五倍,并且仍然可以生成简洁易读的代码。这样做的代价是为切片分配使用稍多的内存。

答案 1 :(得分:0)

也许您应该考虑使用正则表达式来分割日志行,例如:

package main

import "fmt"
import "time"
import "regexp"

func main() {
    s := "10.0.0.1 Jan 11 2014 10:00:00 hello"
    r := regexp.MustCompile("^([^/w]+) ([a-zA-Z]+ [0-9]{1,2} [0-9]{4} [0-9]{1,2}:[0-9]{2}:[0-9]{2}) (.*)")
    m := r.FindStringSubmatch(s)
    if len(m) >= 4 {
        fmt.Println("IP:", m[1])
        fmt.Println("Timestamp:", m[2])
        fmt.Println("Message:", m[3])
        t, err := time.Parse("Jan 02 2006 15:04:05", m[2])
        if err != nil {
            fmt.Println(err.Error())
        } else {
            fmt.Println("Parsed Time:",t)
        }
    } else {
           fmt.Println("Regexp mismatch!")
    }
}

http://play.golang.org/p/EP-waAPGB4