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 相关文章推荐
启动Tomcat时出现大量乱码的解决方法
Jun 21 Java/Android
springBoot基于webSocket实现扫码登录
Jun 22 Java/Android
深入理解java.lang.String类的不可变性
Jun 27 Java/Android
Java集成swagger文档组件
Jun 28 Java/Android
logback 实现给变量指定默认值
Aug 30 Java/Android
Java 在生活中的 10 大应用
Nov 02 Java/Android
Mybatis-Plus进阶分页与乐观锁插件及通用枚举和多数据源详解
Mar 21 Java/Android
Spring Boot DevTools 全局配置学习指南
Mar 31 Java/Android
java高级用法JNA强大的Memory和Pointer
Apr 19 Java/Android
Android自定义双向滑动控件
Apr 19 Java/Android
Android Studio 计算器开发
May 20 Java/Android
Java 中的 Lambda List 转 Map 的多种方法详解
Jul 07 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
MySQL GBK→UTF-8编码转换
2007/05/24 PHP
IIS下PHP的三种配置方式对比
2014/11/20 PHP
Jquey拖拽控件Draggable使用方法(asp.net环境)
2010/09/28 Javascript
屏蔽IE弹出&quot;您查看的网页正在试图关闭窗口,是否关闭此窗口&quot;的方法
2013/12/31 Javascript
jQuery操作表单常用控件方法小结
2015/03/23 Javascript
使用堆实现Top K算法(JS实现)
2015/12/25 Javascript
JavaScript中的this机制
2016/01/30 Javascript
通过Tabs方法基于easyUI+bootstrap制作工作站
2016/03/28 Javascript
详解JavaScript常量定义
2017/01/03 Javascript
Javascript 详解封装from表单数据为json串进行ajax提交
2017/03/29 Javascript
AngularJS基于provider实现全局变量的读取和赋值方法
2017/06/28 Javascript
vue-router+vuex addRoutes实现路由动态加载及菜单动态加载
2017/09/28 Javascript
vuex实现像调用模板方法一样调用Mutations方法
2019/11/06 Javascript
js get和post请求实现代码解析
2020/02/06 Javascript
[00:12]DAC2018 Miracle-站上中单舞台,他能否再写奇迹?
2018/04/06 DOTA
python实现DNS正向查询、反向查询的例子
2014/04/25 Python
在Python程序中进行文件读取和写入操作的教程
2015/04/28 Python
django接入新浪微博OAuth的方法
2015/06/29 Python
Python字典简介以及用法详解
2016/11/15 Python
python音频处理用到的操作的示例代码
2017/10/27 Python
Pycharm 操作Django Model的简单运用方法
2018/05/23 Python
idea2020手动安装python插件的实现方法
2020/07/17 Python
cookies应对python反爬虫知识点详解
2020/11/25 Python
CSS3 animation ? steps 函数详解
2019/08/30 HTML / CSS
美国生日蛋糕店:Bake Me A Wish!
2017/02/08 全球购物
戴尔新西兰官网:Dell New Zealand
2020/01/07 全球购物
市场营销专科应届生求职信
2013/11/24 职场文书
好矿嫂事迹材料
2014/01/21 职场文书
建筑设计专业求职自我评价
2014/03/02 职场文书
毕业自我鉴定总结
2014/03/24 职场文书
小学生安全责任书
2014/07/25 职场文书
习总书记三严三实学习心得体会
2014/10/13 职场文书
四风之害观后感
2015/06/09 职场文书
队名及霸气口号大全
2015/12/25 职场文书
《地震中的父与子》教学反思
2016/02/16 职场文书
mysql 索引合并的使用
2021/08/30 MySQL