首页 文章

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

提问于
浏览
0

我'm trying to figure out if there'是一种使用 golang.org/x/net/html tokenizer库获取标签当前字符位置的方法吗?

简化的代码如下所示:

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行),但我不知道如何访问它们 .

我刚刚遇到的一个可能的解决方案是创建一个只能一次读取一个字符的"reader",这样我的 SizeLen 计算总是正确的 . 但是,这似乎是一个黑客,任何建议将不胜感激 .

2 回答

  • 0

    您可以使用Tokenizer的Buffered方法仔细算法来完成您尝试做的事情(不是您想要的),该方法返回当前在缓冲区中已被标记化的字节片段 . 但是我不认为你会得到你想要的东西,因为 <div><form></form></div> 可能会在给你第一个div标记之前缓冲整个字符串 . 在这种情况下,缓冲内容的大小对计算解决方案没有帮助 .

    具有嵌套结构的标记标记lang几乎总是需要缓冲输入才能工作 . 私有span属性应该是无用的,因为它只是它缓冲区中的引用,而不是读者的绝对位置 .

    由于html Tokenizer没有提供API来访问原始数据中标记的原始位置,为了得到你想要的东西我可能只需要在令牌的原始缓冲区上执行一个strings.Index或bytes.Index来获取位置:

    strings.Index(body, string(tokenizer.Raw()))
    
  • 1

    一个非缓冲读者最终为我工作 . 读者的实现看起来像:

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

相关问题