在Django查询中处理外文字符

3 投票
4 回答
2302 浏览
提问于 2025-04-17 10:52

我正在建立一个从GeoNames.com导入城市名称的搜索功能。有些城市的名字里有国际字符。比如,"Istanbul"在数据库里其实是"İstanbul"。

当人们搜索"Istanbul"时,"İstanbul"却没有显示出来。

有没有办法在搜索中添加一个过滤器或解码器,让它知道"İstanbul"等于"Istanbul"呢?

目前的情况是:

cities = City.objects.filter(name__icontains=query)

4 个回答

0

在没有更多信息的情况下,很难给出明确的建议,因为我们不知道你想要什么样的行为。

不过,有一个很明显的步骤就是为每个名字定义一个标准形式(比如小写字母,没有重音符号等等),然后在数据库中除了存储正确的名字外,还要在第二列存储这个标准形式。接着,把用户搜索的字符串也映射到这个标准形式上。比如,“istanbul”可以作为“İstanbul”的标准形式。

另一个明显的步骤是把城市名字单独放在一个表格中,而不是和城市的其他信息放在一起。这样,每个城市可以有多个名字,也就是同义词。然后,对于每个城市名字,可以定义尽可能多的同义词,以便捕捉用户可能使用的不同拼写方式。例如,你可以把“Istanbul”和“イスタンブル”都作为“İstanbul”的同义词。

当然,你也可以把这两种方法结合起来使用。

1

我觉得在Django里没有现成的解决方案。

我会在数据库里创建一个单独的列,叫做类似“名称组合”的东西,把所有可能的组合放进去,比如“伊斯坦布尔伊斯坦布尔”,然后进行查询。

cities = City.objects.filter(NameCombinations__icontains=query)
6

Unidecode 可以帮助你解决这个问题的一种情况。Unidecode 会把一些非 ASCII 字符转换成 ASCII 字符,比如:

>>> from unidecode import unidecode
>>> unidecode(u"İstanbul")
'Istanbul'

你也可以通过拆分 Unicode 字符并去掉组合的音调符号来达到类似的效果。不过,这种方法有个问题,就是有些字符是无法拆分的。比如,"ö" 可以拆分成 "o" 和一个变音符号,但 "Ł"(带横杠的 L)就无法拆分。Unidecode 能成功把 "Ł" 转换成 "L"。

不过,Unidecode 并不能解决你所有的问题;城市的名字可能有不同的叫法,或者这些名字的写法也可能不同。举个例子,在美国我们叫中国的首都 "Beijing",但以前我们叫它 "Peking"(在瑞典语中仍然叫 "Peking"),而用 unidecode 转换这个名字会得到其他的结果:

>>> unidecode(u"\u5317\u4EB0")
'Bei Jing '

最好的解决办法是准备一个特定语言的名字列表,而不是使用城市的实际名称。

撰写回答