這裡介紹如何使用 Perl 的 String::Approx 模組進行模糊字串比對(approximate matching)。

模糊搜尋是實務上常用的搜尋方法,當使用者輸入的關鍵字有些誤差時,透過模糊搜尋還是可以找到使用者想要找的資訊,例如 colorcolour 兩個字其實是一樣的,如果使用者輸入其中一個,使用一般性的比對就找不到另外一個,或是說使用者根本把單字拼錯,那一般的搜尋就更找不到了。


在 Perl 程式語言中,我們可以使用 String::Approx 這個 Perl 模組可以讓您進行模糊字串比對,以下是使用的範例教學。

首先安裝 String::Approx 這個 Perl 模組,在 Ubuntu Linux 中可以用 apt 安裝:

apt-get install libstring-approx-perl

接著就可以使用了,下面這個是一個簡單的 Hello, World 程式:

#!/usr/bin/perl
use String::Approx qw(amatch);

@list = <DATA>;
$pattern = "strong";

@matches = amatch($pattern, @list);
print @matches;

__DATA__
steamer
strait
string
stringed
strings
strong
strophe
stream
stem
stemming

這裡我們首先引入 String::Approx 模組的 amatch 函數,然後利用 amatch 比對 @list 中的字串,搜尋 $pattern 這個關鍵字,並輸出比對成功的字串結果。

這裡 __DATA__ 那一行以下的都是一般的文字資料,放在這裡的資料可以在程式中使用 <DATA> 來讀取。

這裡我們將 $pattern 設為 strong,而程式執行之後,搜尋出來的結果會像這樣:

string
stringed
strings
strong

除了找出 strong 之外,幾個類似的字眼也會一併找出來,如果使用者是要找 string,但是打錯字輸入成 strong,用模糊搜尋還是可以找得出來,這就是模糊搜尋的好處。

如果只是要判斷是否有比對成功,可以這樣做:

if (amatch($pattern, @list)) {
	# matched
}

在預設的情況下,amatch 會比對兩個字串之間的差異,如果差異在一定的門檻值以內(預設為 $pattern 長度的十分之一),就會視為比對成功,差異的計算是看 $pattern 與比對的字串之間增加、減少或是改變了幾個字元,實際的計算方式請參考 Levenshtein distance 的說明。

您可以直接指定這個字串差異的門檻值,可以使用百分比,或是差異數目(edit),或是兩者同時使用:

amatch($pattern, ["3 30%"], @list);

當同時指定多個值的時候,amatch 會檢查每一個門檻值,當每一個門檻值都符合的時候,才會視為比對成功。

另外也可以針對插入(Insertions)、刪除(Deletions)與替換(Substitutions)的門檻值做指定:

amatch($pattern, ["I2 D20% S0"], @list);

加入 i 可以讓比對時忽略大小寫的差異:

amatch($pattern, ["i I2 D20% S0"], @list);

如果想要檢查 字串之間的 Levenshtein distance,可以使用 adist

use String::Approx 'adist';
$dist = adist("pattern", $input);
@dist = adist("pattern", @input);

另外也可以使用 adistr 計算相對的距離,如果距離為 0 代表完全符合,若是 1 代表完全不符合:

use String::Approx 'adistr';
$dist = adistr("pattern", $input);
@dist = adistr("pattern", @inputs);

您可以結合 adistadistr 將多個字串依照相似性來排序,這個排序方法在許多應用上都常常用到:

my %d;
@d{@inputs} = map { abs } adistr("pattern", @inputs);
my @d = sort { $d{$a} <=> $d{$b} } @inputs;

如果您只是想要計算字串之間的 Levenshtein distance,那麼也可以使用 Perl 的 Text::LevenshteinText::LevenshteinXS 模組,另外 Text::WagnerFischerText::PhraseDistance 也可以做類似的事情。

最後要提醒一點,String::Approx 的執行速度通常比 Perl 內建的比對函數慢上數十倍,所以如果您需要比對大量的字串時,建議還是盡量使用一般的常規表達式來處理,真的沒辦法才使用 String::Approx 模組。

參考資料:Safari Books Online