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 相关文章推荐
spring项目中切面及AOP的使用方法
Jun 26 Java/Android
新手初学Java网络编程
Jul 07 Java/Android
java设计模式--原型模式详解
Jul 21 Java/Android
Java 在线考试云平台的实现
Nov 23 Java/Android
SpringDataJPA在Entity中常用的注解介绍
Dec 06 Java/Android
Java实现学生管理系统(IO版)
Feb 24 Java/Android
RestTemplate如何通过HTTP Basic Auth认证示例说明
Mar 17 Java/Android
Java实战之课程信息管理系统的实现
Apr 01 Java/Android
SpringBoot 集成短信和邮件 以阿里云短信服务为例
Apr 22 Java/Android
Android开发EditText禁止输入监听及InputFilter字符过滤
Jun 10 Java/Android
springboot读取resources下文件的方式详解
Jun 21 Java/Android
Java 多线程并发FutureTask
Jun 28 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中的字符串函数
2006/11/24 PHP
PHP中__FILE__、dirname与basename用法实例分析
2014/12/01 PHP
浅谈php冒泡排序
2014/12/30 PHP
PHP限制HTML内容中图片必须是本站的方法
2015/06/16 PHP
区分JS中的undefined,null,&quot;&quot;,0和false
2007/03/08 Javascript
基于jQuery的为attr添加id title等效果的实现代码
2011/04/20 Javascript
jQuery使用ajaxSubmit()提交表单示例
2014/04/04 Javascript
深入探寻javascript定时器
2015/01/02 Javascript
fastclick插件导致日期(input[type=&quot;date&quot;])控件无法被触发该如何解决
2015/11/09 Javascript
易操作的jQuery表单提示插件
2015/12/01 Javascript
jQuery隐藏和显示效果实现
2016/04/06 Javascript
基于JS实现横线提示输入验证码随验证码输入消失(js验证码的实现)
2016/10/27 Javascript
微信小程序 wx.request(接口调用方式)详解及实例
2016/11/23 Javascript
JavaScript中捕获与冒泡详解及实例
2017/02/03 Javascript
js移动端事件基础及常用事件库详解
2017/08/15 Javascript
vue自定义全局共用函数详解
2018/09/18 Javascript
create-react-app使用antd按需加载的样式无效问题的解决
2019/02/26 Javascript
在layui框架中select下拉框监听更改事件的例子
2019/09/20 Javascript
springboot+vue实现文件上传下载
2020/11/17 Vue.js
如何在VUE中使用vue-awesome-swiper
2021/01/04 Vue.js
利用Python实现Windows下的鼠标键盘模拟的实例代码
2017/07/13 Python
详解python中@的用法
2019/03/27 Python
简单了解python的内存管理机制
2019/07/08 Python
浅谈Python3识别判断图片主要颜色并和颜色库进行对比的方法
2019/10/25 Python
Pytorch 定义MyDatasets实现多通道分别输入不同数据方式
2020/01/15 Python
django在保存图像的同时压缩图像示例代码详解
2020/02/11 Python
教你如何用python操作摄像头以及对视频流的处理
2020/10/12 Python
python中slice参数过长的处理方法及实例
2020/12/15 Python
欧铁通票官方在线销售网站:Eurail.com
2017/10/14 全球购物
PatPat香港:婴童服饰和亲子全家装在线购物
2020/09/27 全球购物
见习期自我鉴定
2014/01/31 职场文书
物资采购方案
2014/06/12 职场文书
生物科学专业自荐书
2014/06/20 职场文书
2014医学院领导班子对照检查材料思想汇报
2014/09/19 职场文书
幼儿园小班家长评语
2014/12/30 职场文书
使用CSS实现百叶窗效果示例代码
2023/05/07 HTML / CSS