android T9 搜索联系人分析与实现(支持多音字)

来源:互联网 发布:网络营销策划岗位职责 编辑:程序博客网 时间:2024/04/25 05:30

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

public class T9PinyinUnit {private String mPinyin;//保存拼音串(当然也可以任意字符串)private String mNumber; //保存mPinyin对应的T9数字        ......}

例如:

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

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

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

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

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

//"Hi你说"  {//Hi-HimPinyin=false;mT9PinyinUnitIndex.size=1;mStartPosition=0;{mT9PinyinUnitIndex.get(0).setPinyin("Hi");mT9PinyinUnitIndex.get(0).setNumber("44");}}{//你->ni3mPinyin=true;mT9PinyinUnitIndex.size=1;mStartPosition=2;{mT9PinyinUnitIndex.get(0).setPinyin()="ni";mT9PinyinUnitIndex.get(0).setNumber="64";}}{//说->shuo1,shui4,yue4mPinyin=true;mT9PinyinUnitIndex.size=3;mStartPosition=3;{{mT9PinyinUnitIndex.get(0).setPinyin()="shuo";mT9PinyinUnitIndex.get(0).setNumber="7486";};{mT9PinyinUnitIndex.get(1).setPinyin()="shui";mT9PinyinUnitIndex.get(1).setNumber="7484";};{mT9PinyinUnitIndex.get(2).setPinyin()="yue";mT9PinyinUnitIndex.get(2).setNumber="983";}}

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

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

2.相关基本函数

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

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

详细解析格式如图1:


图1.T9数据解析格式

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

        /**     * @description 将T9输入字符串与保存解析后的结果(元素类型为PinyinUnit的链表)进行匹配     * @param pinyinUnits元素类型为PinyinUnit的链表的索引     * @param baseData   解析成元素类型为PinyinUnit的链表之前的原始数据     * @param searchT9搜索关键字('0'~'9','*','#')     * @param chineseKeyWord保存与baseData匹配的关键字     * @return 如果匹配返回true,否则返回false.     */    public static boolean matchPinyinUnits(final List<PinyinUnit> pinyinUnits,    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英文字符与数字的对应关系如下:

1          2(ABC)    3(DEF) 4(GHI)     5(JKL)    6(MNO) 7(PQRS)    8(TUV)    9(WXYZ) *          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搜索联系人Demo:一个演示android T9搜索联系人的项目

代码链接:Github:https://github.com/handsomezhou/T9Search

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

T9搜索库&T9搜索联系人Demo(支持多音字):将T9搜索数据解析和匹配接口制作成库,并演示android T9搜索联系人项目使用此库.

代码链接: Github:https://github.com/handsomezhou/T9SearchLibrary

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





1 0
原创粉丝点击