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 相关文章推荐
分析Netty直接内存原理及应用
Jun 14 Java/Android
解决tk mapper 通用mapper的bug问题
Jun 16 Java/Android
springboot利用redis、Redisson处理并发问题的操作
Jun 18 Java/Android
java基础——多线程
Jul 03 Java/Android
使用logback实现按自己的需求打印日志到自定义的文件里
Aug 30 Java/Android
剑指Offer之Java算法习题精讲二叉树专项训练
Mar 21 Java/Android
Java 超详细讲解设计模式之中的抽象工厂模式
Mar 25 Java/Android
springboot入门 之profile设置方式
Apr 04 Java/Android
Java工作中实用的代码优化技巧分享
Apr 21 Java/Android
SpringBoot 集成短信和邮件 以阿里云短信服务为例
Apr 22 Java/Android
java.util.NoSuchElementException原因及两种解决方法
Jun 28 Java/Android
MyBatis XPathParser解析器使用范例详解
Jul 15 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
详解:――如何将图片储存在数据库里
2006/12/05 PHP
PHP中使用CURL伪造来路抓取页面或文件
2011/05/04 PHP
深入php之规范编程命名小结
2013/05/15 PHP
php好代码风格的阶段性总结
2016/06/25 PHP
php微信公众平台开发(一) 配置接口
2016/12/06 PHP
php实现留言板功能(会话控制)
2017/05/23 PHP
PHP使用Redis长连接的方法详解
2018/02/12 PHP
Laravel利用gulp如何构建前端资源详解
2018/06/03 PHP
PHP yield关键字功能与用法分析
2019/01/03 PHP
PHP array_shift()用法实例分析
2019/01/07 PHP
List all the Databases on a SQL Server
2007/06/21 Javascript
JavaScript让IE浏览器event对象符合W3C DOM标准
2009/11/24 Javascript
Jquery cookie操作代码
2010/03/14 Javascript
解析javascript瀑布流原理实现图片滚动加载
2016/03/10 Javascript
不能不知道的10个angularjs英文学习网站
2016/03/23 Javascript
AngularJS 简单应用实例
2016/07/28 Javascript
教你JS中的运算符乘方、开方及变量格式转换
2016/08/09 Javascript
Bootstrap中的fileinput 多图片上传及编辑功能
2016/09/05 Javascript
vue $set 给数据赋值的实例
2019/11/09 Javascript
vue仿淘宝滑动验证码功能(样式模仿)
2019/12/10 Javascript
使用typescript改造koa开发框架的实现
2020/02/04 Javascript
javascript实现页面的实时时钟显示示例
2020/08/06 Javascript
pygame学习笔记(3):运动速率、时间、事件、文字
2015/04/15 Python
python 爬虫 批量获取代理ip的实例代码
2018/05/22 Python
关于阿里云oss获取sts凭证 app直传 python的实例
2019/08/20 Python
HTML5 canvas基本绘图之填充样式实现
2016/06/27 HTML / CSS
Shopbop中文官网:美国亚马逊旗下时尚购物网站
2020/12/15 全球购物
思想品德自我评价
2014/02/04 职场文书
项目施工员岗位职责
2014/03/09 职场文书
倡议书格式范文
2014/04/14 职场文书
社区先进事迹材料
2014/05/19 职场文书
2015年九一八事变纪念日演讲稿
2015/03/19 职场文书
2015年幼儿园德育工作总结
2015/05/25 职场文书
社会实践心得体会范文
2016/01/14 职场文书
JavaScript数组reduce()方法的语法与实例解析
2021/07/07 Javascript
Go语言编译原理之变量捕获
2022/08/05 Golang