Documentation
¶
Overview ¶
Package ahocorasick implements an Aho-Corasick automaton used to scan through bytes of some encoding to find instances of identifiable words (patterns) within a dictionary.
Index ¶
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Automaton ¶
type Automaton[C Character, ID Identifier] struct { // contains filtered or unexported fields }
Automaton represents a compiled Aho-Corasick automaton for efficient multi-pattern matching. Create an Automaton via New and use Automaton.Matches to find all occurrences of dictionary words in input text.
The C type parameter specifies the character type for dictionary words, either rune for UTF-8 text or byte for ASCII or raw bytes.
The ID type parameter specifies the identifier type for dictionary words. Note that the identifier type chosen must have sufficient cardinality to fit all words within the dictionary.
func New ¶
func New[C Character, ID Identifier]( dict iter.Seq2[ID, []byte], opts ...Option[C], ) (Automaton[C, ID], error)
New compiles a new automaton from the provided dictionary dict and set of options opts. The dictionary should be an iterator that yields pairs of word identifiers and their corresponding content as UTF-8 bytes.
Returns an error if any of the dictionary words contain invalid UTF-8 bytes. Words may also be silently rejected during insertion if they fall outside the configured minimum or maximum character constraints (see WithMinChars and WithMaxChars).
Example ¶
A basic example of compiling an automaton from an iterator-style dictionary of words.
package main
import (
"github.com/smotes/ahocorasick"
)
func main() {
auto, err := ahocorasick.New[rune](func(yield func(int, []byte) bool) {
dict := []string{"Hello", "World"}
for id, word := range dict {
if !yield(id, []byte(word)) {
return
}
}
})
if err != nil {
panic(err)
}
_ = auto
}
Output:
func (*Automaton[C, ID]) Matches ¶
Matches returns an iterator that yields all matches of dictionary words in the input text according to the specified match type. The iterator may yield an error if the specified match type is invalid or the input text contains an invalid sequence of bytes for the encoding.
Matches may be safely called concurrently across multiple goroutines.
Example (LeftmostLongest) ¶
A basic example of leftmost longest matching.
package main
import (
"fmt"
"github.com/smotes/ahocorasick"
)
func main() {
auto, err := ahocorasick.New[rune](func(yield func(int, []byte) bool) {
dict := []string{
"Hello", // id=0
"World", // id=1
"He", // id=2
"She", // id=3
}
for id, word := range dict {
if !yield(id, []byte(word)) {
return
}
}
})
if err != nil {
panic(err)
}
text := []byte("World Hello")
for m, err := range auto.Matches(ahocorasick.MatchTypeLeftmostLongest, text) {
if err != nil {
panic(err)
}
fmt.Printf("m=%+v s=%s\n", m, text[m.StartIndex:m.EndIndex])
}
}
Output: m={WordID:1 StartIndex:0 EndIndex:5} s=World m={WordID:0 StartIndex:6 EndIndex:11} s=Hello
Example (Overlapping) ¶
A basic example of overlapping matching.
package main
import (
"fmt"
"github.com/smotes/ahocorasick"
)
func main() {
auto, err := ahocorasick.New[rune](func(yield func(int, []byte) bool) {
dict := []string{
"Hello", // id=0
"World", // id=1
"He", // id=2
"She", // id=3
}
for id, word := range dict {
if !yield(id, []byte(word)) {
return
}
}
})
if err != nil {
panic(err)
}
text := []byte("World Hello")
for m, err := range auto.Matches(ahocorasick.MatchTypeOverlapping, text) {
if err != nil {
panic(err)
}
fmt.Printf("m=%+v s=%s\n", m, text[m.StartIndex:m.EndIndex])
}
}
Output: m={WordID:1 StartIndex:0 EndIndex:5} s=World m={WordID:2 StartIndex:6 EndIndex:8} s=He m={WordID:0 StartIndex:6 EndIndex:11} s=Hello
type Character ¶
Character is a character unit used for interpreting data, either a single byte for handling ASCII characters and raw bytes or a rune for handling unicode encoded as a sequence of UTF-8 bytes.
type EncodingError ¶
EncodingError denotes the input data contains an invalid character given the specified encoding; either invalid ASCII or UTF-8 bytes.
func (*EncodingError[C]) Error ¶
func (err *EncodingError[C]) Error() string
type Identifier ¶
type Identifier interface {
~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uint | ~uintptr |
~int8 | ~int16 | ~int32 | ~int64 | ~int | ~string
}
Identifier is a constraint that limits dictionary word identifiers to a subset of comparable types. Booleans, floats, pointers, channels, arrays and structs are excluded to ensure that identifiers have sufficient cardinality and are straightforward to compare by value.
type InvalidMatchTypeError ¶
type InvalidMatchTypeError MatchType
InvalidMatchTypeError specifies the match type specified in [Matches] is an invalid value.
func (InvalidMatchTypeError) Error ¶
func (err InvalidMatchTypeError) Error() string
type Match ¶
type Match[ID Identifier] struct { // WordID is the identifier of the matched word. WordID ID // StartIndex is the index of the first byte of the matched word. StartIndex int // EndIndex is the index of the first byte after the matched word. EndIndex int }
Match represents a single match of a dictionary word in the input text.
type MatchType ¶
type MatchType int
MatchType specifies the type of matches to return when searching the input text.
type Option ¶
type Option[C Character] func(*options[C])
Option is custom optional behaviour for the automaton during the building and/or matching phases.
func WithASCIIValidation ¶
WithASCIIValidation specifies that all dictionary words and all input text should be validated as ASCII bytes (ie: > 127).
func WithFastByteLookup ¶
WithFastByteLookup optimizes the underlying trie data structure for faster lookup when using byte characters at the expense of more memory usage.
It achieves this by using a dense transition strategy of fixed-size slices rather than a sparse strategy via maps when constructing the edges of the trie that drives the automaton.
func WithMaxChars ¶
WithMaxChars specifies the maximum number of characters a dictionary word can have to be included in the automaton. Words longer than this length are silently rejected during insertion.
A value n <= 0 means no configured maximum limit.
This option is applied before a word is inserted into the trie, so excluded words never contribute to automaton state or suffix links.
func WithMinChars ¶
WithMinChars specifies the minimum number of characters a dictionary word must have to be included in the automaton. Words shorter than this length are silently rejected during insertion.
A value n <= 0 means no configured minimum limit other than the implicit minimum of 1 character, since empty words are always rejected.
This option is applied before a word is inserted into the trie, so excluded words never contribute to automaton state or suffix links.
func WithNormalizeFunc ¶
WithNormalizeFunc specifies a normalization function for all characters during both dictionary word insertion and text matching. It can be used to normalize inconsistencies between dictionary words and/or input text data such as casing (eg: "Hello" vs "hello"), accents (eg: "café" vs "cafe"), or interchangeable characters (eg: ` backtick vs ' apostrophe).
If multiple dictionary words normalize to the same sequence only the word with the latest ID will be retained in the automaton.
Normalization functions cannot be chained together if multiple are specified - the latest will always take precedence.
Note that the normalization function is called on every single character transition in the automaton and so is the hottest path in the entire package if specified. For high-performance use cases, input text and dictionary words should both be pre-normalized and this option should be left off.
Example ¶
An example of a normalization function that casts all characters in the dictionary words and input text to lowercase for case insensitivity and replaces all backticks to apostrophes for handling inconsistencies.
package main
import (
"fmt"
"unicode"
"github.com/smotes/ahocorasick"
)
func main() {
auto, err := ahocorasick.New(func(yield func(int, []byte) bool) {
dict := []string{
"Hello", // id=0
"WoRLD", // id=1
"aren't", // id=2
"they`ll", // id=3
}
for id, word := range dict {
if !yield(id, []byte(word)) {
return
}
}
}, ahocorasick.WithNormalizeFunc(func(r rune) rune {
if r == '`' {
return '\''
}
return unicode.ToLower(r)
}))
if err != nil {
panic(err)
}
text := []byte("Aren`t HELLO world they'll HellO heLLO")
for m, err := range auto.Matches(ahocorasick.MatchTypeOverlapping, text) {
if err != nil {
panic(err)
}
fmt.Printf("m=%+v s=%s\n", m, text[m.StartIndex:m.EndIndex])
}
}
Output: m={WordID:2 StartIndex:0 EndIndex:6} s=Aren`t m={WordID:0 StartIndex:7 EndIndex:12} s=HELLO m={WordID:1 StartIndex:13 EndIndex:18} s=world m={WordID:3 StartIndex:19 EndIndex:26} s=they'll m={WordID:0 StartIndex:27 EndIndex:32} s=HellO m={WordID:0 StartIndex:33 EndIndex:38} s=heLLO
Directories
¶
| Path | Synopsis |
|---|---|
|
internal
|
|
|
char
Package char is an internal package used for handling characters within text in a generic way, whether dealing with single bytes such as raw bytes and ASCII or dealing with potentially multi-byte unicode runes in UTF-8 text.
|
Package char is an internal package used for handling characters within text in a generic way, whether dealing with single bytes such as raw bytes and ASCII or dealing with potentially multi-byte unicode runes in UTF-8 text. |
|
trie
Package trie is an internal package used for building the internal trie data structure used by the Aho-Corasick algorithm.
|
Package trie is an internal package used for building the internal trie data structure used by the Aho-Corasick algorithm. |