Bulanık dize eşleme için bir maç puanı sağlayan herhangi bir program var mı?


17

Dosya Ave dosya dizeleri listesi var B. Dosya A'daki her dizeyi alıp dosya B'deki en benzer dizeyi bulmak istiyorum.

Bunun için bulanık karşılaştırma sağlayan bir araç arıyorum.

Örneğin:

$ fuzzy_compare "Some string" "Some string"
100

Burada 100 eşitlik oranıdır. Örneğin Levenshtein mesafesi .

Herhangi bir yardımcı program var mı? Tekerleği yeniden icat etmek istemiyorum.


1
Açıklığı geliştirmek için sorunuzu düzenledim, ancak dosyaA'daki her dizeyi yalnızca ilkiyle değil, dosyaB'deki dizelerle karşılaştırmayı soracak şekilde değiştirdim . Ne demek istediğini sanıyordum ama yanılmışsam lütfen beni düzelt.
terdon


@muru no, bu sadece bulanık eşleme için, OP'nin bir puana ihtiyacı var.
terdon

Yanıtlar:


23

Levenshtein mesafe algoritmasının farklı dillerde uygulanmasını sağlayan bu sayfayı buldum . Yani, örneğin bash'da şunları yapabilirsiniz:

#!/bin/bash
function levenshtein {
    if [ "$#" -ne "2" ]; then
        echo "Usage: $0 word1 word2" >&2
    elif [ "${#1}" -lt "${#2}" ]; then
        levenshtein "$2" "$1"
    else
        local str1len=$((${#1}))
        local str2len=$((${#2}))
        local d i j
        for i in $(seq 0 $(((str1len+1)*(str2len+1)))); do
            d[i]=0
        done
        for i in $(seq 0 $((str1len))); do
            d[$((i+0*str1len))]=$i
        done
        for j in $(seq 0 $((str2len))); do
            d[$((0+j*(str1len+1)))]=$j
        done

        for j in $(seq 1 $((str2len))); do
            for i in $(seq 1 $((str1len))); do
                [ "${1:i-1:1}" = "${2:j-1:1}" ] && local cost=0 || local cost=1
                local del=$((d[(i-1)+str1len*j]+1))
                local ins=$((d[i+str1len*(j-1)]+1))
                local alt=$((d[(i-1)+str1len*(j-1)]+cost))
                d[i+str1len*j]=$(echo -e "$del\n$ins\n$alt" | sort -n | head -1)
            done
        done
        echo ${d[str1len+str1len*(str2len)]}
    fi
}

while read str1; do
        while read str2; do
                lev=$(levenshtein "$str1" "$str2");
                printf '%s / %s : %s\n' "$str1" "$str2" "$lev"
        done < "$2"
done < "$1"

Bunu farklı kaydedin, ~/bin/levenshtein.shyürütülebilir ( chmod a+x ~/bin/levenshtein.sh) yapın ve iki dosyada çalıştırın. Örneğin:

$ cat fileA
foo
zoo
bar
fob
baar
$ cat fileB
foo
loo
baar
bob
gaf
$ a.sh fileA fileB
foo / foo : 0
foo / loo : 1
foo / baar : 4
foo / bob : 2
foo / gaf : 3
zoo / foo : 1
zoo / loo : 1
zoo / baar : 4
zoo / bob : 2
zoo / gaf : 3
bar / foo : 3
bar / loo : 3
bar / baar : 1
bar / bob : 2
bar / gaf : 2
fob / foo : 1
fob / loo : 2
fob / baar : 4
fob / bob : 1
fob / gaf : 3
baar / foo : 4
baar / loo : 4
baar / baar : 0
baar / bob : 3
baar / gaf : 3

Bu birkaç desen için iyidir, ancak daha büyük dosyalar için çok yavaş olacaktır . Bu bir sorunsa, diğer dillerdeki uygulamalardan birini deneyin. Örneğin Perl:

#!/usr/bin/perl 
use List::Util qw(min);

sub levenshtein
{
    my ($str1, $str2) = @_;
    my @ar1 = split //, $str1;
    my @ar2 = split //, $str2;

    my @dist;
    $dist[$_][0] = $_ foreach (0 .. @ar1);
    $dist[0][$_] = $_ foreach (0 .. @ar2);

    foreach my $i (1 .. @ar1) {
        foreach my $j (1 .. @ar2) {
            my $cost = $ar1[$i - 1] eq $ar2[$j - 1] ? 0 : 1;
            $dist[$i][$j] = min(
                            $dist[$i - 1][$j] + 1, 
                            $dist[$i][$j - 1] + 1, 
                            $dist[$i - 1][$j - 1] + $cost
                             );
        }
    }

    return $dist[@ar1][@ar2];
}
open(my $fh1, "$ARGV[0]");
open(my $fh2, "$ARGV[1]");
chomp(my @strings1=<$fh1>);
chomp(my @strings2=<$fh2>);

foreach my $str1 (@strings1) {
    foreach my $str2 (@strings2) {
        my $lev=levenshtein($str1, $str2);
        print "$str1 / $str2 : $lev\n";
    }
}

Yukarıdaki gibi, komut dosyasını farklı kaydedin ~/bin/levenshtein.plve yürütülebilir yapın ve iki dosyayla bağımsız değişken olarak çalıştırın:

~/bin/levenstein.pl fileA fileB

Burada kullanılan çok küçük dosyalarda bile Perl yaklaşımı bash'den 10 kat daha hızlıdır:

$ time levenshtein.sh fileA fileB > /dev/null

real    0m0.965s
user    0m0.070s
sys     0m0.057s

$ time levenshtein.pl fileA fileB > /dev/null
real    0m0.011s
user    0m0.010s
sys     0m0.000s

Sonuçlar hakkında daha fazla açıklama eklemek için: wikipedia'dan alıntı yapmak için, iki kelime arasındaki Levenshtein mesafesi, bir kelimeyi diğerine değiştirmek için gereken minimum tek karakterli düzenleme sayısıdır (yani, ekleme, silme veya ikame) . Yani sayı ne kadar düşükse maç o kadar iyi olur. Sıfır sayısı mükemmel bir eşleşme anlamına gelir. Ayrıca Levenshtein mesafesinin her karakter düzenlemesini eşit olarak ele aldığını, yani "foo" ve "Foo" ifadelerinin "foo" ve "fox" ile aynı mesafeye yol açtığını unutmayın.
scai
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.