Golang如何使用正则表达式实现字符串部分匹配

Golang如何使用正则表达式实现字符串部分匹配 假设LLM将生成如下完整文本:

Larson graduated from Webster^[1][2]^ and Tooele^[3]^...

我的程序可能接收到的输出序列是:

  • Larson
  • graduated from
  • Webster^
  • [
  • 1][2
  • ]^ and
  • Tooele
  • ^[
  • 3
  • ]^

我希望在收到字符串时,能立即根据正则表达式 \^(\[\d+])+\^ 过滤掉所有引用。

处理流程如下:

image

最终输出: Larson graduated from Webster and Tooele...

问题是如何像 Webster^Webster^1][2 这样,对字符串进行部分匹配的正则表达式?

注意:

不要手动枚举所有正则表达式,例如:

\^$|\^\[$|\^\[\d+$|\^(\[\d+])+$|\^(\[\d+])*\[$|\^(\[\d+])*\[\d+$

这种方式容易出错且难以维护。


更多关于Golang如何使用正则表达式实现字符串部分匹配的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang如何使用正则表达式实现字符串部分匹配的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


在Go中处理流式数据时,可以使用正则表达式的FindReaderIndex方法配合io.RuneReader来实现部分匹配。以下是实现方案:

package main

import (
    "bufio"
    "fmt"
    "io"
    "regexp"
    "strings"
)

type CitationFilter struct {
    re       *regexp.Regexp
    buffer   []rune
    inMatch  bool
    matchBuf []rune
}

func NewCitationFilter() *CitationFilter {
    return &CitationFilter{
        re: regexp.MustCompile(`\^(\[\d+])+\^`),
    }
}

func (cf *CitationFilter) ProcessChunk(chunk string) string {
    cf.buffer = append(cf.buffer, []rune(chunk)...)
    
    var result strings.Builder
    for len(cf.buffer) > 0 {
        if cf.inMatch {
            // 正在匹配引用标记中
            cf.matchBuf = append(cf.matchBuf, cf.buffer[0])
            cf.buffer = cf.buffer[1:]
            
            // 尝试匹配完整模式
            if cf.re.MatchString(string(cf.matchBuf)) {
                cf.inMatch = false
                cf.matchBuf = nil
            } else if !couldBePartial(cf.matchBuf) {
                // 不可能是部分匹配,输出缓冲内容
                result.WriteString(string(cf.matchBuf))
                cf.inMatch = false
                cf.matchBuf = nil
            }
        } else {
            // 查找可能的引用开始
            start := -1
            for i, r := range cf.buffer {
                if r == '^' {
                    start = i
                    break
                }
            }
            
            if start == -1 {
                // 没有找到^,输出全部
                result.WriteString(string(cf.buffer))
                cf.buffer = nil
            } else {
                // 输出^之前的内容
                if start > 0 {
                    result.WriteString(string(cf.buffer[:start]))
                }
                
                // 开始匹配
                cf.inMatch = true
                cf.matchBuf = []rune{cf.buffer[start]}
                cf.buffer = cf.buffer[start+1:]
            }
        }
    }
    
    return result.String()
}

func couldBePartial(buf []rune) bool {
    str := string(buf)
    
    // 检查是否可能是部分匹配的模式
    if !strings.HasPrefix(str, "^") {
        return false
    }
    
    // 允许的模式:^, ^[, ^[数字, ^[数字], ^[数字][, 等
    re := regexp.MustCompile(`^\^(\[\d+\])*(\[\d+)?(\[)?$`)
    return re.MatchString(str)
}

// 使用示例
func main() {
    filter := NewCitationFilter()
    
    chunks := []string{
        "Larson ",
        "graduated from ",
        "Webster^",
        "[",
        "1][2",
        "]^ and ",
        "Tooele",
        "^[",
        "3",
        "]^",
        "...",
    }
    
    var output strings.Builder
    for _, chunk := range chunks {
        processed := filter.ProcessChunk(chunk)
        output.WriteString(processed)
    }
    
    // 处理剩余的缓冲
    if len(filter.buffer) > 0 {
        output.WriteString(string(filter.buffer))
    }
    
    fmt.Println("Final output:", output.String())
    // 输出: Larson graduated from Webster and Tooele...
}

更简洁的方案是使用regexp.RegexpFindReaderIndex

package main

import (
    "bytes"
    "fmt"
    "regexp"
    "strings"
)

type StreamingFilter struct {
    re     *regexp.Regexp
    buffer bytes.Buffer
}

func NewStreamingFilter() *StreamingFilter {
    return &StreamingFilter{
        re: regexp.MustCompile(`\^(\[\d+])+\^`),
    }
}

func (sf *StreamingFilter) ProcessChunk(chunk string) string {
    sf.buffer.WriteString(chunk)
    data := sf.buffer.Bytes()
    
    // 查找所有完整匹配
    matches := sf.re.FindAllIndex(data, -1)
    if matches == nil {
        return chunk
    }
    
    var result strings.Builder
    lastEnd := 0
    
    for _, match := range matches {
        // 输出匹配之前的内容
        result.Write(data[lastEnd:match[0]])
        lastEnd = match[1]
    }
    
    // 输出剩余内容
    result.Write(data[lastEnd:])
    
    // 更新缓冲区
    sf.buffer.Reset()
    sf.buffer.Write(result.Bytes())
    
    return result.String()
}

func main() {
    filter := NewStreamingFilter()
    
    testCases := []struct {
        input    string
        expected string
    }{
        {"Webster^", "Webster"},
        {"[1][2", ""},
        {"]^ and", " and"},
        {"Tooele^[", "Tooele"},
        {"3]^...", "..."},
    }
    
    for _, tc := range testCases {
        output := filter.ProcessChunk(tc.input)
        fmt.Printf("Input: %q -> Output: %q\n", tc.input, output)
    }
}

对于更复杂的流式处理,可以使用状态机:

package main

import (
    "fmt"
    "strings"
)

type CitationState int

const (
    StateText CitationState = iota
    StateStartCite    // 遇到^
    StateInBracket    // 遇到[
    StateInNumber     // 在数字中
    StateEndBracket   // 遇到]
    StateEndCite      // 遇到结束^
)

type CitationParser struct {
    state    CitationState
    buffer   strings.Builder
    citeBuf  strings.Builder
}

func (cp *CitationParser) ProcessRune(r rune) string {
    switch cp.state {
    case StateText:
        if r == '^' {
            cp.state = StateStartCite
            cp.citeBuf.WriteRune(r)
            return ""
        }
        cp.buffer.WriteRune(r)
        return ""
        
    case StateStartCite:
        cp.citeBuf.WriteRune(r)
        if r == '[' {
            cp.state = StateInBracket
        } else {
            // 无效格式,回退到文本状态
            cp.buffer.WriteString(cp.citeBuf.String())
            cp.citeBuf.Reset()
            cp.state = StateText
        }
        return ""
        
    case StateInBracket:
        cp.citeBuf.WriteRune(r)
        if r >= '0' && r <= '9' {
            cp.state = StateInNumber
        } else {
            // 无效格式
            cp.buffer.WriteString(cp.citeBuf.String())
            cp.citeBuf.Reset()
            cp.state = StateText
        }
        return ""
        
    case StateInNumber:
        cp.citeBuf.WriteRune(r)
        if r == ']' {
            cp.state = StateEndBracket
        } else if r < '0' || r > '9' {
            // 无效格式
            cp.buffer.WriteString(cp.citeBuf.String())
            cp.citeBuf.Reset()
            cp.state = StateText
        }
        return ""
        
    case StateEndBracket:
        cp.citeBuf.WriteRune(r)
        if r == '^' {
            cp.state = StateEndCite
            cp.citeBuf.Reset()
            cp.state = StateText
            return ""
        } else if r == '[' {
            cp.state = StateInBracket
        } else {
            // 无效格式
            cp.buffer.WriteString(cp.citeBuf.String())
            cp.citeBuf.Reset()
            cp.state = StateText
        }
        return ""
        
    case StateEndCite:
        // 不应该到达这里
        cp.state = StateText
        return ""
    }
    
    return ""
}

func (cp *CitationParser) Flush() string {
    result := cp.buffer.String()
    if cp.citeBuf.Len() > 0 {
        result += cp.citeBuf.String()
    }
    cp.buffer.Reset()
    cp.citeBuf.Reset()
    cp.state = StateText
    return result
}

func main() {
    parser := &CitationParser{}
    
    input := "Webster^[1][2]^ and Tooele^[3]^"
    for _, r := range input {
        parser.ProcessRune(r)
    }
    
    result := parser.Flush()
    fmt.Println("Result:", result) // 输出: Webster and Tooele
}
回到顶部