android 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

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-Hi
			mPinyin=false;
			mT9PinyinUnitIndex.size=1;
			mStartPosition=0;
			{
				mT9PinyinUnitIndex.get(0).setPinyin("Hi");
				mT9PinyinUnitIndex.get(0).setNumber("44");
			}
		}	
		{//你->ni3
			mPinyin=true;
			mT9PinyinUnitIndex.size=1;
			mStartPosition=2;
			{
				mT9PinyinUnitIndex.get(0).setPinyin()="ni";
				mT9PinyinUnitIndex.get(0).setNumber="64";
			}
		}
		{//说->shuo1,shui4,yue4
			mPinyin=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. 这是相关链接:pinyin4j, a Java library converting Chinese to pinyin

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 search			T9搜索关键字('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搜索库&T9搜索联系人Demo(支持多音字):将T9搜索数据解析和匹配接口制作成库,并演示android T9搜索联系人项目使用此库.

代码链接: Github:GitHub - handsomezhou/T9SearchLibrary: Provide data analysis methods, data matching method and so on for T9 search.

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值