douqianrou9079
douqianrou9079
2019-07-21 20:11

为什么在使用fmt.Fscanf时出现“输入格式不匹配”​​的提示?

已采纳

I'm trying to use fmt.Fscanf but I'm having hard time figuring out how. Having the following code:

package main

import (
    "fmt"
    "strings"
)

func main() {
    var host, user, date, httpStr string
    var code, size int

    r := strings.NewReader(`127.0.0.1 - james [09/May/2018:16:00:39 +0000] "GET /report HTTP/1.0" 200 123`)

    _, err := fmt.Fscanf(r, `%s - %s [%s], "%s" %d %d`,
        &host, &user, &date, &httpStr, &code, &size)
    if err != nil {
        fmt.Printf("Failed to parse log line, error: %+v
", err)
        panic(err)
    }

    fmt.Println(host, user, date, httpStr, code, size)

}

Go playground: https://play.golang.org/p/zGxc6MXOF3a

I get:

Failed to parse log line, error: input does not match format
panic: input does not match format

goroutine 1 [running]:
main.main()
        .../fscanf/main.go:19 +0x57b
exit status 2

Why?

  • 点赞
  • 写回答
  • 关注问题
  • 收藏
  • 复制链接分享
  • 邀请回答

1条回答

  • dourong6054 dourong6054 2年前

    The error comes from how Fscanf parses space-separated strings. This becomes an issue when reading the date and HTTP string:

    When it reads the date, instead of reading 09/May/2018:16:00:39 +0000 it will read until the first space: 09/May/2018:16:00:39 then expect ].

    To fix this, it would make sense to parse the string as [%s %s], but this would fail more subtly. It would parse the timezone as +0000] because it stops at the first space, not according to the format string. The same issue occurs with the HTTP string: it stops parsing after the verb GET, then after the route /report, but when reading the protocol it reads HTTP/1.0" not HTTP/1.0.

    A working solution is as follows:

    package main
    
    import (
        "fmt"
        "log"
        "strings"
    )
    
    func main() {
        var host, user, date, tzOffset, verb, route, proto string
        var code, size int
    
        r := strings.NewReader(`127.0.0.1 - james [09/May/2018:16:00:39 +0000] "GET /report HTTP/1.0" 200 123`)
    
        _, err := fmt.Fscanf(r, `%s - %s %s %s %s %s %s %d %d`,
            &host, &user, &date, &tzOffset, &verb, &route, &proto, &code,
            &size)
    
        if err != nil {
            log.Fatal(err)
        }
    
        date = date[1:] + " " + tzOffset[:len(tzOffset)-1]
        httpString := verb[1:] + " " + route + " " + proto[:len(proto)-1]
    
        fmt.Println(host, user, date, httpString, code, size)
    }
    

    Here we ignore the brackets and quotes around the date and HTTP string when parsing, then remove them afterwards.

    Output:

    127.0.0.1 james 09/May/2018:16:00:39 +0000 GET /report HTTP/1.0 200 123
    
    点赞 评论 复制链接分享

相关推荐