duanmu1736 2014-02-17 13:37
浏览 272
已采纳

Golang从字符串中的子字符串解析日期

I am writing a log file parser, and have written some test code to parse this in C.

The string to be parsed looks as follows:

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

In C, parsing this in place is quite easy. First I find the pointer to the date within the string and then just consume as much as possible using strptime(). This is possible as strptime() will return the position in the string after the call.

Eventually I decided to go with Golang instead of C, but while porting the code over I have some issues. As far as I can tell, time.Parse() does not give me any option to parse from within an existing string (though this can be solved with slices) or indication about how much of the original string it have consumed when parsing the date from within the string.

Is there any elegant way in Go I can parse the date/time right out of the string without having to first extract the datetime into an exact slice e.g. by returning the number of characters extracted after parsing?

  • 写回答

2条回答 默认 最新

  • duananyu9231 2014-05-25 14:15
    关注

    Unfortunately, the time.Parse method can't tell you how many characters it parsed, so we will need to investigate other elegant solutions. In your example of parsing log statements, the use of regular expressions, as @rob74 suggested, is a reasonably elegant strategy. The example below ignores errors for brevity:

    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
    }
    

    Benchmarks show the above regular expression to be about two times more efficient than @rob74's example on my machine, parsing about a 100,000 lines per second:

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

    We can, however, keep the solution short and more efficient if we use strings.SplitN instead. For example:

    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
    }
    

    This splits the string on the first 5 spaces and puts the remaining string (the message part) inside the final parts slice element. This is not very elegant, since we rely on the number of spaces in the date format, but we could count the spaces in the date format string programmatically for a more general solution. Let's see how this compares to our regular expression solution:

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

    It compares quite favorably, as it turns out. Using SplitN is about five times faster than using regular expressions, and still results in concise and readable code. It does this at the cost of using slightly more memory for the slice allocation.

    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论
查看更多回答(1条)

报告相同问题?

悬赏问题

  • ¥15 执行 virtuoso 命令后,界面没有,cadence 启动不起来
  • ¥50 comfyui下连接animatediff节点生成视频质量非常差的原因
  • ¥20 有关区间dp的问题求解
  • ¥15 多电路系统共用电源的串扰问题
  • ¥15 slam rangenet++配置
  • ¥15 有没有研究水声通信方面的帮我改俩matlab代码
  • ¥15 ubuntu子系统密码忘记
  • ¥15 保护模式-系统加载-段寄存器
  • ¥15 电脑桌面设定一个区域禁止鼠标操作
  • ¥15 求NPF226060磁芯的详细资料