postgresql - Postgresql levenshtein 和预组合字符与组合字符
问题描述
我有包含两个相似字符的字符串。两者都显示为带有 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
因此,当我将这两个字符与levenshtein和unaccent进行比较时,结果为 1:
select levenshtein(unaccent('ą'), unaccent('ą'));
levenshtein
-------------
1
而不是 0。
在第二种情况下,我怎样才能“摆脱 ogonek”?
(如何)我可以使用字符串的 UTF-8 代码来获得实现的结果吗?
编辑:正如@s-man 建议的那样,添加组合字符unaccent.rules
将解决这个特定问题。但是要普遍解决 unaccent 的预组合字符与组合字符问题,我必须在配置中显式添加/修改每个丢失/“错误配置”的组合字符。
解决方案
去除重音会给你一个 0 的 Levenshtein 距离,但它也会给你一个 0 和 之间的距离ą
,a
这听起来并不理想。
更好的解决方案是规范化Unicode 字符串,即在比较它们之前将组合字符序列E'a\u0328'
转换为预先组合的字符。E'\u0105'
不幸的是,Postgres 似乎没有内置的 Unicode 规范化功能,但您可以通过PL/Perl或PL/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
推荐阅读
- javascript - discord.js 随机图像显示不正确
- google-cloud-platform - Deployment Manager 无法更新实例模板 - NO_METHOD_TO_UPDATE_FIELD
- f# - 为什么在 IComparable 和比较方面这两个定义上推断的 F# 类型不同?
- shell - 解析大量文件的有效方法
- c# - 任务WhenAll异常处理
- php - 从 PHP 中的目录和文本文档导入数据
- python - InternalError: (pymysql.err.InternalError) 使用 sqlalchemy 插入数据时
- python - 如何让这个程序更详细,并在我玩完后告诉我胜率?
- database - Opensips avp_db_query 无法比较空值
- laravel - 通过 $id 更新数据,我想使用另一个模型对象作为控制器函数参数并在 Laravel 中重命名该参数