Java实现简易的分词器功能


Posted in Java/Android onJune 15, 2021

业务需求:

生活中常见的搜索功能大概可分为以下几类:

  • 单关键词。如“Notebook”
  • 双关键词加空格。如“Super Notebook”
  • 多关键词加多空格。如“Intel Super Notebook”

当然,还有四甚至五关键词,这些搜索场景在生活中可以用罕见来形容,不在我们的讨论范围。我们今天就以上三种生活中最常见的搜索形式进行探讨分析。业务需求也很简单,假设我们要完成一个搜索功能,业务层、持久层、控制层不在我们讨论的范围,仅讨论分词功能如何实现。

分析:

假设用户键入的搜索内容为以下内容:

Intel Super Notebook

我们可以利用Java中String强大而丰富的方法来慢慢拼凑一个小算法来达到目的。String中大多数方法的参数和返回值都与下标相关,那么,分析上述语句的下标,我们可发现如下内容:

Java实现简易的分词器功能

上述内容红色是我们分词的关键内容。对于一个语句而言(不是语言学上通俗的语句,因为该句没有主谓宾),重要的就是各单词或词组的首字母下标与该单词或词组后面最近一个空格。我们发现,Intel这个单词首字母下标为0,距离该单词后面最近的一个空格下标为5;Super首字母下标为距离该单词前面最近的一个空格的下标加1,也就是6;Notebook首字母下标为距离该单词前面最近的一个空格的下标加1,也就是12;最后就是该语句的尾下标,也就是19。

当然,实际情况会有用户多输入了两个甚至三个空格在某两个单词之间,例如如下形式:

Intel  Super  Notebook

(注意这里的空格为每个单词之间为2个)

这个问题很容易解决,我们把两个或三个空格替换为一个空格即可(为什么不是四个或者更多?因为现实情况是用户不太可能在各个单词之间连按多个空格),如下:

sentence = sentence.replace("  ", " ");
sentence = sentence.replace("   ", " ");

这样以来语句中就只存在单个空格了。

经过分析我们得知,若想对一个语句进行分词,就必须知道各个单词的起始下标才行。起始下标可以由空格的下标得知,那我们该如何得知空格的下标?

很简单,我们写个方法,通过迭代语句的每个单词,判断其是否存在空格即可。方法如下:

private int firstPosition(){
	int first = 0;
	for(int i = 0; i < sentence.length(); i++){
		if(String.valueOf(sentence.charAt(i)).equals(" ")){
			first = i;
			return first;
		}	
	}
	return first;
}

这个方法的作用是判断一个语句中第一个空格的位置。既然有第一个了,肯定要有第二个了。要注意第一个内容是从0开始进行迭代,而第二个空格的判断方法要从第一个空格的位置加1开始,否则迭代的刚好还是第一个空格的位置。内容如下:

private int secondPosition(){
		int second = 0;
		for(int i = (firstPosition() + 1); i < sentence.length(); i++){
			if(String.valueOf(sentence.charAt(i)).equals(" ")){
				second = i;
				return second;
			}	
		}
		return second;
	}

第三个为什么不迭代?因为第三个单词之后就没有空格了,就到结尾了。

找出每个空格的下标索引后,我们还需知道语句中含有多少个空格,是没有,还是1个或2个(连续的重复空格在上文已经被替换为单个空格了)。方法如下:

private int countBlank(String s){
	// Store single blank signal.
	int amount = 0;

	// If s contains single blank signal, and it will increse amount's value of 1 every loop times.
	for(int i = 0; i < s.length(); i++){
		if(String.valueOf(sentence.charAt(i)).equals(" ")){
			amount++;
		}
	}
	return amount;
}

拿到了空格的总个数及每个空格的下标,我们就可以写个方法进行分割了。由于我是采用了泛型集合作为数据源,这里的方法返回类型就为void。

我们先假设输入的仅有以下内容:

Intel

输入的仅有一个词组。我们先判断其空格的个数,发现为0,那么也不用进行什么操作了,直接添加其作为集合的数据。

public void divide(){
		// Record every single blank signal's position.
		int position1 = firstPosition();
		int position2 = secondPosition();
		
		if(sentence.contains(" ")){

		} else{
        	words.add(sentence);
     	}
		
	}

现在情况变为输入的内容如下:

Intel Super

我们知道了这个语句共有一个空格,下标为5,长度为11,那可以这样判断:是否包含空格,如果是,那就判断其空格数是否大于等于0,如果为真,就添加到数据源。接着判断其空格数是否大于等于1,如果真,进入下一层判断其空格数是否大于等于1其小于2,如果真,就添加到数据源。内容如下:

