T9搜索联系人

 最近在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

[java]  view plain  copy
  1. public class T9PinyinUnit {  
  2.     private String mPinyin; //保存拼音串(当然也可以任意字符串)  
  3.     private String mNumber; //保存mPinyin对应的T9数字  
  4.         ......  
  5. }  

例如:

"hao" => mPinyin="hao";mNumber="426";

"???hao" => mPinyin="???hao";mNumber="???426";

(2)保存单个汉字拼音(可能多音字)的基本数据结构

[java]  view plain  copy
  1. public class PinyinUnit {  
  2.     private boolean mPinyin;    //判断是否是拼音(可能是汉字转换成的拼音,也可能是非汉字转换的字符串)  
  3.     private int mStartPosition; //记录此汉字或字符串在原来数据的位置  
  4.     private List<T9PinyinUnit> mT9PinyinUnitIndex;//用链表保存汉字拼音,多音字则长度大于1,单音字或字符串长度为1  
  5.     ......  
  6. }  

例如:一串可能含汉字的字符串转换保存到若干PinyinUnit类型变量中.

[java]  view plain  copy
  1. //"Hi你说"    
  2. {//Hi-Hi  
  3.     mPinyin=false;  
  4.     mT9PinyinUnitIndex.size=1;  
  5.     mStartPosition=0;  
  6.     {  
  7.         mT9PinyinUnitIndex.get(0).setPinyin("Hi");  
  8.         mT9PinyinUnitIndex.get(0).setNumber("44");  
  9.     }  
  10. }     
  11. {//你->ni3  
  12.     mPinyin=true;  
  13.     mT9PinyinUnitIndex.size=1;  
  14.     mStartPosition=2;  
  15.     {  
  16.         mT9PinyinUnitIndex.get(0).setPinyin()="ni";  
  17.         mT9PinyinUnitIndex.get(0).setNumber="64";  
  18.     }  
  19. }  
  20. {//说->shuo1,shui4,yue4  
  21.     mPinyin=true;  
  22.     mT9PinyinUnitIndex.size=3;  
  23.     mStartPosition=3;  
  24.     {  
  25.         {  
  26.             mT9PinyinUnitIndex.get(0).setPinyin()="shuo";  
  27.             mT9PinyinUnitIndex.get(0).setNumber="7486";  
  28.         };  
  29.         {  
  30.             mT9PinyinUnitIndex.get(1).setPinyin()="shui";  
  31.             mT9PinyinUnitIndex.get(1).setNumber="7484";  
  32.         };  
  33.         {  
  34.             mT9PinyinUnitIndex.get(2).setPinyin()="yue";  
  35.             mT9PinyinUnitIndex.get(2).setNumber="983";  
  36.         }  
  37. }  

    从上述转换过程中可以看出,一串可能含汉字的字符串(如姓名)会解析保存到一个PinyinUnit类型链表中,每个PinyinUnit类型变量保存着一个汉字的拼音或者一串非汉字字符串,这就是我们想要的基本数据解析结果。

  注意汉字转换为拼音可以用Javapinyin4j. 这是相关链接:http://pinyin4j.sourceforge.net/

2.相关基本函数

(1)字符串(可能含汉字)解析函数 

[java]  view plain  copy
  1. /** 
  2.  * @description 将一串字符串(可能含中文)解析保存到一个元素类型为PinyinUnit的链表中. 
  3.  * 调用此函数时,只需传入chineseString,并传入已初始化的元素类型为PinyinUnit的链表索引, 
  4.  * 其解析结果将会保存传入的元素类型为PinyinUnit的链表中. 
  5.  * @param chineseString 需要转换的字符串 
  6.  * @param pinyinUnit 元素类型为PinyinUnit的链表索引. 
  7.  */  
  8. public static void chineseStringToPinyinUnit(String chineseString,List<PinyinUnit> pinyinUnit);  

详细解析格式如图1:


图1.T9数据解析格式

(2)T9输入字符串匹配函数 

[java]  view plain  copy
  1.    /** 
  2.  * @description 将T9输入字符串与保存解析后的结果(元素类型为PinyinUnit的链表)进行匹配 
  3.  * @param pinyinUnits       元素类型为PinyinUnit的链表的索引 
  4.  * @param baseData          解析成元素类型为PinyinUnit的链表之前的原始数据 
  5.  * @param search            T9搜索关键字('0'~'9','*','#') 
  6.  * @param chineseKeyWord    保存与baseData匹配的关键字 
  7.  * @return 如果匹配返回true,否则返回false. 
  8.  */  
  9. public static boolean matchPinyinUnits(final List<PinyinUnit> pinyinUnits,  
  10.         final String baseData, String search,StringBuffer chineseKeyWord);  
    关键算法解析

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英文字符与数字的对应关系如下:

[plain]  view plain  copy
  1. 1          2(ABC)    3(DEF)   
  2. 4(GHI)     5(JKL)    6(MNO)   
  3. 7(PQRS)    8(TUV)    9(WXYZ)   
  4. *          0         #  

    匹配算法:字符串(可能含有汉字)解析为元素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

CSDN:http://download.csdn.net/detail/zjqyjg/8171851

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值