为什么 x/net/html Token().Attr 上的 len 在这里为空切片返回非零值?

vad*_*bar 4 tokenize go slice

我正在使用 Golang 中的内置html库。这是重现该问题的代码:

package main

import (
    "fmt"
    "log"
    "net/http"

    "golang.org/x/net/html"
)

const url = "https://google.com"

func main() {
    resp, err := http.Get(url)
    if err != nil {
        log.Fatal(err)
    }
    defer resp.Body.Close()

    if resp.StatusCode != 200 {
        log.Fatalf("Status code error: %d %s", resp.StatusCode, resp.Status)
    }

    h := html.NewTokenizer(resp.Body)

    for {
        if h.Next() == html.ErrorToken {
            break
        }

        l := len(h.Token().Attr)

        if l != 0 {
            fmt.Println("=======")
            fmt.Println("Length", l) // greater than 0
            fmt.Println("Attr", h.Token().Attr) // empty all the times
        }
    }
}

Run Code Online (Sandbox Code Playgroud)

输出如下所示

=======
Length 2
Attr []
typeof Attr []html.Attribute
=======
Length 8
Attr []
typeof Attr []html.Attribute
=======
Length 1
Attr []
typeof Attr []html.Attribute
=======
Length 1
Attr []
typeof Attr []html.Attribute

Run Code Online (Sandbox Code Playgroud)

去版本

go version go1.17.7 linux/amd64
Run Code Online (Sandbox Code Playgroud)

当 the为空时,为什么 Go 认为这里的长度h.Token().Attr不为零?h.Token().Attr

PS:保存输出h.Token().Attr并使用它len并打印内容使一切正常

代码:

=======
Length 2
Attr []
typeof Attr []html.Attribute
=======
Length 8
Attr []
typeof Attr []html.Attribute
=======
Length 1
Attr []
typeof Attr []html.Attribute
=======
Length 1
Attr []
typeof Attr []html.Attribute

Run Code Online (Sandbox Code Playgroud)

输出

Length 3
Attr [{ value AJiK0e8AAAAAYtZT7PXDBRBC2BJawIxezEfmIL6Aw5Uy} { name iflsig} { type hidden}]
=======
Length 4
Attr [{ class fl sblc} { align left} { nowrap } { width 25%}]
=======
Length 1
Attr [{ href /advanced_search?hl=en-IN&authuser=0}]
=======
Length 4
Attr [{ id gbv} { name gbv} { type hidden} { value 1}]
Run Code Online (Sandbox Code Playgroud)

hob*_*bbs 6

Tokenizer 有一种有趣的界面,并且Token()在调用Next(). 正如医生所说:

\n
\n

在 EBNF 表示法中,每个令牌的有效调用序列为:
\nNext {Raw} [ Token | Text | TagName {TagAttr} ]

\n
\n

也就是说:调用后,Next()您可以调用Raw()零次或多次;那么你可以

\n
    \n
  • 打电话Token()一次,
  • \n
  • 打电话Text()一次,
  • \n
  • 调用TagName()一次,然后调用TagAttr()零次或多次(大概,要么根本不调用,因为您不关心属性,要么调用足够的次数来检索所有属性)。
  • \n
  • 或者什么都不做(也许你正在跳过令牌)。
  • \n
\n

不按顺序调用事物的结果是未定义的,因为这些方法修改内部状态 \xe2\x80\x94 它们不是纯粹的访问器。Token()在您的第一个代码片段中,您在调用 之间多次调用Next(),因此结果无效。所有属性均由第一次调用消耗,并且不会由后续调用返回。

\n