public void divide(){
		// Record every single blank signal's position.
		int position1 = firstPosition();
		int position2 = secondPosition();
		
		if(sentence.contains(" ")){
			int blankAmount = countBlank(sentence);
			if (blankAmount >= 0) {
				words.add(sentence.substring(0, position1));
				if (blankAmount >= 1) {
					if(blankAmount >= 1 && blankAmount < 2)));
						words.add(sentence.substring(position1, sentence.length()));
					} else {

					}
	
				} 
				
			}
		} else{
        	words.add(sentence);
     	}
	}

下面就是较为全面的情况了:

Intel Super Notebook

我们判断完两个情况就看第三个情况。第三个单词其获取是通过第二个空格下标与语句长度得来。但第二个单词就要改为第一个空格下标加1与第二个空格下标加1了。那么至此分割方法也就完成了:

public String divide(){
	// Record every single blank signal's position.
	int position1 = firstPosition();
	int position2 = secondPosition();
	
	if(sentence.contains(" ")){
		int blankAmount = countBlank(sentence);
		if (blankAmount >= 0) {
			words.add(sentence.substring(0, position1));
			if (blankAmount >= 1) {
				if(blankAmount >= 1 && blankAmount < 2){
					words.add(sentence.substring(position1, sentence.length()));
				} else {
					words.add(sentence.substring(position1, position2));
					if (blankAmount >= 2) {
						words.add(sentence.substring(position2, sentence.length()));
					}
				}

			} 
			
		}
	} else{
        	words.add(sentence);
     	}
	
}

测试:

Intel Super Notebook

SIZE:3
POSITION(0): Intel
POSITION(1): Super
POSITION(2): Notebook

Intel   Super   Notebook

(注这里有重复且连续的空格)
SIZE:3
POSITION(0): Intel
POSITION(1): Super
POSITION(2): Notebook

英特尔 超级  笔记本

SIZE:3
POSITION(0): 英特尔
POSITION(1): 超级
POSITION(2): 笔记本

华为

SIZE:1
POSITION(0): 华为

完整代码:

class DivideWord{
	
	private String sentence;
	private List<String> words = new ArrayList<String>();

	public DivideWord(String sentence) {
		// Replace two or three blank signal that connected into single blank signal.
		sentence = sentence.replace("  ", " ");
		sentence = sentence.replace("   ", " ");
		this.sentence = sentence;
	}
	
	private int countBlank(String s){
		// Store single blank signal.
		int amount = 0;

		// If s contains single blank signal, and it will increse amount's value of 1 every loop times.
		for(int i = 0; i < s.length(); i++){
			if(String.valueOf(sentence.charAt(i)).equals(" ")){
				amount++;
			}
		}
		return amount;
	}
	
	private int firstPosition(){
		int first = 0;
		for(int i = 0; i < sentence.length(); i++){
			if(String.valueOf(sentence.charAt(i)).equals(" ")){
				first = i;
				return first;
			}	
		}
		return first;
	}
	
	private int secondPosition(){
		int second = 0;
		for(int i = (firstPosition() + 1); i < sentence.length(); i++){
			if(String.valueOf(sentence.charAt(i)).equals(" ")){
				second = i;
				return second;
			}	
		}
		return second;
	}
	
	public String divide(){
		// Record every single blank signal's position.
		int position1 = firstPosition();
		int position2 = secondPosition();
		
		if(sentence.contains(" ")){
			int blankAmount = countBlank(sentence);
			if (blankAmount >= 0) {
				words.add(sentence.substring(0, position1));
				if (blankAmount >= 1) {
					if(blankAmount >= 1 && blankAmount < 2){
						words.add(sentence.substring(position1, sentence.length()));
					} else {
						words.add(sentence.substring(position1, position2));
						if (blankAmount >= 2) {
							words.add(sentence.substring(position2, sentence.length()));
						}
					}
	
				} 
				
			}
		} else{
        	words.add(sentence);
     	}
	}
	
	public int getSize(){
		return words.size();
	}
	
	public String getWord(int position){
		return words.get(position);
	}
}

public class DateGet {
	public static void main(String[] args){
		DivideWord divideWord = new DivideWord("英特尔");
		divideWord.divide();
		System.out.println("SIZE:" + divideWord.getSize());
		System.out.println("POSITION :" + divideWord.getWord(0));
		
	}
}

