क्या फजी स्ट्रिंग मिलान के लिए कोई कार्यक्रम है, जो एक मैच स्कोर प्रदान करता है?


17

मेरे पास फ़ाइल Aऔर फ़ाइल में स्ट्रिंग की सूची है B। मैं फ़ाइल A में प्रत्येक स्ट्रिंग लेना चाहता हूं और फ़ाइल B में सबसे समान स्ट्रिंग ढूंढना चाहता हूं।

इसके लिए, मैं एक ऐसे उपकरण की तलाश कर रहा हूं, जो फजी तुलना प्रदान करता हो।

उदाहरण के लिए:

$ fuzzy_compare "Some string" "Some string"
100

जहां 100 कुछ समानता अनुपात है। उदाहरण के लिए लेवेंसाइटिन दूरी

क्या इसकी कोई उपयोगिता है? मैं पहिए को फिर से नहीं लगाना चाहता।


1
मैंने आपके प्रश्न को स्पष्टता में सुधार करने के लिए संपादित किया है, लेकिन यह फ़ाइलए में प्रत्येक स्ट्रिंग की तुलना करने के बारे में पूछने के लिए इसे बदल दिया है और न केवल पहले वाले को। मैंने मान लिया कि आपका क्या मतलब था लेकिन अगर मैं गलत था तो कृपया मुझे सुधारें।
टेराडॉन


@ मुरु नहीं, यह केवल फजी मिलान के लिए है, ओपी को एक अंक की आवश्यकता है।
टेर्डन

जवाबों:


23

मुझे यह पृष्ठ मिला है जो विभिन्न भाषाओं में लेवेंसहाइट दूरी एल्गोरिथ्म का कार्यान्वयन प्रदान करता है। इसलिए, उदाहरण के लिए, आप कर सकते हैं:

#!/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"

इसे सहेजें ~/bin/levenshtein.sh, इसे निष्पादन योग्य बनाएं ( chmod a+x ~/bin/levenshtein.sh) और इसे अपनी दो फाइलों पर चलाएं। उदाहरण के लिए:

$ 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

यह कुछ पैटर्न के लिए ठीक है, लेकिन बड़ी फ़ाइलों के लिए बहुत धीमी हो जाएगी । यदि यह एक समस्या है, तो अन्य भाषाओं में कार्यान्वयन में से एक का प्रयास करें। उदाहरण के लिए पर्ल:

#!/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";
    }
}

ऊपर के रूप में, स्क्रिप्ट को सहेजें ~/bin/levenshtein.plऔर इसे निष्पादन योग्य बनाएं और इसे दो फ़ाइलों के साथ तर्क के रूप में चलाएं:

~/bin/levenstein.pl fileA fileB

यहां तक ​​कि यहां उपयोग की जाने वाली बहुत छोटी फाइलों में, पर्ल दृष्टिकोण बैश की तुलना में 10 गुना तेज है:

$ 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

परिणामों के बारे में कुछ और स्पष्टीकरण जोड़ने के लिए: विकिपीडिया को उद्धृत करते हुए, दो शब्दों के बीच लेवेंसहाइट की दूरी एक शब्द को दूसरे में बदलने के लिए आवश्यक एकल-वर्ण संपादन (यानी सम्मिलन, विलोपन या प्रतिस्थापन) की न्यूनतम संख्या है । इसका मतलब है कि संख्या जितनी कम होगी, मैच उतना ही बेहतर होगा। शून्य की संख्या का मतलब है एक परिपूर्ण मैच। यह भी ध्यान दें कि लेवेंसहाइट दूरी प्रत्येक चरित्र संपादन को समान रूप से संभालती है, जिसका अर्थ है "फू" और "फू" समान दूरी को "फू" और "लोमड़ी" के रूप में ले जाते हैं।
scai
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.