Parsing 从字符串中的子字符串转到分析日期

Parsing 从字符串中的子字符串转到分析日期,parsing,time,go,Parsing,Time,Go,我正在编写一个日志文件解析器,并且已经编写了一些测试代码来用C解析它 要分析的字符串如下所示: s := `10.0.0.1 Jan 11 2014 10:00:00 hello` 在C语言中,就地解析是相当容易的。首先,我在字符串中找到指向日期的指针,然后使用strtime()尽可能多地使用它。这是可能的,因为调用后strptime()将返回字符串中的位置 最后,我决定使用go而不是C,但是在移植代码时,我遇到了一些问题。据我所知,time.Parse()没有给我任何从现有字符串中解析的选项

我正在编写一个日志文件解析器,并且已经编写了一些测试代码来用C解析它

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

s := `10.0.0.1 Jan 11 2014 10:00:00 hello`
在C语言中,就地解析是相当容易的。首先,我在字符串中找到指向日期的指针,然后使用strtime()尽可能多地使用它。这是可能的,因为调用后strptime()将返回字符串中的位置

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


是否有任何优雅的方法,我可以将字符串中的日期/时间解析出来,而不必首先将DATE时间提取到一个精确的切片中,例如返回解析后提取的字符数?

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

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!")
    }
}

不幸的是,
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示例的两倍,每秒解析大约100000行:

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

BenchmarkParseRegex   100000         17130 ns/op
BenchmarkParseSplit   500000          3557 ns/op

事实证明,相比之下,这是相当有利的。使用
SplitN
的速度大约是使用正则表达式的五倍,并且仍然可以生成简洁易读的代码。它这样做的代价是为片分配使用稍多的内存。

1<代码>时间。解析
允许解析/跳过固定的“噪音”。2.在第一个空间中分割你的线条实在是太简单了,我认为这是你正在寻找的优雅的解决方案。够公平的,但是约会后的噪音呢?因为我想在数据中的精确位置继续解析,所以我需要知道time.Parse在切片中的结束位置。