到此这篇关于Java实现简易的分词器功能的文章就介绍到这了,更多相关Java分词器功能内容请搜索三水点靠木以前的文章或继续浏览下面的相关文章希望大家以后多多支持三水点靠木!

Java/Android 相关文章推荐
Java并发编程之Executor接口的使用
Jun 21 Java/Android
图解排序算法之希尔排序Java实现
Jun 26 Java/Android
Java比较两个对象中全部属性值是否相等的方法
Aug 07 Java/Android
java中用float时,数字后面加f,这样是为什么你知道吗
Sep 04 Java/Android
springboot如何接收application/x-www-form-urlencoded类型的请求
Nov 02 Java/Android
SpringBoot+Redis实现布隆过滤器的示例代码
Mar 17 Java/Android
springboot用户数据修改的详细实现
Apr 06 Java/Android
SpringBoot中获取profile的方法详解
Apr 08 Java/Android
Android 界面一键变灰 深色主题工具类
Apr 28 Java/Android
详解Android中的TimePickerView(时间选择器)的用法
Apr 30 Java/Android
详解Flutter自定义应用程序内键盘的实现方法
Jun 14 Java/Android
Java使用HttpClient实现文件下载
Aug 14 Java/Android
Java用自带的Image IO给图片添加水印
java Nio使用NioSocket客户端与服务端交互实现方式
JVM入门之类加载与字节码技术(类加载与类的加载器)
Spring Cache和EhCache实现缓存管理方式
浅析NIO系列之TCP
JPA如何使用entityManager执行SQL并指定返回类型
Jun 15 #Java/Android
Java Shutdown Hook场景使用及源码分析
You might like
php中cookie的作用域
2008/03/27 PHP
PHP中time(),date(),mktime()区别介绍
2013/09/28 PHP
thinkphp模板的包含与渲染实例分析
2014/11/26 PHP
如何使用纯PHP实现定时器任务(Timer)
2015/07/31 PHP
PHP实现的数组和XML文件相互转换功能示例
2018/03/15 PHP
php命名空间设计思想、用法与缺点分析
2019/07/17 PHP
动态的改变IFrame的高度实现IFrame自动伸展适应高度
2012/12/28 Javascript
使用jQuery实现的网页版的个人简历(可换肤)
2013/04/19 Javascript
深入Javascript函数、递归与闭包(执行环境、变量对象与作用域链)使用详解
2013/05/08 Javascript
CSS+jQuery实现的一个放大缩小动画效果
2013/09/24 Javascript
从js向Action传中文参数出现乱码问题的解决方法
2013/12/29 Javascript
jquery选择器之基本过滤选择器详解
2014/01/27 Javascript
JavaScript html5 canvas绘制时钟效果
2016/03/01 Javascript
jQuery插件HighCharts绘制2D柱状图、折线图的组合双轴图效果示例【附demo源码下载】
2017/03/09 Javascript
vue.js的安装方法
2017/05/12 Javascript
详解webpack4之splitchunksPlugin代码包分拆
2018/12/04 Javascript
关于AngularJS中几种Providers的区别总结
2020/05/17 Javascript
详解vue中v-on事件监听指令的基本用法
2020/07/22 Javascript
[10:04]国际邀请赛采访专栏:DK.Farseer,mouz.Black^,采访员Josh专访
2013/08/05 DOTA
pymongo为mongodb数据库添加索引的方法
2015/05/11 Python
Python使用xlrd模块操作Excel数据导入的方法
2015/05/26 Python
简单掌握Python的Collections模块中counter结构的用法
2016/07/07 Python
Python文件操作基本流程代码实例
2017/12/11 Python
Python使用Turtle库绘制一棵西兰花
2019/11/23 Python
Python3.7.0 Shell添加清屏快捷键的实现示例
2020/03/23 Python
HTML5 Canvas API中drawImage()方法的使用实例
2016/03/25 HTML / CSS
HTML5表单验证特性(知识点小结)
2020/03/10 HTML / CSS
Hotels.com南非:酒店预订
2017/11/02 全球购物
一份全面的PHP面试问题考卷
2012/07/15 面试题
体育教师求职信
2014/05/24 职场文书
新品发布会策划方案
2014/06/08 职场文书
中央空调节能方案
2014/06/15 职场文书
诚信考试标语
2014/06/24 职场文书
农村文化活动总结
2014/08/28 职场文书
导盲犬小Q观后感
2015/06/11 职场文书
浅谈Redis 中的过期删除策略和内存淘汰机制
2022/04/03 Redis