Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Lookahead and Lookbehind

Assert what comes before or after the match without including it.

Lookahead

Positive Lookahead (?=...)

Match only if followed by:

fn main() {
    use fuzzy_regex::FuzzyRegex;

    // Match "hello" only if followed by " world"
    let re = FuzzyRegex::new("hello(?= world)").unwrap();

    assert!(re.is_match("hello world"));  // Matches "hello"
    assert!(!re.is_match("hello there")); // No match
}

Negative Lookahead (?!...)

Match only if NOT followed by:

fn main() {
    use fuzzy_regex::FuzzyRegex;

    // Match "hello" only if NOT followed by " world"
    let re = FuzzyRegex::new("hello(?! world)").unwrap();

    assert!(re.is_match("hello there")); // Matches "hello"
    assert!(!re.is_match("hello world")); // No match
}

Lookbehind

Positive Lookbehind (?<=...)

Match only if preceded by:

fn main() {
    use fuzzy_regex::FuzzyRegex;

    // Match "world" only if preceded by "hello "
    let re = FuzzyRegex::new("(?<=hello )world").unwrap();

    assert!(re.is_match("hello world"));
    assert!(!re.is_match("bye world"));

    // Get match position
    let m = re.find("say hello world here").unwrap();
    assert_eq!(m.start(), 9); // After "hello "
}

Negative Lookbehind (?<!...)

Match only if NOT preceded by:

fn main() {
    use fuzzy_regex::FuzzyRegex;

    // Match "world" only if NOT preceded by "hello "
    let re = FuzzyRegex::new("(?<!hello )world").unwrap();

    assert!(re.is_match("bye world"));
    assert!(!re.is_match("hello world"));
}

Fuzzy Lookbehind

Lookbehind can include fuzzy matching:

fn main() {
    use fuzzy_regex::FuzzyRegex;

    // Match "world" preceded by "hello" with up to 1 error
    let re = FuzzyRegex::new("(?<=(?:hello){e<=1})world").unwrap();

    assert!(re.is_match("hello world")); // Exact
    assert!(re.is_match("hallo world")); // 1 substitution
    assert!(!re.is_match("goodbye world")); // Too different
}

Fuzzy Lookbehind Examples

fn main() {
    use fuzzy_regex::FuzzyRegex;

    // Allow insertions in lookbehind
    let re = FuzzyRegex::new("(?<=(?:hello){i<=2})world").unwrap();
    assert!(re.is_match("helllo world")); // 2 insertions (ll)
    assert!(re.is_match("hellllo world")); // 3 insertions - no match

    // Allow deletions in lookbehind
    let re = FuzzyRegex::new("(?<=(?:hello){d<=1})world").unwrap();
    assert!(re.is_match("helo world")); // 1 deletion (one 'l')
    assert!(!re.is_match("heo world")); // 2 deletions - no match

    // Combined: substitutions and insertions
    let re = FuzzyRegex::new("(?<=(?:hello){s<=1,i<=1})world").unwrap();
    assert!(re.is_match("hallo world")); // 1 substitution
    assert!(re.is_match("helloh world")); // 1 insertion
    assert!(re.is_match("hallo world")); // matches, prefers lower edits
}

Case Insensitive with Fuzzy

Combine case-insensitive matching with fuzzy for robust matching:

fn main() {
    use fuzzy_regex::FuzzyRegexBuilder;

    // Case-insensitive + fuzzy - case differences don't count as edits
    let re = FuzzyRegexBuilder::new("(?i)(?:hello){e<=1}")
        .build()
        .unwrap();

    assert!(re.is_match("hello")); // 0 edits
    assert!(re.is_match("HELLO")); // 0 edits (case difference is free!)
    assert!(re.is_match("hallo")); // 1 edit (actual substitution)

    // Builder's case_insensitive flag works the same way
    let re2 = FuzzyRegexBuilder::new("(?:hello){e<=1}")
        .case_insensitive(true)
        .build()
        .unwrap();

    assert!(re2.is_match("HELLO")); // 0 edits
    assert!(re2.is_match("HeLLo")); // 0 edits (all case differences)

    // Get edit count to verify
    let m = re.find("HELLO").unwrap();
    assert_eq!(m.total_edits(), 0); // No edits charged for case!
}

Fuzzy Lookahead

Fuzzy matching also works with lookahead:

fn main() {
    use fuzzy_regex::FuzzyRegex;

    // Match "hello" followed by "world" with up to 1 error
    let re = FuzzyRegex::new("hello(?= {e<=1}world)").unwrap();

    assert!(re.is_match("hello world")); // Exact
    assert!(!re.is_match("hello world!")); // ! is not space, lookahead fails
}

Variable-Length Lookbehind

fn main() {
    use fuzzy_regex::FuzzyRegex;

    // Variable-length lookbehind (alternation)
    let re = FuzzyRegex::new("(?<=(?:hello|hi) )world").unwrap();
    println!("{}", re.is_match("hi world"));
}