我试图弄清楚是否有办法使用golang.org/x/net/html
令牌系统库来获取标记的当前字符位置?
简化代码如下:
func LookForForm(body string) {
reader := strings.NewReader(body)
tokenizer := html.NewTokenizer(reader)
idx := 0
lastIdx := 0
for {
token := tokenizer.Next()
lastIdx = idx
idx = int(reader.Size()) - int(reader.Len())
switch token {
case html.ErrorToken:
return
case html.StartTagToken:
t := tokenizer.Token()
tagName := strings.ToLower(t.Data)
if tagName == "form" {
fmt.Printf("found at form at %d\n", lastIdx)
return
}
}
}
}
这不起作用(我认为)因为读者不是逐个字符地阅读而是通过块读取因此我对Size-Len的计算无效。 tokenizer
维护了两个私有span
结构(https://github.com/golang/net/blob/master/html/token.go第147行),但我不知道如何访问它们。
我刚刚遇到的一个可能的解决方案就是建立一个"读者"只能一次读取一个字符,因此我的Size
和Len
计算始终是正确的。但是,这似乎是一个黑客,任何建议将不胜感激。
答案 0 :(得分:1)
您可以使用Tokenizer的Buffered方法仔细算法来完成您尝试做的事情(不是您想要的),该方法返回当前缓冲区中尚未被标记化的字节片段。但我不认为你会得到你想要的东西,因为<div><form></form></div>
可能会在给你第一个div标记之前缓冲整个字符串。在这种情况下,缓冲内容的大小对计算解决方案没有帮助。
使用嵌套结构标记标记lang几乎总是需要缓冲输入才能工作。私有span属性应该是无用的,因为它只是它缓冲区中的引用,而不是读者的绝对位置。
由于html Tokenizer没有提供API来访问原始数据中标记的原始位置,为了得到你想要的东西我可能只会在令牌的原始缓冲区上做一个strings.Index或bytes.Index获得这个职位:
strings.Index(body, string(tokenizer.Raw()))
答案 1 :(得分:0)
非缓冲读者最终为我工作。阅读器的实现类似于:
package rule
import (
"errors"
"io"
"unicode/utf8"
)
type Reader struct {
s string
i int64
z int64
prevRune int64 // index of the previously read rune or -1
}
func (r *Reader) String() string {
return r.s
}
func (r *Reader) Len() int {
if r.i >= r.z {
return 0
}
return int(r.z - r.i)
}
func (r *Reader) Size() int64 {
return r.z
}
func (r *Reader) Pos() int64 {
return r.i
}
func (r *Reader) Read(b []byte) (int, error) {
if r.i >= r.z {
return 0, io.EOF
}
r.prevRune = -1
b[0] = r.s[r.i]
r.i += 1
return 1, nil
}
然后,令牌化器的循环很容易计算:
reader := NewReader(body)
tokenizer := html.NewTokenizer(reader)
idx := 0
lastIdx := 0
tokenLoop:
for {
token := tokenizer.Next()
switch token {
case html.ErrorToken:
break tokenLoop
case html.EndTagToken, html.TextToken, html.CommentToken, html.SelfClosingTagToken:
lastIdx = int(reader.Pos())
case html.StartTagToken:
t := tokenizer.Token()
tagName := strings.ToLower(t.Data)
idx = int(reader.Pos())
if tagName == "form" {
fmt.Printf("found at form at %d\n", lastIdx)
return
}
}
}