首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >Devnagari字符串比较

Devnagari字符串比较
EN

Stack Overflow用户
提问于 2019-10-04 09:34:54
回答 2查看 217关注 0票数 2

我在文本文件中有数十万devnagari单词(每行一个字)。为了更正,我不得不在另一个文件中复制类似于“अक्तूबर,अक्‍टूबर”“कौम,क़ौम”的最相似的单词。拷贝允许最大两位差。为此,我使用了"awk“来查找单词的差异,并将类似的单词复制到另一个文件中。但是它失败了,因为这个命令只适用于罗马字符,而不适用于devnagari字符。

代码语言:javascript
复制
awk -v string=कौम -v string1=क़ौम '{ for (i=1;i<=length(string);i++) { if (substr(string,i,1) != substr(string1,i,1)) { count++ } }} END { print (count/length(string)*100"% difference") }' <<< ""

66.6667%差异

以上百分比是错误的,因为以上两个词有很大的后面差异和预期的差异应该在5-10%之间。

你能告诉我在这种情况下该怎么做吗?

python,perl,shell任何东西都被接受。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2019-10-04 11:17:06

你似乎想比较一下字素簇。

一个字素聚类表示一个水平分割的文本单元,包括一些字素基(可以由一个韩语音节组成)以及应用于它的任意数量的非间距标记。

这只是一种“幻想”的方式,每一个字素聚类都是一个“视觉字符”。

我们确认一下。下面的程序可以让我们看到你的字符串,分为字素簇。

代码语言:javascript
复制
use open ':std', ':encoding(UTF-8)';

use charnames qw( :full );

for my $arg_idx (0..$#ARGV) {
   my $arg = $ARGV[$arg_idx];

   utf8::decode($arg);

   for my $grapheme_cluster ($arg =~ /\X/g) {
      printf("%s %v04X\n", $grapheme_cluster, $grapheme_cluster);
      for my $code_point (unpack('W*', $grapheme_cluster)) {
         printf("   %04X %s\n", $code_point, charnames::viacode($code_point));
      }
   }

   print("\n") if $arg_idx != $#ARGV;
}

对于你的一组字符串,我们得到

代码语言:javascript
复制
$ grapheme_clusters क़ौम              $ grapheme_clusters क़ौम           
कौ 0915.094C                         क़ौ 0915.093C.094C                 
   0915 DEVANAGARI LETTER KA            0915 DEVANAGARI LETTER KA       
                                        093C DEVANAGARI SIGN NUKTA      
   094C DEVANAGARI VOWEL SIGN AU        094C DEVANAGARI VOWEL SIGN AU   
म 092E                               म 092E                             
   092E DEVANAGARI LETTER MA            092E DEVANAGARI LETTER MA       

到目前为止还不错,这就产生了一个预期的差异。

对于另一组字符串,我们得到

代码语言:javascript
复制
$ grapheme_clusters अक्तूबर            $ grapheme_clusters अक्‍टूबर
अ 0905                               अ 0905         
   0905 DEVANAGARI LETTER A             0905 DEVANAGARI LETTER A
क् 0915.094D                          क्‍ 0915.094D.200D
   0915 DEVANAGARI LETTER KA            0915 DEVANAGARI LETTER KA
   094D DEVANAGARI SIGN VIRAMA          094D DEVANAGARI SIGN VIRAMA
                                        200D ZERO WIDTH JOINER
तू 0924.0942                          टू 091F.0942
   0924 DEVANAGARI LETTER TA            091F DEVANAGARI LETTER TTA
   0942 DEVANAGARI VOWEL SIGN UU        0942 DEVANAGARI VOWEL SIGN UU
ब 092C                               ब 092C           
   092C DEVANAGARI LETTER BA            092C DEVANAGARI LETTER BA
र 0930                               र 0930
   0930 DEVANAGARI LETTER RA            0930 DEVANAGARI LETTER RA       

啊,里面有个意想不到的ZERO WIDTH JOINER。如果我们要删除它(例如使用s/\N{ZERO WIDTH JOINER}//g,或者通过使用s/\pC//g删除所有控制字符),我们就会得到预期的单一差异。

既然我们已经确定了所需的内容,我们就可以编写解决方案了。

代码语言:javascript
复制
use List::Util qw( max );

sub count_diffs {
   my ($s1, $s2) = @_;

   s/\N{ZERO WIDTH JOINER}//g for $s1, $s2;

   my @s1 = $s1 =~ /\X/g;
   my @s2 = $s2 =~ /\X/g;

   no warnings qw( uninitialized );
   return 0+grep { $s1[$_] ne $s2[$_] } 0..max(0+@s1, 0+@s2)-1;
}

这种方法的一个主要问题是它不能很好地处理插入或删除。例如,它认为abcdefbcdef有6个不同之处。计算簇序的Levenshtein距离比按指数进行比较要有效得多。

代码语言:javascript
复制
use Algorithm::Diff qw( traverse_balanced );

sub count_diffs {
   my ($s1, $s2) = @_;

   s/\N{ZERO WIDTH JOINER}//g for $s1, $s2;

   my @s1 = $s1 =~ /\X/g;
   my @s2 = $s2 =~ /\X/g;

   my $diffs = 0;
   traverse_balanced(\@s1, \@s2,
      {
         DISCARD_A => sub { ++$diffs; },
         DISCARD_B => sub { ++$diffs; },
         CHANGE    => sub { ++$diffs; },
      },
   );

   return $diffs;
}

最后,出于性能考虑,您不希望一次只比较两个字符串;您希望同时比较每个字符串和每个其他字符串。我不知道有什么很好用的解决方案。

票数 3
EN

Stack Overflow用户

发布于 2019-10-04 10:32:15

代码语言:javascript
复制
use utf8;
use List::Util qw(sum max);
use List::SomeUtils qw(pairwise);

sub norm { $_[0] =~ s/\pC//gr =~ /\X/g }
for my $pair (
    [qw(अक्तूबर अक्‍टूबर)],
    [qw(कौम क़ौम)],
) {
    my @e0 = norm $pair->[0];
    my @e1 = norm $pair->[1];
    my $equal = sum pairwise { $a eq $b } @e0, @e1;
    my $max = max scalar(@e0), scalar(@e1);
    my $similarity = $equal / $max;
    printf "%.1f%% similarity, %.1f%% difference\n",
        100 * $similarity,
        100 * (1 - $similarity);
}
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/58233681

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档