最近在Android项目开发过程中需要实现类似电话拨号功能,这里涉及到T9键盘搜索联系人,于是研究了一番,将思路和心得记录于此,方便自己与他人。
相信大家对T9输入法并不陌生,这里并不涉及到T9输入法,这里只涉及到T9搜索.以T9键盘搜索联系人为例,分析与实现T9搜索。
一开始我的想法是,根据T9键盘的输入然后组合起来,根据所有组合来进行搜索,但仔细一想,很显然行不通,这样组合起来的结果是随着输入字符的增加成指数级增长的.后来一想,T9键盘输入是'0'~'9','*',‘#’这12个字符的组合,为了更具通用性,应该将输入与搜索分开,搜索模块应该根据最原始的T9输入数据来匹配有限基本数据(这里是所有联系人),然后返回搜索结果.所以形成如下思路:
T9搜索联系人思路:
1.加载联系人(获取姓名与电话号码)
2.解析联系人(将可能含有汉字的字符串(姓名)解析成可直接根据输入可匹配的格式)
3.获得T9输入(获得‘0’~‘9’,‘*’,'#'组成的字符串)
4.根据T9输入数据匹配联系人 (根据获得‘0’~‘9’,‘*’,'#'组成的字符串,匹配解析后的联系人数据)
5.显示结果 (显示匹配结果)
T9搜索联系人基本上就是上述流程.1,3,5点实现起来问题不大,关键是2,4点,我们将重点主要放在2,4点上。
代码分析与实现
1.基本数据结构
(1)保存单个拼音的基本数据结构:T9PinyinUnit
例如:
"hao" => mPinyin="hao";mNumber="426";
"???hao" => mPinyin="???hao";mNumber="???426";
(2)保存单个汉字拼音(可能多音字)的基本数据结构
例如:一串可能含汉字的字符串转换保存到若干PinyinUnit类型变量中.
从上述转换过程中可以看出,一串可能含汉字的字符串(如姓名)会解析保存到一个PinyinUnit类型链表中,每个PinyinUnit类型变量保存着一个汉字的拼音或者一串非汉字字符串,这就是我们想要的基本数据解析结果。
注意汉字转换为拼音可以用Java库pinyin4j. 这是相关链接:http://pinyin4j.sourceforge.net/
2.相关基本函数
(1)字符串(可能含汉字)解析函数
详细解析格式如图1:
图1.T9数据解析格式
(2)T9输入字符串匹配函数
关键算法解析
1.T9匹配算法
匹配规则:汉字主要以声母,汉字全拼,及以声母位置开始的汉字全拼的子串来匹配;非汉字(可能是一串字符)就以其子串来匹配。
匹配情形:
(1).汉字A:(汉字A从声母位置开始的子串可匹配汉字A)
例如汉字:"汉"=>'han';则可匹配‘汉’的关键字有:"h","ha","han".
(2).非汉字B:(非汉字B的子串可匹配非汉字B)
例如非汉字:"how"; 则可匹配”how“的关键字有:"h","o","w","ho","ow","how".
(3).汉字C+汉字D: (汉字C声母或汉字C全拼)+汉字D从声母开始的子串可匹配
例如汉字"汉"+汉字"文":"hanwen";则可匹配"hanwen"的关键字有:"hw","hwe","hwen","hanw","hanwe","hanwen"
(4).汉字E+非汉字F: (汉字E的声母或汉字E的全拼)+非汉字F从第一字符开始的子串可匹配
例如汉字"汉"+非汉字"how":"hanhow";则可匹配"hanhow"的关键字有:"hh","hho","hhow","hanh","hanho","hanhow".
(5).非汉字G+汉字H: (非汉字G从任意位置到非汉字G最后一个字符的子串)+(汉字H从声母开始的子串)可匹配
例如非汉字"how"+汉字"汉":"howhan";则可匹配"howhan"的关键字有:"wh","wha","whan","owh","owha","owhan","howh","howha","howhan".
T9输入是{'0'~'9','*','#'}的组合,非汉字字符串可以转换为{'0'~'9','*','#'+其他字符};汉字则可先转换为拼音然后再转换为{'2'~'9'}的组合.T9英文字符与数字的对应关系如下:
匹配算法:字符串(可能含有汉字)解析为元素PinyinUnit的链表保存的数据后,就可以直接用关键字与其进行匹配.以字符串"说了么Git???"为例,解析数据保存在元素为PinyinUnit的链表pinyinUnits中.如图2.
图2 T9数据解析简要格式
最初的想法是将这些结果进行排列组合,然后再来匹配。这里的组合结果有3*2*3*1种,但组合起来并不是我们想要的结果,因为我们不仅仅是匹配组合串中的子串,我们不能忽略汉字中声母可匹配的规则,所以这不是一种好办法。我们决定采取搜索关键字与PinyinUnit变量单个进行比较的规则来匹配.
算法思路如下:
从pinyinUnits的第一个PinyinUnit元素开始,将搜索关键字与此PinyinUnit的第一个T9PinyinUnit元素进行匹配;
(1):如果此PinyinUnit元素的T9PinyinUnit元素是搜索关键字起始位置开始的子串,说明搜索关键字部分匹配成功,则去掉已匹配的关键字,将余下的关键字作为搜索关键字,继续从pinyinUnits的下一个PinyinUnit元素的第一个T9PinyinUnit元素进行匹配,如果匹配成功,则进入步骤(1),如果匹配不成功则进入步骤(2);
(2):如果此PinyinUnit元素的T9PinyinUnit元素不是搜索关键字起始位置开始的子串,则说明不匹配,则从当前Pinyin元素的下一个T9PinyinUnit元素进行匹配,如果匹配成功,则进入步骤(1),如果匹配不成功则进入步骤(2);
截止条件:成功匹配搜索关键字返回true;搜索完所有情况,匹配不成功返回false;
(A)这种深度搜索的方法只匹配搜索了以某个 PinyinUnit开始的情况,如果匹配成功,则直接返回;
(B)若没有与搜索关键字匹配的结果,则需要将pinyinUnits中下一个PinyinUnit元素开始的链表按照上述方式匹配搜索一次,再根据其匹配搜索结果,判断进入步骤(A)还是(B).
(截止条件:成功匹配或pinyinUnits中以最后一个PinyinUnit元素为开始元素的链表也遍历完.)
相关代码链接
T9搜索库&T9搜索联系人Demo(支持多音字):将T9搜索数据解析和匹配接口制作成库,并演示android T9搜索联系人项目使用此库.
代码链接: Github:https://github.com/handsomezhou/T9SearchLibrary