标签(空格分隔): 未分类


高级操作

1.脚本表达式

用双引号包含的脚本被称为脚本表达式,目前支持嵌入Python。 脚本表达式只能在顺序表达式中使用。代码可以在三个位置存在:

位置 功能 例子
匹配(match) 在字符串中匹配字符 match(m.mstr)
转写(rewrite) 对匹配完成的串转写 str.lower(m.mstr)
条件(condition) 判断转写条件是否满足

由于tn本身所带的匹配和转写功能一般足够使用,所以脚本在匹配和转写中只是作为补充,而条件是最需要嵌入脚本的。

由于脚本表达式用双引号表示,为了避免语法解析出现错误,因此在Python代码中需要用单引号来表示字符串。

例1

rule= $(rule0) $(rule1) $(rule2) : $(rewrite1) $(rewrite2) "m.str+'haha'"

rewrite1负责转写rule0,rewrite2转写rule1, 后面的脚本表达式转写rule2:

m代表rule2所匹配的结果。这个结果称为MatchResult(可参考tnpy源代码),它有如下属性:

ot       #原始输入字符串
m.mstr #匹配串
m.rstr #转写串
m.pos #匹配得到的位置

例2

rule= $(rule0) $(rule1) $(rule2) : "m[0].mstr+m[1].mstr+m[2].mstr";

以上脚本,将三个规则的匹配字符串加起来返回。

转写部分只有一个规则时,该规则需要转写匹配部分里的全部内容,形参为m[0],m[1]...,就像这个例子描述的样子。

但如果转写部分有多个规则,则转写部分的规则数量必须和匹配部分的规则数量一致,一一对应:

rule= $(rule0) $(rule1) $(rule2) : "m.mstr" "m.mstr" "m.mstr";

此时,三个脚本表达式分别承载前面的三个顺序规则。由于对应的只有一个规则,所以m等价于m[0]。

例3

low_to_up_letter = $(low_letter) : "unicode.upper(m.mstr)";

$(low_letter)匹配了小写字母,后面的表达式将前面表达式匹配后的结果转换为大写,并返回。

例4

unit_electric = $(integer_decimal) $(unit_tabl_electric) :: "abs(e(unit_kywd_electric),m[0])<33"

这个例子稍微复杂一些,例如识别30m到底是30米还是30兆字节,就取决于文本附近有没有相应的关键字。

unit_kywd_electric规则定义如下:

unit_kywd_electric = ("速度") | ("网速") | ("电脑") | ("导体")...;#省略一部分

上面的脚本,$(integer_decimal)匹配30, $(unit_tabl_electric)匹配m,

e函数在原始字符串中匹配unit_kywd_electric实体,之后判断这个实体在字符串的位置和m[0]的位置的绝对值是否小于33, 用来确定这是否是信息计量单位。

上面的表达式有些复杂,同时,当e函数匹配失败返回None,那么程序就会报错,因此可以修改为

dist('unit_kywd_electric',0)<33

dist是tnpy里内置的一个函数:

 def dist(name, i=0):
header = e(name)
if head is None:
return int_max;
return abs(header.pos - m[i].pos)

tn脚本不建议(也不能够)写入超过多行python代码,因此为了安全和方便,可以自行定制函数来方便匹配和转写,tnpy会将这些函数嵌入到引擎当中,成为闭包函数,例如:

#%Script% extends

这样就导入了extends.py库

2. 使用纯Python编写规则

前面提到,之所以为tn定义一套特别的语法,是为了方便能够跨语言实现解析。这种TN语法能够用正则表达式方便的进行词法分析和语法分析,具体细节可参考tnpy源代码。

但是,我们也可以使用纯Python来编写规则,这样有很多好处,可以内嵌其他实体类型,进一步扩展语言的功能。也能借助现成的Python编译器,及时发现未引用的规则。

规则需要先引入实体:

from tnpy import StringEntity as SE, RegexEntity as RE, TableEntity as TE, SequenceEntity as SQE, RepeatEntity as RPE

接下来我们就能够定义不同的规则了:

build = SE('成立于', '建成了');
splitkw0 = RE('^|[,\.。,和\r\n]');
quotekw = TE([RE('校训'), RE('育人精神')]);
quote0 = RE('"([^"]+)"', '$1');
anything = RE('.*');

得益于Python非常fancy的语法,buiid实际上是("成立于":"建成了"), quotekw则是两个正则的或表达式。

下面定义了一个顺序表达式,是不是可读性也很强呢?

quote1 = SQE([quotekw, anything, quote0, anything, splitkw0])

python版本的规则和tn规则也能相互引用,tn规则可以直接引用py规则,而py规则想要引用,则需要

quote_example= SQE([REF('quote')],[rewriterule])

我简直深深地爱上了Python。

3. 结合NLP和词性

原始的tnpy,为了保证代码的纯粹性,没有加入这些功能,如果我们希望匹配

**名词**确实是**形容词**

