Java日期时间API系列40-----中文语句中的时间语义识别(time NLP)代码实现分析
从上篇 Java日期时间API系列39-----中文语句中的时间语义识别(time NLP 输入一句话,能识别出话里的时间)原理分析 中得知解析的主要步骤分为三步:
(1)加载正则文件
(2)解析中文语句中的所有时间词语
(3)根据基准时间,循环解析(2)中的时间词语。
下面结合代码分析一下。
1.加载正则文件
(1)正则文件介绍:
TimeRegex.Gzip(原项目中名称为TimeExp.m)是所有解析识别的基础。解压后查看可以看到文件内部为大量正则表达式,如部分截图如下:
(2)单例加载
public class TextAnalysis { private static volatile TextAnalysis instance;
private static Pattern pattern;
private boolean isPreferFuture; private TextAnalysis(){
try {
pattern = RegexResourceUtil.readModel("TimeRegex.Gzip");
isPreferFuture = true;
} catch (Exception e) {
e.printStackTrace();
}
} public static TextAnalysis getInstance(){
if(instance == null){
synchronized(TextAnalysis.class){
if(instance == null){
instance = new TextAnalysis();
}
}
}
return instance;
}
} //RegexResourceUtil.readModel(String) /**
* 获取Pattern
* @param fileName 文件名称
* @return Pattern 正则对象
* @throws Exception 异常
*/
public static Pattern readModel(String fileName) throws Exception {
try(InputStream resourceAsStream = RegexResourceUtil.class.getClassLoader().getResourceAsStream(fileName)){
ObjectInputStream in = new ObjectInputStream(
new BufferedInputStream(new GZIPInputStream((resourceAsStream))));
Pattern p = (Pattern) in.readObject();
return Pattern.compile(p.pattern());
}
}
2.解析中文语句中的所有时间词语
/**
* 根据正则集合识别出时间词语
* @param text 待处理文本
* @return 时间词语
*/
public List<String> analysis(String text){
Matcher match;
int startline = -1, endline = -1; List<String> tempResult = new ArrayList<>();
tempResult.add("");
int rpointer = 0;// 计数器,记录当前识别到哪一个字符串了 match = pattern.matcher(text);
boolean startmark = true;
while (match.find()) {
startline = match.start();
if (endline == startline) // 假如下一个识别到的时间字段和上一个是相连的 @author kexm
{
rpointer--;
tempResult.set(rpointer, tempResult.get(rpointer) + match.group());// 则把下一个识别到的时间字段加到上一个时间字段去
} else {
if (!startmark) {
rpointer--;
rpointer++;
}
startmark = false;
tempResult.set(rpointer, match.group());// 记录当前识别到的时间字段,并把startmark开关关闭。这个开关貌似没用?
}
endline = match.end();
rpointer++;
if((tempResult.size()-1)<rpointer){
tempResult.add("");
}
}
if (rpointer > 0) {
rpointer--;
rpointer++;
}
return tempResult;
}
3.根据基准时间,循环解析(2)中的时间词语。
/**
* 时间表达式单元构造方法
* 该方法作为时间表达式单元的入口,将时间表达式字符串传入
*
* @param timeExpression 时间表达式字符串
* @param textAnalysis 正则文件分析类
* @param timePoint 上下文时间
*/ public TimeNLP(String timeExpression, TextAnalysis textAnalysis, TimeContext timePoint) {
this.timeExpression = timeExpression;
this.textAnalysis = textAnalysis;
this.timeContextOrigin = timePoint;
timeNormalization();
} /**
* 时间表达式规范化的入口
* <p>
* 时间表达式识别后,通过此入口进入规范化阶段,
* 具体识别每个字段的值
*/
private void timeNormalization() {
//标准时间解析
LocalDateTime localDateTime = normStandardTime();
if(localDateTime == null){
normYear();
normMonth();
normDay();
normMonthFuzzyDay();/**add by kexm*/
normBaseRelated();
normBaseTimeRelated();
normCurRelated();
normHour();
normMinute();
normSecond();
normTotal();
modifyTimeBase();
localDateTime = LocalDateTime.of(1970, 1, 1, 0, 0);
}
String[] timeGrid = new String[6];
timeGrid = timeContextOrigin.getTimeBase().split("-"); int tunitpointer = 5;
while (tunitpointer >= 0 && timeContext.getTunit()[tunitpointer] < 0) {
tunitpointer--;
}
for (int i = 0; i < tunitpointer; i++) {
if (timeContext.getTunit()[i] < 0)
timeContext.getTunit()[i] = Integer.parseInt(timeGrid[i]);
}
String[] resultTmp = new String[6];
resultTmp[0] = String.valueOf(timeContext.getTunit()[0]);
if (timeContext.getTunit()[0] >= 10 && timeContext.getTunit()[0] < 100) {
resultTmp[0] = "19" + String.valueOf(timeContext.getTunit()[0]);
}
if (timeContext.getTunit()[0] > 0 && timeContext.getTunit()[0] < 10) {
resultTmp[0] = "200" + String.valueOf(timeContext.getTunit()[0]);
} for (int i = 1; i < 6; i++) {
resultTmp[i] = String.valueOf(timeContext.getTunit()[i]);
}
if (Integer.parseInt(resultTmp[0]) != -1) {
timeNorm += resultTmp[0] + "年";
localDateTime = localDateTime.withYear(Integer.valueOf(resultTmp[0]));
if (Integer.parseInt(resultTmp[1]) != -1) {
timeNorm += resultTmp[1] + "月";
localDateTime = localDateTime.withMonth(Integer.valueOf(resultTmp[1]));
if (Integer.parseInt(resultTmp[2]) != -1) {
timeNorm += resultTmp[2] + "日";
localDateTime = localDateTime.withDayOfMonth(Integer.valueOf(resultTmp[2]));
if (Integer.parseInt(resultTmp[3]) != -1) {
timeNorm += resultTmp[3] + "时";
localDateTime = localDateTime.withHour(Integer.valueOf(resultTmp[3]));
if (Integer.parseInt(resultTmp[4]) != -1) {
timeNorm += resultTmp[4] + "分";
localDateTime = localDateTime.withMinute(Integer.valueOf(resultTmp[4]));
if (Integer.parseInt(resultTmp[5]) != -1) {
timeNorm += resultTmp[5] + "秒";
localDateTime = localDateTime.withSecond(Integer.valueOf(resultTmp[5]));
}
}
}
}
}
}
timeContextOrigin.setTunit(timeContext.getTunit().clone());
timeContext.setTimeBase(timeContextOrigin.getTimeBase());
timeContext.setOldTimeBase(timeContextOrigin.getOldTimeBase());
time = DateTimeConverterUtil.toDate(localDateTime);
timeNormFormat = DateTimeFormatterUtil.format(localDateTime, DateTimeFormatterUtil.YYYY_MM_DD_HH_MM_SS_FMT);
} //下面只举例 年的识别 /**
* 年-规范化方法
* <p>
* 该方法识别时间表达式单元的年字段
*/
private void normYear() {
/**假如只有两位数来表示年份*/
Pattern pattern = RegexEnum.NormYearTwo.getPattern();
Matcher match = pattern.matcher(timeExpression);
if (match.find()) {
timeContext.getTunit()[0] = Integer.parseInt(match.group());
if (timeContext.getTunit()[0] >= 0 && timeContext.getTunit()[0] < 100) {
if (timeContext.getTunit()[0] < 30) /**30以下表示2000年以后的年份*/
timeContext.getTunit()[0] += 2000;
else/**否则表示1900年以后的年份*/
timeContext.getTunit()[0] += 1900;
} }
/**不仅局限于支持1XXX年和2XXX年的识别,可识别三位数和四位数表示的年份*/
pattern = RegexEnum.NormYearFour.getPattern();
match = pattern.matcher(timeExpression);
if (match.find())/**如果有3位数和4位数的年份,则覆盖原来2位数识别出的年份*/ {
timeContext.getTunit()[0] = Integer.parseInt(match.group());
}
}
timenlp相关代码仍有很多需要不断优化的地方,欢迎参与。
Java日期时间API系列40-----中文语句中的时间语义识别(time NLP)代码实现分析的更多相关文章
- Java日期时间API系列39-----中文语句中的时间语义识别(time NLP 输入一句话,能识别出话里的时间)原理分析
NLP (Natural Language Processing) 是人工智能(AI)的一个子领域.自然语言是人类智慧的结晶,自然语言处理是人工智能中最为困难的问题之一(来自百度百科). 其中中文更是 ...
- Java日期时间API系列19-----Jdk8中java.time包中的新的日期时间API类,ZonedDateTime与ZoneId和LocalDateTime的关系,ZonedDateTime格式化和时区转换等。
通过Java日期时间API系列6-----Jdk8中java.time包中的新的日期时间API类中时间范围示意图:可以很清晰的看出ZonedDateTime相当于LocalDateTime+ZoneI ...
- Java日期时间API系列11-----Jdk8中java.time包中的新的日期时间API类,使用java8日期时间API重写农历LunarDate
通过Java日期时间API系列7-----Jdk8中java.time包中的新的日期时间API类的优点,java8具有很多优点,现在网上查到的农历转换工具类都是基于jdk7及以前的类写的,下面使用ja ...
- Java日期时间API系列8-----Jdk8中java.time包中的新的日期时间API类的LocalDate源码分析
目录 0.前言 1.TemporalAccessor源码 2.Temporal源码 3.TemporalAdjuster源码 4.ChronoLocalDate源码 5.LocalDate源码 6.总 ...
- Java日期时间API系列12-----Jdk8中java.time包中的新的日期时间API类,日期格式化,常用日期格式大全
通过Java日期时间API系列10-----Jdk8中java.time包中的新的日期时间API类的DateTimeFormatter, 可以看出java8的DateTimeFormatter完美解决 ...
- Java日期时间API系列1-----Jdk7及以前的日期时间类
先看一个简单的图: 主要的类有: Date类负责时间的表示,在计算机中,时间的表示是一个较大的概念,现有的系统基本都是利用从1970.1.1 00:00:00 到当前时间的毫秒数进行计时,这个时间称为 ...
- java 日期和字符串互转,依据当天整天时间 得到当天最后一秒的日期时间
java 日期和字符串互转.依据当天整天时间 得到当天最后一秒的日期时间 package com.hi; import java.text.DateFormat; import java.text ...
- NET MVC全局异常处理(一) 【转载】网站遭遇DDoS攻击怎么办 使用 HttpRequester 更方便的发起 HTTP 请求 C#文件流。 Url的Base64编码以及解码 C#计算字符串长度,汉字算两个字符 2019周笔记(2.18-2.23) Mysql语句中当前时间不能直接使用C#中的Date.Now传输 Mysql中Count函数的正确使用
NET MVC全局异常处理(一) 目录 .NET MVC全局异常处理 IIS配置 静态错误页配置 .NET错误页配置 程序设置 全局异常配置 .NET MVC全局异常处理 一直知道有.NET有相关 ...
- Java日期时间API系列13-----Jdk8中java.time包中的新的日期时间API类,时间类转换,Date转LocalDateTime,LocalDateTime转Date等
从前面的系列博客中可以看出Jdk8中java.time包中的新的日期时间API类设计的很好,但Date由于使用仍非常广泛,这就涉及到Date转LocalDateTime,LocalDateTime转D ...
- Java日期时间API系列6-----Jdk8中java.time包中的新的日期时间API类
因为Jdk7及以前的日期时间类的不方便使用问题和线程安全问题等问题,2005年,Stephen Colebourne创建了Joda-Time库,作为替代的日期和时间API.Stephen向JCP提交了 ...
随机推荐
- SLF4J2.0.x与Logback1.3.x的绑定变动还是很大的,不要乱点鸳鸯谱
开心一刻 今天跟我姐聊天 我:我喜欢上了我们公司的一个女同事,她好漂亮,我心动了,怎么办 姐:喜欢一个女孩子不能只看她的外表 我:我知道,还要看她的内在嘛 姐:你想多了,还要看看自己的外表 背景介绍 ...
- [香橙派开发系列]使用wiringPi控制26个引脚
目录 前言 一.香橙派使用的包 二.使用wiringPi包的命令 1.下载wiringOP 2.gpio readall信息分析 3.设置gpio的模式 4.设置gpio输出的电平 三.wiringP ...
- 图书《数据资产管理核心技术与应用》核心章节节选-3.1.2. 从Spark 执行计划中获取数据血缘
本文节选自清华大学出版社出版的图书<数据资产管理核心技术与应用>,作者为张永清等著. 从Spark 执行计划中获取数据血缘 因为数据处理任务会涉及到数据的转换和处理,所以从数据任务中解析血 ...
- 【Vue】Re17 Router 第四部分(参数传递,守卫函数)
一.案例搭建 新建Profile组件 组件写好内容后配置路由 { path : '/profile', component : () => import('../components/Profi ...
- 基于 ChatGPT 的聊天软件合集打包分享
「基于 ChatGPT 的聊天软件合集打包」 链接:https://pan.quark.cn/s/ef1f5e9c48e4 BotGem(原名AMA) 官网:https://botgem.com/ ...
- 局域网中如何为Ubuntu20.04和window10共享文件
如题,自己有两台电脑,一个安装的window10,一个安装的是Ubuntu20.04,两个电脑之间经常需要传文件,这个时候总是用U盘来回导文件就变得十分的不方便,于是就想到了设置网络共享. 本文的做法 ...
- python编程中的circular import问题
循环引入,circular import是编程语言中常见的问题,在C语言中我们可以使用宏定义来处理,在c++语言中我们可以使用宏定义和类的预定义等方式来解决,那么在python编程中呢? 其实在pyt ...
- WPF Button自定义样式收集 带有图片的Button
此篇只是收集平时写过的样式~ 带有图片的Button 为Button设定了一些附加属性,用于添加图片到Button. 比如初始化图片和点击后的图片 public static readonly Dep ...
- fastDFS安装时,./make.sh编译时出错找不到./make.sh: line 99: perl: command not found
1.背景 报错如下: 2.解决方案 执行命令: yum -y install zlib zlib-devel pcre pcre-devel gcc gcc-c++ openssl openssl-d ...
- 告别手动调度,海豚调度器 3.1.x 集群部署让你轻松管理多机!
转载自第一片心意 1 前言 由于海豚调度器官网的集群部署文档写的较乱,安装过程中需要跳转到很多地方进行操作,所以自己总结了一篇可以直接跟着从头到尾进行操作的文档,以方便后续的部署.升级.新增节点.减少 ...