PostgreSQL的levenshtein和预组合字符与组合字符

时间:2019-06-20 09:33:58

标签: postgresql utf-8 levenshtein-distance unaccent

我有包含两个相似外观字符的字符串。两者都显示为带有小齿的小字母“ a”:

±

ą

(注意:根据渲染器的不同,有时它们的渲染方式相似,有时略有不同)

但是,它们是不同的:

第一个字符的特征:

在PostgreSQL中:

select ascii('ą');
ascii 
-------
261

十六进制的UTF-8编码为:\xC4\x85

因此它是预先组成的字符https://en.wikipedia.org/wiki/Precomposed_character

第二个字符的特征:

在PostgreSQL中:

select ascii('ą');
ascii 
-------
97

(与字符“ a”相同)

强烈表示所呈现的字符是从两个字符中组合而成的。确实是这样:

十六进制的UTF-8编码为:\x61\xCC\xA8

所以这是

的组合

一个\x61\

组合字符https://en.wikipedia.org/wiki/Combining_character),即单独的ogonek:

̨\xCC\xA8

我想使用PostgreSQL的 levenshtein 函数来确定单词的相似性,因此我想将两个字符视为相同(因为这当然是写有区别的名字的人想要的)具有第一个或第二个字符的实体。

我认为我可以使用 unaccent 总是摆脱ogonek,但这在第二种情况下不起作用:

第一个字符:预期结果:

select levenshtein('ą', 'x');
levenshtein 
-------------
       1

第一个字符:预期结果:

select levenshtein(unaccent('ą'), 'x');
levenshtein 
-------------
       1

第二个字符:预期结果:

select levenshtein('ą', 'x');
levenshtein 
-------------
       2

第二个字符:意外结果:

select levenshtein(unaccent('ą'), 'x');
levenshtein 
-------------
       2

因此,当我将两个字符与 levenshtein unaccent 进行比较时,结果为1:

select levenshtein(unaccent('ą'), unaccent('ą'));
levenshtein 
-------------
       1

而不是0。

在第二种情况下如何“摆脱ogonek”?

(如何)使用字符串的UTF-8代码获得所获得的结果?

编辑:如@ s-man所建议,在unaccent.rules中添加组合字符将解决此特定问题。但是通常要使用 unaccent 解决 组合字符与组合字符 的问题,我必须明确添加/修改每个缺少的/“配置错误”的字符组合字符到/在配置中。

3 个答案:

答案 0 :(得分:2)

您必须更改配置并按照https://postgresql.org/docs/current/unaccent.html

中的说明在配置文件中手动添加缺少的字符

答案 1 :(得分:2)

删除重音将使Levenshtein距离为0,但在ąa之间的距离也为0,这听起来并不理想。

更好的解决方案是normalise Unicode字符串,即在比较它们之前将组合字符序列E'a\u0328'转换为预组合字符E'\u0105'

不幸的是,Postgres似乎没有内置的Unicode规范化功能,但是您可以通过PL/PerlPL/Python语言扩展名轻松地访问它。

例如:

create extension plpythonu;

create or replace function unicode_normalize(str text) returns text as $$
  import unicodedata
  return unicodedata.normalize('NFC', str.decode('UTF-8'))
$$ language plpythonu;

然后:

test=# select levenshtein(unicode_normalize(E'a\u0328'), unicode_normalize(E'\u0105'));
 levenshtein
-------------
           0

这也解决了your previous question中的问题,在该问题中,组合字符导致了Levenshtein距离:

test=# select levenshtein(unicode_normalize(E'a\u0328'), 'x');
 levenshtein
-------------
           1

答案 2 :(得分:1)

  

注意:此解决方案基于@ S-Man的建议,即向unaccent.rules文件中显式添加缺少的字符。

     

注意:此答案的前提是相关的预先组成的字符https://en.wikipedia.org/wiki/Precomposed_character)已映射到unaccent.rules文件中。如果没有,则还必须添加它们。

有些字符由多个字符组成:

目标是在包含“基本”字符的地方映射“多个字符”。

(假设相应的预组合字符已映射到“基本”字符,原始unaccent.rules文件中就是这种情况)

不重音会检查“多个字符”字符中的每个字符以进行替换,因此不必考虑基本字符和变音符号的每种组合。

相反,变音符号必须映射在[nothing]上。这可以通过将unaccent.rules文件(https://postgresql.org/docs/current/unaccent.html)中的第二列保留为空来实现。

这是从https://en.wikipedia.org/wiki/Diacritic获得的拉丁字母变音符号列表: ´ ˝ ` ̏ ˆ ˇ ˘ ̑ ¸ ¨ · ̡ ̢ ̉ ̛ ͅ ˉ ˛ ͂ ˚ ˳ ῾ ᾿

添加到该问题的ogonek中: ̨

现在(当然,在PostgreSQL重新启动之后),不重音将“多个字符”字符映射到“基本”字符上,就像对预组合字符一样。

注意:上面的列表可能并不完整,但至少应解决“预组合字符与组合字符”问题的很大一部分。