从net / html tokenizer获取流中的当前位置

时间:2017-11-16 21:30:45

标签: go

我试图弄清楚是否有办法使用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行),但我不知道如何访问它们。

我刚刚遇到的一个可能的解决方案就是建立一个"读者"只能一次读取一个字符,因此我的SizeLen计算始终是正确的。但是,这似乎是一个黑客,任何建议将不胜感激。

2 个答案:

答案 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
            }
        }
    }