Rust字符串模糊匹配库rapidfuzz的使用,rapidfuzz提供高效的字符串相似度计算与模糊搜索功能

Rust字符串模糊匹配库rapidfuzz的使用

RapidFuzz Logo

RapidFuzz是一个通用的字符串匹配库,提供Rust、C++和Python实现。

主要特性

  • 多样化的字符串度量:提供多种字符串度量方法,从基于编辑距离的Levenshtein距离到更精细相似度评估的Jaro-Winkler相似度
  • 性能优化:库设计时考虑了性能,每个实现都经过精心设计以确保最佳性能,适合分析大型数据集
  • 易用性:API设计简单易用,同时仍为优化实现留有空间

安装

安装非常简单:

cargo add rapidfuzz

使用示例

以下是使用Levenshtein距离的示例。其他度量方法可以在fuzzdistance模块中找到。

use rapidfuzz::distance::levenshtein;

// 使用Levenshtein距离进行简单比较
assert_eq!(
    3,
    levenshtein::distance("kitten".chars(), "sitting".chars())
);

// 如果确定输入字符串仅包含ASCII字符,通常对字节操作更快
assert_eq!(
    3,
    levenshtein::distance("kitten".bytes(), "sitting".bytes())
);

// 可以提供score_cutoff值来过滤掉距离比score_cutoff更差的字符串
assert_eq!(
    None,
    levenshtein::distance_with_args(
        "kitten".chars(),
        "sitting".chars(),
        &levenshtein::Args::default().score_cutoff(2)
    )
);

// 可以提供score_hint告诉实现预期的分数
// 这可以用来在内部选择更高效的实现,但当距离实际上比score_hint更差时可能会导致性能下降
assert_eq!(
    3,
    levenshtein::distance_with_args(
        "kitten".chars(),
        "sitting".chars(),
        &levenshtein::Args::default().score_hint(2)
    )
);

// 当比较单个字符串与多个字符串时,可以使用提供的`BatchComparators`
// 这些可以缓存部分计算结果,提供显著的加速
let scorer = levenshtein::BatchComparator::new("kitten".chars());
assert_eq!(3, scorer.distance("sitting".chars()));
assert_eq!(0, scorer.distance("kitten".chars()));

完整示例代码

use rapidfuzz::distance::levenshtein;

fn main() {
    // 基本字符串比较
    let distance = levenshtein::distance("rust".chars(), "rusty".chars());
    println!("Distance between 'rust' and 'rusty': {}", distance); // 输出: 1

    // 使用字节比较(ASCII字符串)
    let byte_distance = levenshtein::distance("rust".bytes(), "Rust".bytes());
    println!("Distance between 'rust' and 'Rust' (bytes): {}", byte_distance); // 输出: 1

    // 使用score_cutoff过滤结果
    let filtered_distance = levenshtein::distance_with_args(
        "hello".chars(),
        "world".chars(),
        &levenshtein::Args::default().score_cutoff(3)
    );
    println!("Filtered distance: {:?}", filtered_distance); // 输出: None

    // 批量比较器
    let names = vec!["Robert", "Rupert", "Rubin", "Robin"];
    let scorer = levenshtein::BatchComparator::new("Rust".chars());
    
    for name in names {
        let dist = scorer.distance(name.chars());
        println!("Distance between 'Rust' and '{}': {}", name, dist);
    }
    // 输出:
    // Distance between 'Rust' and 'Robert': 4
    // Distance between 'Rust' and 'Rupert': 3
    // Distance between 'Rust' and 'Rubin': 3
    // Distance between 'Rust' and 'Robin': 3
}

扩展示例:使用多种字符串度量方法

use rapidfuzz::{distance::levenshtein, fuzz::ratio};

fn main() {
    // 1. 使用Levenshtein距离
    let lev_dist = levenshtein::distance("book".chars(), "back".chars());
    println!("Levenshtein距离: {}", lev_dist); // 输出: 2

    // 2. 使用比率相似度
    let similarity = ratio("hello world".chars(), "hello word".chars());
    println!("相似度比率: {}", similarity); // 输出: 95

    // 3. 带权重的比较
    let weighted_dist = levenshtein::distance_with_args(
        "color".chars(),
        "colour".chars(),
        &levenshtein::Args::default()
            .weights(1, 1, 2) // 插入、删除、替换的权重
    );
    println!("带权重的距离: {}", weighted_dist); // 输出: 1

    // 4. 批量处理多个字符串
    let targets = ["apple", "aple", "appl", "peach", "peal"];
    let query = "apple";
    let scorer = levenshtein::BatchComparator::new(query.chars());
    
    println!("与'{}'的Levenshtein距离:", query);
    for target in targets {
        let dist = scorer.distance(target.chars());
        println!("- {}: {}", target, dist);
    }
    // 输出:
    // - apple: 0
    // - aple: 1
    // - appl: 1
    // - peach: 3
    // - peal: 3
}

