Gorp.NET-用于创建反向模板以从结构化文本中提取数据的新库

Gorp.NET是一个新库,用于基于现有Salesforce Gorp代码库创建可逆模板以从结构化文本中提取数据。

在本出版物中,我将简要介绍如何使用该库来解析称为Gorp的结构化文本(工具示例之一,有时称为可逆模板构建系统)。
一般而言,什么是可逆模板 ? 假设我们有一个特定的系统,该系统使我们能够根据模板语法定义的严格规则,根据确定的初始数据生成所需的文本。 现在,让我们想象一个任务的含义是相反的-我们有一个文本,该文本具有某种结构上的完整性,可以通过使用基于上一示例中的模板的系统来实现。 我们的目标是从文本中提取基于其构成的源数据。 如果我们尝试提供某种特定的通用语法来解决此问题,并将其提供给相应的解析器,该解析器会将输入文本解析为单独的元素,这将是实现可逆模板概念的语法示例。

为什么我决定专门写有关Gorp的文章 ? 事实是,我决定以这个特定的系统为基础来完成自己的项目-该项目本身的历史记录,包括我对原始Gorp项目所做的所有更改的一些详细信息,可以在上一篇文章中找到。 在这里,我们将专注于技术部分,包括使用引擎的改进版本。 为了方便起见,我将继续将其命名为Gorp.NET ,尽管实际上它不是未移植到.NET的Gorp版本,而只是一个稍作修饰和最终确定的版本,全部使用同一Java。 另一件事是, Gorp本身 (在我的版本中)上方的加载项(称为BIRMA.NET )以托管DLL库的形式使用其自身的特殊程序集-Gorp.NET ,如果您运行源文本,您自己可以轻松获得(他的存储库地址通过IKVM.NET实用程序为https://github.com/S-presso/gorp/ )。

我现在要注意的是,对于从任何结构化文本中提取数据的各种任务, Gorp.NET工具本身就足以满足您的要求 -至少在您掌握Java的命令很少或至少知道如何从项目中的外部Java模块调用方法的情况下.NET Framework,以及其中包括来自标准JVM库的各种类型(我通过相同的IKVM.NET实现了这一点,但是,现在它已处于不受支持的项目状态)。 好吧,接下来您将如何处理提取的数据-正如他们所说,这是您的个人业务。 仅GorpGorp.NET仅提供了一个裸框架。 进一步处理所有此类数据的一些基础工作包含上述BIRMA.NET 。 但是BIRMA.NET功能本身的描述已经是另一份出版物的主题(尽管我已经在以前的BIRMA技术的比较历史回顾中设法提到了一些内容)。 在此,展望未来,我将大胆地声明一下自己,即用于描述Gorp.NET (以及相应地,在BIRMA.NET中 )使用的可逆模板的技术在此类其他工艺中是独一无二的(我说“手工艺品”,因为我还没有看到大型公司为此目的而推广自己的框架-也许,只有Salesforce本身及其最初的Gorp实施)。

为了最全面地揭示Gorp中使用的模板描述系统所基于的概念和技术方面,我只在此处留下了英文原始文档的链接。 它中所述的所有内容,都可以安全地与Gorp.NET结合使用 。 现在,我将向您介绍一些基本信息。

因此,模板的描述是一种文本文档(也许甚至以单个大行显示,可以传递给相应的处理方法)。 它由三个部分组成,其中包含对三个最重要实体的顺序描述: 模式模式样本 (摘录)。

最低级别的块是模式 -它们只能包含正则表达式和对其他模式的引用。 层次结构的下一个层次是模板模板的描述还包含指向模式(也可以命名)的链接,以及以文本文字形式包含的内容,到嵌套模板和提取器的链接。 我现在也不会涉及参数化模式 (在源文档中,很少有使用它们的示例)。 好,最后,有一些示例指定了特定的语法规则,这些规则将命名模式与源文本中的特定出现关联起来。

据我了解, Gorp的创建者设定的最初目标是解析报告文件(或日志文件)中包含的数据序列。 考虑一个简单的系统特定应用示例。

假设我们有一个包含以下行的报告:

<86> 2015-05-12T20:57:53.302858 + 00:00 10.1.11.141 RealSource:“ 10.10.5.3”


让我们组成一个示例模板,以使用Gorp工具进行解析:

pattern %phrase \\S+
pattern %num \\d+\n
pattern %ts %phrase
pattern %ip %phrase

extract interm {
template <%num>$eventTimeStamp(%ts) $logAgent(%ip) RealSource: "$logSrcIp(%ip)"
}


请注意,这里甚至省略了模板分配块,因为所有必需的模板已经包含在最终选择中。 此处使用的所有模板均已命名,其内容在其名称后的括号中指出。 结果,将创建一个名为eventTimeStamplogAgentlogSrcIp的文本数据集。

现在,我们将编写一个用于提取必要数据的简单程序。 假设我们创建的模板已经包含在名为extracts.xtr的文件中。
 import com.salesforce.gorp.DefinitionReader; import com.salesforce.gorp.ExtractionResult; import com.salesforce.gorp.Gorp; // ... DefinitionReader r = DefinitionReader.reader(new File("extractions.xtr")); Gorp gorp = r.read(); final String TEST_INPUT = "<86>2015-05-12T20:57:53.302858+00:00 10.1.11.141 RealSource: \"10.10.5.3\""; ExtractionResult result = gorp.extract(TEST_INPUT); if (result == null) { // no match, handle throw new IllegalArgumentException("no match!"); } Map<String,Object> properties = asMap(); // and then use extracted property values 


一个简单的解析模板的另一个示例:

# Patterns
pattern %num \d+
pattern %hostname [a-zA-Z0-9_\-\.]+
pattern %status \w+

# Templates
@endpoint $srcHost(%hostname): $srcPort(%num)

# Extraction
extract HostDefinition {
template @endpoint $status(%status)
}


好吧,我认为要点很明确。 同样也要提到的是,对于extract方法,还有一个带有两个输入参数的定义,其中第二个输入参数具有逻辑类型。 如果将其设置为true ,则该方法在执行时将遍历所有可能的数据集-直到遇到合适的数据集为止(您也可以将方法调用替换为extractSafe-已经没有第二个参数)。 默认值为false ,该方法可能会“发誓”输入数据和所使用的模板之间的差异。
我同时注意到Gorp.NET还引入了extract方法的新扩展实现:现在有一个版本,带有两个逻辑类型的后续参数。 使用对extractAllFound视图的缩写,我们默认将它们都设置为true。 第三个参数的正值为我们提供了更大的变化范围:从现在开始,我们可以在所需的,已经结构化的样本(包含提取的数据集)之间的间隔中分析文本中是否包含任意字符。

因此,是时候回答这个问题了:除了对extract方法的扩展之外,在Gorp基本版本的这种修改中到底有什么独特之处?
事实是,几年前我已经创建了自己的一种从文本中提取所需数据的工具(该工具也是基于使用其自身特定语法处理某些模板的工具)时,其工作原理略有不同。 它们与Gorp和所有派生框架中实现的方法的主要区别在于,要提取的每个文本元素都是通过列出其左边界和右边界来设置的(每个边界又可以是元素本身的一部分,也可以只是将其与所有后续或先前的文本)。 同时,实际上,在一般情况下,不像Gorp那样对源文本本身的结构进行分析,而只挑选出必要的部分。 至于包含在它们之间的文本的内容,它根本不会屈服于任何结构分析(它很可能是不连贯的字符集)。

Gorp中是否可以实现类似的效果? 在其初始版本中-可能不是(如果我对此不正确,请纠正我)。 如果我们简单地写一个(。*)之类的表达式,则紧跟在掩码后面以指定要搜索的下一个元素的左边框,然后使用“贪婪”量词,将捕获整个后续文本。 而且,我们无法在现有Gorp实现中使用具有“非贪婪”语法的常规。
Gorp.NET允许通过引入两种特殊类型的模式- (%all_before)(%all_after)来顺利解决此问题。 实际上,它们中的第一个是“非贪婪”版本(。*)的替代品,适用于编译自己的模板。 至于(%all_after) ,它还会查看源文本,直到所描述模式的下一部分首次出现-但已经依赖于先前模式的搜索结果。 它们之间的所有内容也将落入当前元素的可提取子字符串中。 从某种意义上说(%all_after) “回头看”,而(%all_before)相反,“ 回头看”。 我注意到在BIRMA的第一个版本中, (%all_before)的唯一模拟是元素说明中缺少的左边框,而模拟(%all_after)分别为空而不是右边框。 如果在描述下一个元素时未设置两个边界,则解析器显然会捕获所有后续文本! 但是, BIRMA的所有这些实现现在仅具有历史意义(您可以在我当时的报告中阅读有关它的更多信息)。
隐藏文字
由于源代码质量极低,因此从未在任何地方进行过布局-实际上,它们可以作为不良软件系统设计的纪念碑。


让我们以从特定网站提取特定用户数据的任务示例来看一下使用服务模式(%all_before)(%all_after)的功能。 我们将解析Amazon网站,尤其是以下页面: https : //www.amazon.com/B06-Plus-Bluetooth-Receiver-Streaming/product-reviews/B078J3GTRK/ )。
隐藏文字
我公司发送了一个示例,该示例来自开发人员空缺的测试任务,该开发人员专门处理数据解析,但不幸的是,该任务未响应我提出的解决方案。 没错,他们只是要求我描述解决方案的一般过程-无需提供特定的算法,作为回应,我已经尝试引用Gorp模板,而当时我自己的扩展名仅存在,正如他们所说的,“在纸上” ”。
为了好奇,我将允许自己在回信中引用一个片段,这显然是第一次提到Gorp.NET ,尽管这是私人性质。
“为使上述我用来解决该问题的正则表达式列表更加直观,我在其基础上编译了一个现成的模板(将其附加到字母上),该模板可通过应用我自己更通用的开发内容来提取所有必要的数据,设计来解决这类问题。 它的代码基于github.com/salesforce/gorp项目,并且在同一页面上对编译此类模板的规则进行了一般性描述。 一般来说,这样的描述本身就意味着具体的正则表达式及其处理逻辑的分配。 这里最难的一点是,对于每个数据样本,我们必须由常规人员完整地描述包含它们的文本的整个结构,而不仅仅是单个元素本身(如编写我们自己的程序那样按循环顺序搜索时可以做到的那样,例如如前所述)。”

最初的任务是从上面的页面收集以下数据:

  • 用户名
  • 等级
  • 评论标题
  • 日期
  • 文字内容


好吧,现在我将为您提供一个由我编译的模板,它使您可以快速有效地完成此任务。 我认为一般含义应该很明显-也许您自己可以提供一个更简洁的解决方案。
隐藏文字
通常,在这个示例中,我调试了自己的Gorp扩展功能(已经没有任何使用目的,而是基于“概念验证”的思想)。


pattern %optspace ( *)
pattern %space ( +)

pattern %cap_letter [AZ]
pattern %small_letter [az]
pattern %letter (%cap_letter|%small_letter)
pattern %endofsentence (\.|\?|\!)+
pattern %delim (\.|\?|\!\,|\:|\;)
pattern %delim2 (\(|\)|\'|\")

pattern %word (%letter|\d)+
pattern %ext_word (%delim2)*%word(%delim)*(%delim2)*

pattern %text_phrase %optspace%ext_word(%space%ext_word)+
pattern %skipped_tags <([^>]+)>

pattern %sentence (%text_phrase|%skipped_tags)+(%endofsentence)?

pattern %start <div class=\"a-fixed-right-grid view-point\">

pattern %username_start <div class=\"a-profile-content\"><span class=\"a-profile-name\">
pattern %username [^\s]+
pattern %username_end </span>

pattern %user_mark_start <i data-hook=\"review-star-rating\"([^>]+)><span class=\"a-icon-alt\">
pattern %user_mark [^\s]+
pattern %user_mark_end ([^<]+)</span>

pattern %title_start data-hook=\"review-title\"([^>]+)>(%skipped_tags)*
pattern %title [^<]+
pattern %title_end </span>

pattern %span class <span class=\"[^\"]*\">

pattern %date_start <span data-hook="review-date"([^>]+)>
pattern %date ([^<]+)
pattern %date_end </span>

pattern %content_start <span data-hook=\"review-body\"([^>]+)>(%skipped_tags)*
pattern %content0 (%sentence)+
pattern %content (%all_after)
pattern %content_end </span>

template @extractUsernameStart (%all_before)%username_start
template @extractUsername $username(%username)%username_end
template @extractUserMarkStart (%all_before)%user_mark_start
template @extractUserMark $user_mark(%user_mark)%user_mark_end
template @extractTitleStart (%all_before)%title_start
template @extractTitle $title(%title)%title_end
template @extractDateStart (%all_before)%date_start
template @extractDate $date(%date)%date_end
template @extractContentStart (%all_before)%content_start
template @extractContent $content(%content)%content_end

extract ToCEntry {
template @extractUsernameStart@extractUsername@extractUserMarkStart@extractUserMark@extractTitleStart@extractTitle@extractDateStart@extractDate@extractContentStart@extractContent
}



今天可能就这些了。 关于我已经实施的第三方工具,该框架已经完全参与其中,我可能会再告诉您一次。

Source: https://habr.com/ru/post/zh-CN476778/


All Articles