README.md
1aho-corasick
2============
3A library for finding occurrences of many patterns at once with SIMD
4acceleration in some cases. This library provides multiple pattern
5search principally through an implementation of the
6[Aho-Corasick algorithm](https://en.wikipedia.org/wiki/Aho%E2%80%93Corasick_algorithm),
7which builds a finite state machine for executing searches in linear time.
8Features include case insensitive matching, overlapping matches, fast searching
9via SIMD and optional full DFA construction and search & replace in streams.
10
11[![Build status](https://github.com/BurntSushi/aho-corasick/workflows/ci/badge.svg)](https://github.com/BurntSushi/aho-corasick/actions)
12[![crates.io](https://img.shields.io/crates/v/aho-corasick.svg)](https://crates.io/crates/aho-corasick)
13
14Dual-licensed under MIT or the [UNLICENSE](https://unlicense.org/).
15
16
17### Documentation
18
19https://docs.rs/aho-corasick
20
21
22### Usage
23
24Add this to your `Cargo.toml`:
25
26```toml
27[dependencies]
28aho-corasick = "0.7"
29```
30
31
32### Example: basic searching
33
34This example shows how to search for occurrences of multiple patterns
35simultaneously. Each match includes the pattern that matched along with the
36byte offsets of the match.
37
38```rust
39use aho_corasick::AhoCorasick;
40
41let patterns = &["apple", "maple", "Snapple"];
42let haystack = "Nobody likes maple in their apple flavored Snapple.";
43
44let ac = AhoCorasick::new(patterns);
45let mut matches = vec![];
46for mat in ac.find_iter(haystack) {
47 matches.push((mat.pattern(), mat.start(), mat.end()));
48}
49assert_eq!(matches, vec![
50 (1, 13, 18),
51 (0, 28, 33),
52 (2, 43, 50),
53]);
54```
55
56
57### Example: case insensitivity
58
59This is like the previous example, but matches `Snapple` case insensitively
60using `AhoCorasickBuilder`:
61
62```rust
63use aho_corasick::AhoCorasickBuilder;
64
65let patterns = &["apple", "maple", "snapple"];
66let haystack = "Nobody likes maple in their apple flavored Snapple.";
67
68let ac = AhoCorasickBuilder::new()
69 .ascii_case_insensitive(true)
70 .build(patterns);
71let mut matches = vec![];
72for mat in ac.find_iter(haystack) {
73 matches.push((mat.pattern(), mat.start(), mat.end()));
74}
75assert_eq!(matches, vec![
76 (1, 13, 18),
77 (0, 28, 33),
78 (2, 43, 50),
79]);
80```
81
82
83### Example: replacing matches in a stream
84
85This example shows how to execute a search and replace on a stream without
86loading the entire stream into memory first.
87
88```rust
89use aho_corasick::AhoCorasick;
90
91let patterns = &["fox", "brown", "quick"];
92let replace_with = &["sloth", "grey", "slow"];
93
94// In a real example, these might be `std::fs::File`s instead. All you need to
95// do is supply a pair of `std::io::Read` and `std::io::Write` implementations.
96let rdr = "The quick brown fox.";
97let mut wtr = vec![];
98
99let ac = AhoCorasick::new(patterns);
100ac.stream_replace_all(rdr.as_bytes(), &mut wtr, replace_with)
101 .expect("stream_replace_all failed");
102assert_eq!(b"The slow grey sloth.".to_vec(), wtr);
103```
104
105
106### Example: finding the leftmost first match
107
108In the textbook description of Aho-Corasick, its formulation is typically
109structured such that it reports all possible matches, even when they overlap
110with another. In many cases, overlapping matches may not be desired, such as
111the case of finding all successive non-overlapping matches like you might with
112a standard regular expression.
113
114Unfortunately the "obvious" way to modify the Aho-Corasick algorithm to do
115this doesn't always work in the expected way, since it will report matches as
116soon as they are seen. For example, consider matching the regex `Samwise|Sam`
117against the text `Samwise`. Most regex engines (that are Perl-like, or
118non-POSIX) will report `Samwise` as a match, but the standard Aho-Corasick
119algorithm modified for reporting non-overlapping matches will report `Sam`.
120
121A novel contribution of this library is the ability to change the match
122semantics of Aho-Corasick (without additional search time overhead) such that
123`Samwise` is reported instead. For example, here's the standard approach:
124
125```rust
126use aho_corasick::AhoCorasick;
127
128let patterns = &["Samwise", "Sam"];
129let haystack = "Samwise";
130
131let ac = AhoCorasick::new(patterns);
132let mat = ac.find(haystack).expect("should have a match");
133assert_eq!("Sam", &haystack[mat.start()..mat.end()]);
134```
135
136And now here's the leftmost-first version, which matches how a Perl-like
137regex will work:
138
139```rust
140use aho_corasick::{AhoCorasickBuilder, MatchKind};
141
142let patterns = &["Samwise", "Sam"];
143let haystack = "Samwise";
144
145let ac = AhoCorasickBuilder::new()
146 .match_kind(MatchKind::LeftmostFirst)
147 .build(patterns);
148let mat = ac.find(haystack).expect("should have a match");
149assert_eq!("Samwise", &haystack[mat.start()..mat.end()]);
150```
151
152In addition to leftmost-first semantics, this library also supports
153leftmost-longest semantics, which match the POSIX behavior of a regular
154expression alternation. See `MatchKind` in the docs for more details.
155
156
157### Minimum Rust version policy
158
159This crate's minimum supported `rustc` version is `1.41.1`.
160
161The current policy is that the minimum Rust version required to use this crate
162can be increased in minor version updates. For example, if `crate 1.0` requires
163Rust 1.20.0, then `crate 1.0.z` for all values of `z` will also require Rust
1641.20.0 or newer. However, `crate 1.y` for `y > 0` may require a newer minimum
165version of Rust.
166
167In general, this crate will be conservative with respect to the minimum
168supported version of Rust.
169
170
171### FFI bindings
172
173* [G-Research/ahocorasick_rs](https://github.com/G-Research/ahocorasick_rs/)
174is a Python wrapper for this library.
175
176
177### Future work
178
179Here are some plans for the future:
180
181* Assuming the current API is sufficient, I'd like to commit to it and release
182 a `1.0` version of this crate some time in the next 6-12 months.
183* Support stream searching with leftmost match semantics. Currently, only
184 standard match semantics are supported. Getting this right seems possible,
185 but is tricky since the match state needs to be propagated through multiple
186 searches. (With standard semantics, as soon as a match is seen the search
187 ends.)
188