许可证

根据Apache许可证2.0版或MIT许可证授权使用。除非您明确声明,否则您有意提交的任何贡献都应按上述方式双重许可,不附加任何额外条款或条件。


1 回复

Rust字符串模糊匹配库rapidfuzz使用指南

rapidfuzz是一个高效的Rust字符串模糊匹配库,提供多种字符串相似度计算算法和模糊搜索功能。它是Python库RapidFuzz的Rust实现,性能优异且功能丰富。

主要特性

  • 多种相似度算法:Levenshtein、Jaro、Jaro-Winkler等
  • 高性能:经过优化,处理速度快
  • 支持Unicode
  • 模糊搜索功能
  • 可自定义权重和评分

安装

在Cargo.toml中添加依赖:

[dependencies]
rapidfuzz = "0.12"

完整示例代码

下面是一个综合使用rapidfuzz功能的完整示例:

use rapidfuzz::distance::{levenshtein, jaro_winkler};
use rapidfuzz::fuzz;
use rapidfuzz::process;

fn main() {
    // 示例1: 计算Levenshtein距离和相似度
    let str1 = "kitten";
    let str2 = "sitting";
    
    let distance = levenshtein::distance(str1.chars(), str2.chars());
    println!("Levenshtein距离: {}", distance); // 输出: 3
    
    let score = levenshtein::normalized_similarity(str1.chars(), str2.chars());
    println!("相似度分数: {}", score); // 输出: 57.14285714285714

    // 示例2: 使用Jaro-Winkler相似度
    let str3 = "martha";
    let str4 = "marhta";
    
    let jw_score = jaro_winkler::similarity(str3.chars(), str4.chars());
    println!("Jaro-Winkler相似度: {}", jw_score); // 输出: 0.9611111111111111

    // 示例3: 模糊搜索
    let choices = vec!["apple", "banana", "orange", "grape", "melon"];
    let query = "appl";
    
    let best_match = process::extract_one(
        query,
        &choices,
        fuzz::ratio,
        process::ExtractOptions::default()
    );
    
    match best_match {
        Some((choice, score, _)) => {
            println!("最佳匹配: {} (分数: {})", choice, score); // 输出: 最佳匹配: apple (分数: 90)
        }
        None => println!("没有找到匹配"),
    }

    // 示例4: 带权重的部分匹配
    let food_choices = vec![
        "apple pie",
        "banana bread",
        "orange juice",
        "grape soda",
        "melon ball"
    ];
    
    let food_query = "apple";
    
    let best_partial = process::extract_one(
        food_query,
        &food_choices,
        levenshtein::partial_ratio,
        process::ExtractOptions::default()
    );
    
    match best_partial {
        Some((choice, score, _)) => {
            println!("最佳部分匹配: {} (分数: {})", choice, score); // 输出: 最佳部分匹配: apple pie (分数: 100)
        }
        None => println!("没有找到匹配"),
    }

    // 示例5: 自定义权重
    let weights = levenshtein::EditWeights {
        insertion_cost: 1,
        deletion_cost: 1,
        substitution_cost: 2, // 替换代价更高
    };
    
    let weighted_distance = levenshtein::distance_with_weights(
        str1.chars(), 
        str2.chars(), 
        &weights
    );
    println!("自定义权重距离: {}", weighted_distance); // 输出: 5

    // 示例6: 批量处理
    let fruit_choices = vec![
        "apple", 
        "banana", 
        "orange", 
        "grape", 
        "melon",
        "pineapple",
        "strawberry"
    ];
    
    let fruit_query = "appl";
    
    // 获取前3个最佳匹配
    let top_matches = process::extract(
        fruit_query,
        &fruit_choices,
        fuzz::ratio,
        process::ExtractOptions::default()
            .limit(3) // 限制结果数量
            .score_cutoff(70) // 最低分数阈值
    );
    
    println!("前3个匹配结果:");
    for (choice, score, _) in top_matches {
        println!("- {} (分数: {})", choice, score);
    }
    /* 输出:
    前3个匹配结果:
    - apple (分数: 90)
    - pineapple (分数: 72)
    */
}

性能建议

  1. 对于大量重复比较,考虑将字符串预处理为Vec<char>或使用StrWrapper
  2. 根据场景选择合适的算法:
    • 短字符串:Levenshtein
    • 长字符串:Jaro或Jaro-Winkler
    • 部分匹配:partial_ratio
  3. 设置合理的score_cutoff可以显著提高性能

rapidfuzz是处理字符串模糊匹配的强大工具,适用于拼写检查、搜索建议、数据清洗等多种场景。

回到顶部