Expand description
§STReplace
STReplace is a small library for matching and replacing in strings and slices with user-defined functions. It does not currently support overlapping matches.
It provides several extension methods to the String
and &str
types, for this purpose.
This crate also includes several feature flags:
result
: Provides variants ofget_matches
,replace_matches
, andmatch_and_replace
which accept functions that returnResult
values.convenience
: Provides additional convenienceMatcher
andReplacer
implementations, which can be used in place of bare functions.
§Benchmarks
On a very basic benchmark, based on the first example in the section below:
- This crate performed 12.9x as fast as regex,
- 10.1x as fast as regex-lite,
- 3.5x as fast as lazy-regex,
- 1.17x as fast as onig,
- and 0.21x as fast as str.replace.
This benchmark is now several versions out of date, but should remain approximately accurate.
§Examples
use streplace::{InProgressMatch, Match, MatchResult, Matchable};
fn main() {
let test = "hello test, this is a test string";
let new = test.match_and_replace(
|progress, index, character| match progress {
Some(InProgressMatch { start, value }) => {
if index - start >= 4 {
println!("end {start} {value} {character}");
MatchResult::End
} else if "test"
.chars()
.nth(index - start)
.is_some_and(|c| character == c)
{
println!("cont {start} {value}{character}");
MatchResult::Continue
} else {
println!("fail {start} {value} {character}");
MatchResult::Fail
}
}
None => {
if character == 't' {
println!("start {index} {character}");
MatchResult::Start
} else {
println!("none {index} {character}");
MatchResult::None
}
}
},
|Match { start, end, value }| format!("start:{start},end:{end},val:{value}"),
false,
);
println!("{new}");
}
use streplace::{
AccumulatorMatcher, ChainMatchable, InProgressMatch, Match, MatchResult, Matchable,
};
fn main() {
let text = "@first_of(a b c)";
let text = text.match_and_replace(
"@first_of(".chain(AccumulatorMatcher::new(
0,
|progress, index, character, parens| {
let value = progress.map(|p| p.value);
let mut last_chars = value.unwrap_or_default().chars().rev();
let last_char = last_chars.next();
let second_last_char = last_chars.next();
if character == '(' && last_char != Some('\\') {
*parens += 1;
}
if last_char.is_some_and(|c| c == ')') && second_last_char != Some('\\') {
println!("last char is paren");
if *parens == 0 {
*parens = 0;
MatchResult::End
} else {
*parens -= 1;
MatchResult::Continue
}
} else if index == text.len() - 1 && !(character == ')' && last_char != Some('\\'))
{
*parens = 0;
MatchResult::Fail
} else {
MatchResult::Continue
}
},
)),
|Match {
start: _,
end: _,
value,
}| {
let substring = &value["@first_of(".len()..value.len() - 1];
let submatches: Vec<&str> = substring
.get_matches(
AccumulatorMatcher::new(0, |progress, _, character, parens| {
match (progress, character) {
(Some(_), ' ') => {
if *parens == 0 {
MatchResult::End
} else {
MatchResult::Continue
}
}
(
Some(InProgressMatch {
start: _,
value: match_value,
}),
'(',
) => {
if match_value.ends_with('\\') {
MatchResult::Continue
} else {
*parens += 1;
MatchResult::Continue
}
}
(
Some(InProgressMatch {
start: _,
value: match_value,
}),
')',
) => {
if match_value.ends_with('\\') {
MatchResult::Continue
} else {
*parens = (*parens - 1).max(0);
MatchResult::Continue
}
}
(Some(_), _) => MatchResult::Continue,
(None, ' ') => {
*parens = 0;
MatchResult::None
}
(None, _) => {
*parens = 0;
MatchResult::Start
}
}
}),
true,
)
.into_iter()
.map(|m| m.value)
.collect();
let chosen = submatches
.first()
.map(|v| v.to_string())
.unwrap_or_default();
chosen
},
true,
);
println!("{text}");
}
§Changelog
- 3.0.0
- Replaced the match and replace functions with generic
Matcher
,Replacer
,TryMatcher
, andTryReplacer
functions - Removed
get_matches_acc
,match_and_replace_acc
, andtry_match_and_replace_acc
functions, and replaced them withMatcher
implementations - Added convenience implementations of
Matcher
andReplacer
forString
s - Added
AccumulatorMatcher
andChainMatcher
convenienceMatcher
s,TryMatcher
variants of those, and theTryMatcherWrapper
TryMatcher
- Modularized most of the code and moved some modules to feature flags (enabled by default)
- Replaced the match and replace functions with generic
- 2.2.1
- Fixed an error which would occur when traversing strings containing characters spanning more than one byte
- 2.2.0
- Added
try_replace_matches
,try_match_and_replace
, andtry_match_and_replace_acc
functions
- Added
- 2.1.1
- Fixed a major bug in the replacer functions
- 2.1.0
- Added
get_matches_acc
andmatch_and_replace_acc
functions
- Added
- 2.0.0
- Added
match_end
parameter to matching functions
- Added
- 1.0.1
- Implemented Matchable for String
- 1.0.0
- Initial release
Re-exports§
pub use convenience::*;
pub use result::*;
Modules§
- convenience
- Additional Matcher and Replacer implementations including Strings, accumulator functions, and chaining sequential Matchers.
- result
- Allow matching and replacing with functions that return Results.
Structs§
- InProgress
Match - The values for a potential substring match as it’s being processed and created.
- Match
- A substring match.
Enums§
- Match
Result - The result of a substring matching char-level comparison.