从上篇 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)代码实现分析的更多相关文章

  1. Java日期时间API系列39-----中文语句中的时间语义识别(time NLP 输入一句话,能识别出话里的时间)原理分析

    NLP (Natural Language Processing) 是人工智能(AI)的一个子领域.自然语言是人类智慧的结晶,自然语言处理是人工智能中最为困难的问题之一(来自百度百科). 其中中文更是 ...

  2. Java日期时间API系列19-----Jdk8中java.time包中的新的日期时间API类,ZonedDateTime与ZoneId和LocalDateTime的关系,ZonedDateTime格式化和时区转换等。

    通过Java日期时间API系列6-----Jdk8中java.time包中的新的日期时间API类中时间范围示意图:可以很清晰的看出ZonedDateTime相当于LocalDateTime+ZoneI ...

  3. Java日期时间API系列11-----Jdk8中java.time包中的新的日期时间API类,使用java8日期时间API重写农历LunarDate

    通过Java日期时间API系列7-----Jdk8中java.time包中的新的日期时间API类的优点,java8具有很多优点,现在网上查到的农历转换工具类都是基于jdk7及以前的类写的,下面使用ja ...

  4. Java日期时间API系列8-----Jdk8中java.time包中的新的日期时间API类的LocalDate源码分析

    目录 0.前言 1.TemporalAccessor源码 2.Temporal源码 3.TemporalAdjuster源码 4.ChronoLocalDate源码 5.LocalDate源码 6.总 ...

  5. Java日期时间API系列12-----Jdk8中java.time包中的新的日期时间API类,日期格式化,常用日期格式大全

    通过Java日期时间API系列10-----Jdk8中java.time包中的新的日期时间API类的DateTimeFormatter, 可以看出java8的DateTimeFormatter完美解决 ...

  6. Java日期时间API系列1-----Jdk7及以前的日期时间类

    先看一个简单的图: 主要的类有: Date类负责时间的表示,在计算机中,时间的表示是一个较大的概念,现有的系统基本都是利用从1970.1.1 00:00:00 到当前时间的毫秒数进行计时,这个时间称为 ...

  7. java 日期和字符串互转,依据当天整天时间 得到当天最后一秒的日期时间

    java 日期和字符串互转.依据当天整天时间   得到当天最后一秒的日期时间 package com.hi; import java.text.DateFormat; import java.text ...

  8. 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有相关 ...

  9. Java日期时间API系列13-----Jdk8中java.time包中的新的日期时间API类,时间类转换,Date转LocalDateTime,LocalDateTime转Date等

    从前面的系列博客中可以看出Jdk8中java.time包中的新的日期时间API类设计的很好,但Date由于使用仍非常广泛,这就涉及到Date转LocalDateTime,LocalDateTime转D ...

  10. Java日期时间API系列6-----Jdk8中java.time包中的新的日期时间API类

    因为Jdk7及以前的日期时间类的不方便使用问题和线程安全问题等问题,2005年,Stephen Colebourne创建了Joda-Time库,作为替代的日期和时间API.Stephen向JCP提交了 ...

随机推荐

  1. tcp粘包与udp丢包的原因

    tcp粘包与udp丢包的原因 一,什么是tcp粘包与udp丢包 TCP是面向流的, 流要说明就像河水一样, 只要有水, 就会一直流向低处, 不会间断. TCP为了提高传输效率, 发送数据的时候, 并不 ...

  2. 测试环境配置https+端口访问留存

    步骤1:阿里云DNS配置本地公网IP解析 步骤2:本地局域网192.168.1.10服务器配置nginx server { listen 8090 ssl; server_name localhost ...

  3. AMD 驱动安装 error 192解决问题

    AMD 驱动安装 error 192解决问题 环境:win10专业版.自动更新已禁止.硬件安装设置[是](window自动下载驱动) [以下环境均在安全模式下进行 (win+r msconfig,安全 ...

  4. mujoco_py.cymj.GlfwError: Failed to initialize GLFW

    安装mujoco后运行可视化界面代码报错: mujoco_py.cymj.GlfwError: Failed to initialize GLFW 解决方法: sudo apt-get install ...

  5. 【CDQ分治】[P5094 [USACO04OPEN] MooFest G 加强版

    P5094 [USACO04OPEN] MooFest G 加强版 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) #include <bits/stdc++.h> us ...

  6. Win32 处理多个按钮共用一个事件消息

    今天在学习制作计算器小程序中,碰到要多个按钮共用一个事件的问题, 现记录下来. 在窗体上按钮排列 排列的时候要按顺序排放,也就是说,0-9的ID号要连着的. #define IDD_DIALOG1 1 ...

  7. 全网最适合入门的面向对象编程教程:37 Python常用复合数据类型-列表和列表推导式

    全网最适合入门的面向对象编程教程:37 Python 常用复合数据类型-列表和列表推导式 摘要: 在 Python 中,列表是一个非常灵活且常用的复合数据类型.它允许存储多个项,这些项可以是任意的数据 ...

  8. 关于phpstudy小坑 经典数据库报错 1044

    经典数据库报错  1044   权限问题 一个很经典的问题 使用的集成环境的phpstudy ,  一直都挺好的  但是每次删除后不能创建同名的数据库   最后发现原来默认的只有一个库  在这个库下面 ...

  9. MarginNote 4 内存泄露?

    在床上用电脑的时候突然发现电脑风扇呼呼响,一摸很烫,以为是被子把出风口堵住了,于是调整角度继续用.结果一段时间之后风扇还是狂转不停,然后收到了这样的提示.不看不知道一看吓一跳,MarginNote 4 ...

  10. 运行 Java 程序

    Java 程序实际上就是我们编译好的 Java 类文件.运行 Java 程序就是运行 Java 类的 main 函数. 编译并运行 Java 文件 源文件: package com.example; ...