这样的表达,难道要把所有的名词和形容词都列进去吗?这显然是不必要的。tnnlp模块就是解决这个问题的。

tnnlp已经添加入tnpy核心库中了。

使用时也很简单:

from tnnlp import NEREntity as NE,WordEntity as WE;

于是,”地名”建成于”时间”,就能用下面的表达式来解决:

time2 = SQE([NE('nt'), build, 'date_fix'], rewriteOrders=[2, 1, 0]);

rewriteorders=[2,1,0]等价于tn规则里的$3,$2,$1.

其中,NE代表一个实体,nt为地名;类似地,n是名词,ad是形容词。 NLP使用了结巴分词作为分词和词性标注的方法。

4.使用词库

如果我们想匹配“程序员”是伟大的职业这样的表达,那么就需要把程序员或是某种工种的所有表达全部列出来。这个工作量太大了。

同样,描述“好”的形容词也有很多,都列出来也会浪费大量的时间。解决这个问题的办法,就是使用词库。

tnnlp使用了哈工大标注的一份语料库:

Aa01A07= 者 手 匠 客 主 子 家 夫 翁 汉 员 分子 鬼 货 棍 徒
Aa01A08= 每人 各人 每位
Aa01A09= 该人 此人
Aa01B01= 人民 民 国民 公民 平民 黎民 庶 庶民 老百姓 苍生 生灵 生人 布衣 白丁 赤子 氓 群氓 黔首 黎民百姓 庶人 百姓 全民 全员 萌
Aa01B02= 群众 大众 公众 民众 万众 众生 千夫
Aa01B03# 良民 顺民
Aa01B04# 遗民 贱民 流民 游民 顽民 刁民 愚民 不法分子 孑遗
Aa01C01= 众人 人人 人们
Aa01C02= 人丛 人群 人海 人流 人潮
Aa01C03= 大家 大伙儿 大家伙儿 大伙 一班人 众家 各户

890KB的词库,定义了大概几十万个词,并使用树结构来讲词义索引起来,例如,所有A开头的都是和人物有关的,后面的标注进一步做了分类。

因此,你可以使用下面的表达,来描述Ae06节点下的所有词汇:

word= WE('Ae06');
rewrite= RE('.+','$1是一种伟大的职业');
wordme= SQE([word],[rewrite])

一旦遇到Ae06分支下的词,就会自动将其转换为xxx是一种伟大的职业。

这也是写纯Python规则的好处,可以方便地定制类型,扩展核心引擎的功能。

5. 乱序匹配

以提取校训为例,校训一般来说有以下几种表达:

语句1:北京邮电大学的校训是“厚德博学,敬业乐群”。

语句2:“学为人师,行为世范”是北师大启功先生提出的校训。“为学生着想”….

如果用正则提取离校训最近的双引号的内容,可能会出错,因为前后可能还有其他双引号标注的内容,如上面的“为学生着想”。

因此,想抽取校训主要有三个特征:校训关键字双引号标点符号

quote1 = SQE([quotekw, anything, quote0, anything, splitkw0], matchorders=[5, 1, 2, 1, 4]);
quote2 = SQE([splitkw0,anything,quote0, anything, quotekw ], matchorders=[4, 1, 3, 1, 5]);
quote = TE([quote1,quote2])

(其他规则都已经在上面定义过了)

quote规则描述了两种类型quote1quote2, 对quote1来说,要匹配语句1,匹配按照优先次序5,1,2,1,4,先匹配quotekw,找到了校训两字,再找分隔符,找到了句号。此时就把整个句子夹逼到了

校训是“厚德博学,敬业乐群”,再匹配quote0,把实际的双引号中的校训提取出来。最终两个anything匹配null

对quote2来说,匹配语句2,quotekw匹配了校训,splitkw0匹配了句首,quote0匹配了"学为人师,行为世范",anything匹配了夹逼后剩下的部分。

乱序匹配本质上,是通过定义匹配顺序,人为地通过围栏分割句子,将句子分割成树,然后在子节点上再进行匹配,这就解决了顺序表达式难以解决的问题。

其实,这里依旧有问题没能解决。如果我们想匹配ABC这三个字母的全排列,如CBA,CAB... 总共有6种方法,总不至于手工编写所有的匹配模式吧?这还只是三种,数量更多之后,手工编写就变得不可行了。那如何实现高效匹配呢?此事我依旧没有思路

6.总结

本文介绍了tn的高级语法,本质上tn是可以被任意改造和扩展的。因此不应当拘泥于本身提供的文法,而是按照自己的需求自行定制。之后会介绍tn的性能优化,用于模式匹配的技巧和实现原理。

