【问题标题】:Find a distance measure of graphical similarity of two strings找到两个字符串的图形相似性的距离度量
【发布时间】:2018-07-18 18:23:37
【问题描述】:

我没有找到任何这样的包,最好是在 Python 中。是否有一些库允许以图形方式比较两个字符串?

例如,当使用я 而不是R,或者更糟糕的是,使用Α(大写字母,0x0391)而不是A 来混淆时,这将有助于打击垃圾邮件他们的字符串。

此类包的接口可能类似于

distance("Foo", "Bar")  # large distance
distance("Αяe", "Are")  # small distance

谢谢!

【问题讨论】:

  • 您是说希望将结果可视化,还是希望计算机在应用编辑距离之类的东西时区分大写字母和拉丁字母“A”?
  • @ODP 他想要衡量字符的视觉相似性,即两个字符串 a1,a2 的数字 s(a1,a2) 告诉他它们看起来有多相似。我不认为这样的包存在
  • @ODP 希望通过编辑澄清
  • @ODP 无论如何,看看 difflib (特别是序列匹配器)。但我认为它只有助于解决部分问题(以不同的顺序区分相同字母之间的相似性)。字符串也有很多距离度量,我只是不认为它们很容易推广到其他 unicode 字符,甚至它们之间的视觉相似性。您可能必须事先修正相似度数。
  • 还可以查看stackoverflow.com/questions/10433657/…security.stackexchange.com/questions/128286/… 以获取有关该主题的一些有用参考。

标签: python string


【解决方案1】:

我不知道有这样的软件包。但是,您可以使用homoglyph attack generator、Unicode 联盟的confusables、来自维基百科页面IDN homograph attack 或其他此类资源的工具来构建您自己的相似库并构建基于分数的库关于那个。

编辑:看起来 Unicode 人员已经编译了一个很棒的大型数据库,其中包含相似的字符。它可用here。如果我是你,我会构建一个脚本来将其读入 Python 字典,然后解析你的字符串以进行匹配。摘录如下:

FF4A ;  006A ;  MA  # ( j → j ) FULLWIDTH LATIN SMALL LETTER J → LATIN SMALL LETTER J # →ϳ→
2149 ;  006A ;  MA  # ( ⅉ → j ) DOUBLE-STRUCK ITALIC SMALL J → LATIN SMALL LETTER J # 
1D423 ; 006A ;  MA  # ( ? → j ) MATHEMATICAL BOLD SMALL J → LATIN SMALL LETTER J  # 
1D457 ; 006A ;  MA  # ( ? → j ) MATHEMATICAL ITALIC SMALL J → LATIN SMALL LETTER J  # 

【讨论】:

  • 这似乎足够令人满意。我不会将答案标记为已接受,以防有人出现在某些现有库中这样做,但考虑到这些数据库,这样做似乎很容易。
【解决方案2】:

利用his answer 中提供的@Richard 信息,我想出了这个实现UTS#39 的简短Python 3 脚本:

"""Implement the simple algorithm laid out in UTS#39, paragraph 4
"""

import csv
import re
import unicodedata

comment_pattern = re.compile(r'\s*#.*$')


def skip_comments(lines):
    """
    A filter which skip/strip the comments and yield the
    rest of the lines

    :param lines: any object which we can iterate through such as a file
        object, list, tuple, or generator
    """

    for line in lines:
        line = comment_pattern.sub('', line).strip()
        if line:
            yield line


def normalize(s):
    return unicodedata.normalize("NFD", s)


def to_unicode(code_point):
    return chr(int("0x" + code_point.lower(), 16))


def read_table(file_name):
    d = {}
    with open(file_name) as f:
        reader = csv.reader(skip_comments(f), delimiter=";")
        for row in reader:
            source = to_unicode(row[0])
            prototypes = map(to_unicode, row[1].strip().split())
            d[source] = ''.join(prototypes)
    return d
TABLE = read_table("confusables.txt")


def skeleton(s):
    s = normalize(s)
    s = ''.join(TABLE.get(c, c) for c in s)
    return normalize(s)


def confusable(s1, s2):
    return skeleton(s1) == skeleton(s2)


if __name__ == "__main__":
    for strings in [("Foo", "Bar"), ("Αяe", "Are"), ("j", "j")]:
        print(*strings)
        print("Equal:", strings[0] == strings[1])
        print("Confusable:", confusable(*strings), "\n")

假定文件confusables.txt 位于运行脚本的目录中。此外,我不得不删除该文件的第一个字节,因为它是一些奇怪的、不可打印的符号。

它只遵循第 4 段开头列出的简单算法,而不是 4.1 和 4.2 中列出的更复杂的全脚本和混合脚本可混淆的情况。这留给读者作为练习。

请注意,“я”和“R”不会被 unicode 组混淆,因此这两个字符串将返回 False

【讨论】:

    猜你喜欢
    • 2020-03-05
    • 2012-07-25
    • 2021-10-12
    • 2018-09-21
    • 2013-05-05
    相关资源
    最近更新 更多