从上篇 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. RPC和 HTTP协议

    RPC 和HTTP 的区别 服务发现 HTTP,知道服务域名,可以通过 DNS 解析 得到 服务的IP地址,从而进行访问 RPC 需要一个专门的中间服务去保存服务名和IP信息(注册中心,nacos.c ...

  2. 【SpringMVC】01 快速上手

    环境搭建 EvBuild 环境组成: - JDK 1.8 + - IDEA 2018 + - Maven 3.0 + - Tomcat 8.0 + 搭建步骤: 1.创建Maven - SpringMV ...

  3. 【DataBase】XueSQL Training

    地址: http://xuesql.cn/ Lesson0 -- 认识SQL -- [初体验]这是第一题,请你先将左侧的输入框里的内容清空,然后请输入下面的SQL,您将看到所有电影标题: SELECT ...

  4. 【Shiro】03 ini认证实现

    [基本概念] 1.身份验证 即在应用中谁能证明他就是他本人. 一般提供如他们的身份ID 一些标识信息来表明他就是他本人,如提供身份证,用户名/密码来证明. 在 shiro 中,用户需要提供princi ...

  5. python库 —— gym retro 的 ROMs文件下载地址

    如题: python库 -- gym retro 的 ROMs文件下载地址: https://archive.org/details/No-Intro-Collection_2016-01-03_Fi ...

  6. 国产CPU——兆芯(先开)KX-6640MA 使用感受

    上半年买了个兆芯CPU的小mini电脑,一直没有换Windows系统,这两天想着就换了过来,具体配置如下: 1.  使用Python死循环代码烧机,性能和我14年买的i5-4200M的Intel CP ...

  7. 最新版gym-0.26.2下Atari环境的安装以及环境版本v0,v4,v5的说明

    强化学习的游戏仿真环境可以分为连续控制和非连续控制两类,其中连续控制的以mujoco为主,而非连续控制的以Atari游戏为主,本文对gym下的Atari环境的游戏环境版本进行一定的介绍. 参考:[转载 ...

  8. dll文件缺失

    下载[dll修复工具],花点钱就可以了,比自己百度香多了,网上的解决方案解决不了的这个一键全部解决,愿意百度的去百度,百度不到的建议使用,我吃过这个亏,电脑刷机丢失140.dll文件,网上百度一个都不 ...

  9. # games101 作业3分析 详解bump mapping

    games101 作业3分析 详解bump mapping 代码分析 整体代码结构 其实变化还是不大 主要是引入了vertexshader(什么都没做) 与 fragmentshader(使用了不同的 ...

  10. Structures, unions, enumerations, and bitfields in ARM C and C++

    Structures, unions, enumerations, and bitfields in ARM C and C++ Non-Confidential   ARM DUI0375E ARM ...