我有一个存储在MySQL数据库中的Geonames数据库的副本,以及一个允许用户在数据库中搜索其城市的PHP应用程序。如果他们用英语输入城市名称,效果很好,但我希望他们能够用他们的母语进行搜索。
例如,他们应该能够搜索東京
,而不是要求讲日语的人搜索Tokyo
。
Geonames 数据库包含一个带有"alternatenames, comma separated, ascii names automatically transliterated, convenience attribute from alternatename table, varchar(10000)
"的alternatenames
列。
例如,Tokyo
行的alternatenames
值为Edo,TYO,Tochiu,Tocio,Tokija,Tokijas,Tokio,Tokió,Tokjo,Tokyo,Toquio,Toquio - dong jing,Toquio - æ±äº¬,Tòquio,Tókýó,Tóquio,TÅkyÅ,dokyo,dong jing,dong jing dou,tokeiyw,tokkiyo,tokyo,twkyw,twqyw,Τόκιο,Токио,Токё,Токіо,ÕÕ¸Õ¯Õ«Õ¸,טוקיו,توكيو,توکیو,طوكيو,ܛܘܟÜܘ,ܜܘܟÜܘ,टोकà¥à¤¯à¥‹,டோகà¯à®•à®¿à®¯à¯‹,โตเà¸à¸µà¸¢à¸§,ტáƒáƒ™áƒ˜áƒ,东京,æ±äº¬,æ±äº¬éƒ½,ë„ì¿„
。
这些值并不完全包含東京
,但我猜它们包含以某种方式编码或转换的一种形式。因此,我假设如果我对搜索字符串执行相同的编码/转换,那么我将能够匹配该行。例如:
mysql_query( sprintf( "
SELECT * FROM geoname
WHERE
MATCH( name, asciiname, alternatenames )
AGAINST ( %s )
LIMIT 1",
iconv( 'UTF-8', 'ASCII', '東京' )
) );
问题是我不知道这种转换会是什么。我尝试了很多iconv()
、mb_convert_string()
等的组合,但没有运气。
MySQL 表如下所示:
CREATE TABLE `geoname` (
`geonameid` int(11) NOT NULL DEFAULT '0',
`name` varchar(200) DEFAULT NULL,
`asciiname` varchar(200) DEFAULT NULL,
`alternatenames` mediumtext,
`latitude` decimal(10,7) DEFAULT NULL,
`longitude` decimal(10,7) DEFAULT NULL,
`fclass` char(1) DEFAULT NULL,
`fcode` varchar(10) DEFAULT NULL,
`country` varchar(2) DEFAULT NULL,
`cc2` varchar(60) DEFAULT NULL,
`admin1` varchar(20) DEFAULT NULL,
`admin2` varchar(80) DEFAULT NULL,
`admin3` varchar(20) DEFAULT NULL,
`admin4` varchar(20) DEFAULT NULL,
`population` int(11) DEFAULT NULL,
`elevation` int(11) DEFAULT NULL,
`gtopo30` int(11) DEFAULT NULL,
`timezone` varchar(40) DEFAULT NULL,
`moddate` date DEFAULT NULL,
PRIMARY KEY (`geonameid`),
KEY `timezone` (`timezone`),
FULLTEXT KEY `namesearch` (`name`,`asciiname`,`alternatenames`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8mb4
谁能指出我正确的方向?
当我下载日本文件并设置这样的数据库时:
CREATE TABLE geonames (
geonameid SERIAL,
name varchar(200),
asciiname varchar(200),
alternatenames varchar(10000),
latitude float,
longitude float,
featureclass varchar(1),
featurecode varchar(10),
countrycode varchar(2),
cc2 varchar(200),
admin1code varchar(20),
admin2code varchar(80),
admin3code varchar(20),
admin4code varchar(20),
population BIGINT,
elevation INT,
dem INT,
timezone varchar(40),
modificationdate DATE
) CHARSET utf8mb4;
然后我像这样加载数据:
LOAD DATA INFILE '/tmp/JP.txt' INTO TABLE geonames CHARACTER SET utf8mb4;
并像这样选择它:
SELECT alternatenames FROM geonames WHERE geonameid=1850147G
我明白这个:
*************************** 1. row ***************************
alternatenames: Edo,TYO,Tochiu,Tocio,Tokija,Tokijas,Tokio,Tokió,Tokjo,Tokyo,Toquio,Toquio - dong jing,Toquio - 東京,Tòquio,Tókýó,Tóquio,Tōkyō,dokyo,dong jing,dong jing dou,tokeiyw,tokkiyo,tokyo,twkyw,twqyw,Τόκιο,Токио,Токё,Токіо,Տոկիո,טוקיו,توكيو,توکیو,طوكيو,ܛܘܟܝܘ,ܜܘܟܝܘ,टोक्यो,டோக்கியோ,โตเกียว,ტოკიო,东京,東京,東京都,도쿄
我也可以像这样搜索:
SELECT name FROM geonames WHERE alternatenames LIKE '%,東京,%';
说来话长:请注意创建表时的字符集声明。我相信这是您在创建数据库时未能做到的。
推荐阅读:
https://www.joelonsoftware.com/articles/Unicode.html
http://kunststube.net/encoding/
就MySQL而言,至关重要的是MySQL连接的字符集。这是MySQL服务器认为客户端在其通信中使用的字符集。
SHOW VARIABLES LIKE '%characterset%'
例如,如果设置不正确,则客户端正在发送latin1(ISO-8859-1),但MySQL服务器认为它正在接收UTF8,反之亦然,则存在mojibake的潜力。
同样重要的是alternatenames
列的字符集。
处理多字节字符集的一个问题是PHPsprintf
函数。PHP中的许多字符串处理函数都有"多字节"等价物,可以正确处理包含多字节字符的字符串。
https://secure.php.net/manual/en/book.mbstring.php
不幸的是,没有内置的mb_sprintf
函数。
有关 PHP 中字符串处理(包括多字节字符/字符集)的更详细说明:
https://secure.php.net/manual/en/language.types.string.php#language.types.string.details
摘录:
最终,这意味着使用 Unicode 编写正确的程序取决于小心避免那些不起作用并且很可能会损坏数据的函数,而是使用行为正确的函数,通常来自 intl 和 mbstring 扩展。但是,使用可以处理 Unicode 编码的函数只是一个开始。无论语言提供什么功能,了解 Unicode 规范都是必不可少的。
此外,谷歌搜索"utf8 一路走来"可能会返回一些有用的注释。但请注意,这个口头禅不是解决问题的灵丹妙药或灵丹妙药。
MySQL参考手册中指出的另一个可能的问题:
https://dev.mysql.com/doc/refman/5.7/en/fulltext-restrictions.html
13.9.5 全文限制
表意语言(如中文和日语)没有单词分隔符。因此,内置的全文分析器无法确定单词在这些语言和其他此类语言中的开始和结束位置。
在MySQL 5.7.6中,提供了支持中文,日语和韩语(CJK)的基于字符的ngram全文解析器,以及支持日语的基于单词的MeCab解析器插件,用于InnoDB和MySIAM表。