tn文本分析语言(三):高级语法的更多相关文章

  1. tn文本分析语言(二) 基本语法

    tn是desert和tan共同开发的一种用于匹配,转写和抽取文本的语言.解释器使用Python实现,代码不超过1000行. 本文主要介绍tn的基本语法.高级内容可以参考其他篇章.使用这样的语法,是为了 ...

  2. 重磅开源:TN文本分析语言

    tn是desert(沙漠之鹰)和tan共同开发的一种用于匹配,转写和抽取文本的语言(DSL).并为其开发和优化了专用的编译器.基于递归下降方法和正则表达式,能解析自然文本并转换为树和字典,识别时间,地 ...

  3. tn文本分析语言(四) 实现自然语言计算器

    tn是desert和tan共同开发的一种用于匹配,转写和抽取文本的语言.解释器使用Python实现,代码不超过1000行. github地址:https://github.com/ferventdes ...

  4. NetAnalyzer笔记 之 十一 打造自己的协议分析语言(1)初衷与语法构想

    回头看看NetAnalyzer开发系文档上次一篇竟然是2016年,老脸一红.不过这几年墨云成功过的讨到一个温柔贤淑的老婆,有了一个幸福的家庭,去年9月又有了一个大胖儿子,想想也就释然了^_^ 其实这几 ...

  5. [Markdown]纯文本标记语言MarkdowPad2--MD语法知识

    ##1.标题 代码 注:# 后面保持空格 # h1 ## h2 ### h3 #### h4 ##### h5 ###### h6 ####### h7 // 错误代码 ######## h8 // ...

  6. Solr:文本分析

    文本分析时搜索引擎的核心工作之一,对文本包含许多处理步骤,比如:分词.大写转小写.词干化.同义词转化等.简单的说,文本分析就说将一个文本字段的值转为一个一个的token,然后被保存到Lucene的索引 ...

  7. iOS开发——语法篇OC篇&高级语法精讲二

    Objective高级语法精讲二 Objective-C是基于C语言加入了面向对象特性和消息转发机制的动态语言,这意味着它不仅需要一个编译器,还需要Runtime系统来动态创建类和对象,进行消息发送和 ...

  8. 大数据系列博客之 --- 深入简出 Shell 脚本语言(高级篇)

    首先声明,此系列shell系列博客分为四篇发布,分别是: 基础篇:https://www.cnblogs.com/lsy131479/p/9914747.html 提升篇:https://www.cn ...

  9. linux文本分析利器awk

    转 快速理解linux文本分析利器awk 原文链接 杜亦舒 性能与架构 awk是什么 如果工作中需要操作linux比较多,那么awk是非常值得学习的 awk是一个极其强大的文本分析工具,把文件逐行的读 ...

随机推荐

  1. 文档ID:某某 模板文件不存在,无法解析文档!

    如果是生成栏目列表时出现这样的问题]:   1.可以修改include/arc.listview.class.php这个文件. 2.复制代码     echo "模板文件不存在,无法解析文档 ...

  2. js二进制与十进制互转

    十进制转换为二进制: var num = 100; console.log(num.toString(2)); toString()方法可把一个 Number 对象转换为一个字符串,并返回结果. 语法 ...

  3. c#中abstract与virtua、overridel的用法

    1.abstract 抽象方法 ,virtual 虚方法 ,override 重载函数 父类A.m() 子类B.m()   abstract的方法父类可以不实现,让子类去重写(重写=overwrite ...

  4. springmvc入门的第一个小例子

    今天我们探讨一下springmvc,由于是初学,所以简单的了解一下 springmvc的流程,后续会持续更新... 由一个小例子来简单的了解一下 springmvc springmvc是spring框 ...

  5. PHP基础知识之遍历

    遍历对象的时候,默认遍历对象的所有属性 class MyClass{    public $var1 = 'value 1';    public $var2 = 'value 2';    publ ...

  6. 正在运行的android程序,按home键之后退回到桌面,在次点击程序图标避免再次重新启动程序解决办法

    正在运行的android程序,按home键之后退回到桌面,在次点击程序图标避免再次重新启动程序解决办法 例如:一个android程序包含两个Activity,分别为MainActivity和Other ...

  7. 学习笔记:Hashtable和HashMap

    学了这么些天的基础知识发现自己还是个门外汗,难怪自己一直混的不怎么样.但这样的恶补不知道有没有用,是不是过段时间这些知识又忘了呢?这些知识平时的工作好像都是随拿随用的,也并不是平时一点没有关注过这些基 ...

  8. SQL语句优化(转载)

    一.操作符优化 1.IN 操作符 用IN写出来的SQL的优点是比较容易写及清晰易懂,这比较适合现代软件开发的风格.但是用IN的SQL性能总是比较低的,从Oracle执行的步骤来分析用IN的SQL与不用 ...

  9. ABP理论学习之Javascript API(理论完结篇)

    返回总目录 本篇目录 Ajax Notification Message UI block和busy 事件总线 Logging 其他工具功能 说在前面的话 不知不觉,我们送走了2015,同时迎来了20 ...

  10. NodeJs 开发微信公众号(二)测试环境部署

    由于卤煮本人是做前端开发的,所以在做公众号过程中基本上没有遇到前端问题,在这方面花的时间是最少的.加上用了mui框架(纯css界面)和自己积累的代码,很快地开发出了界面来.接着是后台开发.卤煮选的是n ...