3

我有包含两个相似字符的字符串。两者都显示为带有 ogonek 的小 '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

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

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

而不是 0。

在第二种情况下,我怎样才能“摆脱 ogonek”?

(如何)我可以使用字符串的 UTF-8 代码来获得实现的结果吗?

编辑:正如@s-man 建议的那样,添加组合字符unaccent.rules将解决这个特定问题。但是要普遍解决 unaccent 的组合字符与组合字符问题,我必须在配置中显式添加/修改每个丢失/“错误配置”的组合字符。

4

3 回答 3

3

去除重音会给你一个 0 的 Levenshtein 距离,但它也会给你一个 0 和 之间的距离ąa这听起来并不理想。

更好的解决方案是规范化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

这也解决了您上一个问题中的问题,其中组合字符对 Levenshtein 距离有贡献:

test=# select levenshtein(unicode_normalize(E'a\u0328'), 'x');
 levenshtein
-------------
           1
于 2019-06-20T14:40:19.707 回答
2

您必须更改配置并在配置文件中手动添加缺少的字符,如https://postgresql.org/docs/current/unaccent.html中所述

于 2019-06-20T12:02:07.570 回答
1

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

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

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

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

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

unaccent检查“多字符”字符中的每个字符以进行替换,因此没有必要考虑基本字符和变音符号的每个组合。

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

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

再加上这个问题的 ogonek,这是缺失的:̨

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

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

于 2019-06-20T13:49:05.147 回答