一个小项目长达十二年的故事(第一次关于 BIRMA.NET,坦率地说是第一手资料)

这个项目的诞生可以认为是我在 2007 年底的某个时候想到的一个小想法,它注定要在 12 年后才能找到最终的形式(当然,在这个时间点 - 尽管目前的实施,根据对作者来说,非常满意)。

这一切都始于,在履行我当时在图书馆的官方职责的过程中,我提请注意这样一个事实:将书籍(和音乐)出版物目录的扫描文本中的数据输入现有数据库的过程,显然,可以大大简化和自动化,利用输入所需的所有数据的有序性和可重复性,例如文章作者的姓名(如果我们正在谈论文章的集合)、文章的标题文章(或目录中反映的副标题)和当前目录项的页码。 起初,我几乎相信可以在互联网上轻松找到适合执行此任务的系统。 当我因找不到这样的项目而感到惊讶时,我决定尝试自己实现它。

经过相当短的时间后,第一个原型开始工作,我立即开始在日常活动中使用它,同时在我手上的所有示例上对其进行调试。 幸运的是,在我平时的工作场所,我绝不是一名程序员,但我仍然在工作中经历了明显的“停机时间”,在此期间我正在集中调试我的想法——这在当前的现实中几乎是不可想象的事情,这意味着每日报告白天完成的工作。 完善程序的过程总共花费了不少于一年的时间,但即使在此之后,结果也很难被称为完全成功——最初制定了太多不同的概念,但实施起来并不完全清楚:可选的元素可以被跳过; 向前查看元素(为了将之前的元素替换到搜索结果中); 甚至我们自己尝试实现正则表达式(具有独特的语法)之类的东西。 我必须说,在此之前,我已经放弃了一定程度的编程(大约 8 年,甚至更多),因此将我的技能应用于有趣且必要的任务的新机会完全吸引了我的注意力。 毫不奇怪,最终的源代码——在我没有任何明确的设计方法的情况下——很快就变成了难以想象的大杂烩,由 C 语言中的不同部分和 C++ 的一些元素以及可视化编程的各个方面(最初是它)组成。决定使用 Borland C++ Builder 这样的设计系统 - “几乎是 Delphi,但是是 C 语言”)。 然而,所有这一切最终在我们图书馆的日常活动自动化方面取得了成果。

与此同时,为了以防万一,我决定参加培训专业软件开发人员的课程。 我不知道那里是否真的有可能从头开始学习“成为一名程序员”,但考虑到我当时已经拥有的技能,我能够在某种程度上掌握当时更相关的技术,比如如C#、. NET下开发的Visual Studio,以及Java、HTML和SQL相关的一些技术。 整个培训总共花了两年时间,并作为我的另一个项目的起点,该项目最终持续了几年——但这是一个单独出版物的主题。 在这里,需要注意的是,我尝试调整我在所描述的项目中已经进行的开发,以在 C# 和 WinForms 中创建一个成熟的窗口应用程序,实现必要的功能,并将其用作即将推出的文凭项目。
随着时间的推移,这个想法开始在我看来值得在诸如“LIBKOM”和“CRIMEA”等各图书馆代表参加的年度会议上表达。 这个想法是的,但我当时没有实施。 然后我也希望有人能用更有效的方法重写它。 不管怎样,到2013年,我决定写一份我的前期工作报告,并发送给会议组委会,并申请参加会议的资助。 令我有些惊讶的是,我的申请获得了批准,我开始对项目进行一些改进,准备在会议上进行演示。

到那时,该项目已经获得了一个新名称 BIRMA,获得了各种额外的(与其说是完全实现,不如说是假设的)功能 - 所有详细信息都可以在我的报告中找到.

说实话,很难说 BIRMA 2013 是一个完整的活动。 坦白说,这是一件非常草率的匆忙制作的工艺品。 在代码方面,几乎没有什么特别的创新,除了为解析器创建某种统一语法的相当无助的尝试,其外观让人想起 IRBIS 64 格式化语言(事实上,还有 ISIS 系统 -用括号作为循环结构;为什么当时我觉得它看起来很酷)。 解析器无可救药地偶然发现了这些适当类型的圆括号(因为圆括号还发挥了另一个作用,即它们在解析过程中标记了可以跳过的可选结构)。 我再次建议所有想要更详细地了解当时难以想象、不合理的 BIRMA 语法的人,请参阅我当时的报告。

总的来说,除了与我自己的解析器作斗争之外,关于这个版本的代码我没有什么可说的——除了将现有源代码反向转换为 C++,同时保留 .NET 代码的一些典型特征(说实话,它是很难理解,到底是什么促使我把所有东西都移回去——可能是出于对源代码保密的愚蠢恐惧,就好像它相当于可口可乐的秘密配方一样)。

也许这个愚蠢的决定也是难以将生成的 DLL 库与自制工作站的现有接口配对以将数据输入电子目录的原因(是的,我没有提到另一个重要事实:从现在开始,所有BIRMA“引擎”的代码与预期一致,它与接口部分分离并封装在适当的DLL中)。 为什么有必要为此目的编写一个单独的工作站,无论如何,在其外观和与用户交互的方法上,无耻地复制了 IRBIS 64 系统的同一工作站“目录生成器” - 这是一个单独的问题。 简而言之:它为我当时的毕业项目开发提供了必要的坚实性(否则仅难以消化的解析器引擎在某种程度上是不够的)。 此外,我在使用自己的模块(用 C++ 和 C# 实现)实现 Cataloger 工作站的接口以及直接访问我的引擎时遇到了一些困难。

总的来说,奇怪的是,未来 BIRMA.NET 的这个相当笨拙的原型注定会成为我未来四年的“主力”。 不能说,在这段时间里,我至少没有尝试寻找新的、更完整地实施长期想法的方法。 在其他创新中,应该已经存在可以包含可选元素的嵌套循环序列 - 这就是我将如何实现出版物书目描述和各种其他有趣事物的通用模板的想法。 但在我当时的实际活动中,这一切的需求并不大,当时的实现对于录入目录来说已经足够了。 另外,我们图书馆的发展向量开始越来越偏离博物馆档案的数字化、报道等我不感兴趣的活动,最终迫使我最终离开它,让位给那些愿意做的人。对这一切感到更加满意。

矛盾的是,正是在这些戏剧性事件之后,当时已经具备典型长期建设项目所有特征的 BIRMA 项目似乎开始呈现出期待已久的新生命! 我有更多的空闲时间进行闲思,我再次开始梳理万维网以寻找类似的东西(幸运的是,现在我已经可以猜测寻找所有这些不仅在任何地方,而且在 GitHub 上),并且在某个地方今年年初,我终于在不起眼的名字下看到了知名Salesforce公司的相应产品 戈尔普。 就其本身而言,它几乎可以完成我从这样的解析器引擎中需要的一切 - 即,智能地将单个片段与任意但结构清晰的文本隔离,同时为最终用户提供相当用户友好的界面,包括可理解的本质,例如模式、模板和出现,同时使用熟悉的正则表达式语法,由于划分为指定的语义组进行解析,因此可读性变得无与伦比。

总的来说,我决定这就是 戈尔普 (我想知道这个名字是什么意思?也许是某种“面向通用的正则解析器”?)——这正是我长期以来一直在寻找的。 确实,它针对我自己的需求的立即实现存在这样一个问题:该引擎需要过于严格地遵守源文本的结构顺序。 对于某些报告,例如日志文件(即,开发人员将它们放置为使用该项目的明确示例),这非常合适,但对于扫描目录的相同文本,则不太可能。 毕竟,带有目录的同一页面可以以“目录”、“目录”以及我们不需要放置在预期分析结果中的任何其他初步描述(并手动将其删除)开头。每次也很不方便)。 此外,在各个重复元素(例如作者姓名、标题和页码)之间,页面可能包含一定量的垃圾(例如图画和随机字符),如果能够将这些垃圾删除,那就太好了。隔断。 然而,最后一个方面还没有那么重要,但由于第一个方面,现有的实现无法从某个地方开始寻找文本中必要的结构,而是简单地从头开始处理,没有找到在那里指定了模式并......结束了我的工作。 显然,需要进行一些调整,至少在重复结构之间留出一些空间,这让我重新开始工作。

另一个问题是,该项目本身是用 Java 实现的,如果我计划将来实现某种方法,将该技术与熟悉的应用程序连接起来,以便将数据输入现有数据库(例如 Irbis 的“Cataloger”),那么至少 至少在 C# 和 .NET 中执行此操作。 这并不是说 Java 本身是一种糟糕的语言——我什至曾经用它来实现一个有趣的窗口应用程序,该应用程序实现了家用可编程计算器的功能(作为课程项目的一部分)。 而且在语法方面它与同样的升C 非常相似。 嗯,这只是一个优点:我更容易完成现有的项目。 然而,我不想再次陷入这个相当不寻常的窗口(或者更确切地说,桌面)Java 技术的世界 - 毕竟,该语言本身并不是为这种用途“量身定制”的,而且我根本不渴望重复以前的经历。 也许正是因为 C# 与 WinForms 结合起来更接近我们许多人曾经开始使用的 Delphi。 幸运的是,很快就找到了必要的解决方案 - 以项目的形式 IKVM网络,这使得将现有 Java 程序转换为托管 .NET 代码变得容易。 确实,当时该项目本身已经被作者放弃,但它的最新实现使我能够相当成功地对源文本执行必要的操作 戈尔普.

因此,我进行了所有必要的更改,并将其全部组装到适当类型的 DLL 中,该 DLL 可以轻松地被 Visual Studio 中创建的 .NET Framework 的任何项目“拾取”。 同时,我创建了另一个层以方便呈现返回的结果 戈尔普,以相应的数据结构的形式,以便在表视图中处理(以行和列为基础;字典键和数字索引)。 嗯,处理和显示结果所需的实用程序本身编写得相当快。

此外,为新引擎调整模板以教其解析目录扫描文本的现有样本的过程并没有引起任何特殊的复杂性。 事实上,我什至根本不需要参考以前的模板:我只是从头开始创建所有必要的模板。 此外,如果设计用于与系统的先前版本一起使用的模板为可以在其帮助下正确解析的文本设置了相当狭窄的框架,那么新引擎已经可以开发适合多种类型标记的相当通用的模板一次。 我什至尝试为任意目录文本编写某种综合模板,当然,即使为我提供了所有新的可能性,特别是实现相同嵌套重复序列的能力有限(例如,连续几个作者的姓氏和首字母),结果证明这是一个乌托邦。

也许将来可以实现元模板的某种概念,它能够立即检查源文本是否符合多个可用模板,然后根据获得的结果选择最合适的一个,使用某种智能算法。 但现在我更关心另一个问题。 像这样的解析器 戈尔普尽管它具有多种功能并且我做了很多修改,但它本质上仍然无法完成我自己编写的解析器从第一个版本就能够完成的看似简单的事情。 即:他能够从源文本中查找并提取与正确位置使用的模板中指定的掩码相匹配的所有片段,而对给定文本在这些片段之间的空格中包含的内容完全不感兴趣。 到目前为止,我只稍微改进了新引擎,使其能够从当前位置搜索给定序列的此类掩码的所有可能的新重复,从而使文本中存在完全不存在的任意字符集的可能性。在解析中未被考虑,包含在检测到的重复结构之间。 然而,无论使用相应掩码搜索前一个片段的结果如何,这都无法设置下一个掩码:所描述的文本结构的严格性仍然没有为任意包含不规则字符留下空间。

如果对于我遇到的目录示例,这个问题似乎还没有那么严重,那么当尝试将新的解析机制应用于解析网站内容的类似任务(即相同的解析)时,其局限性就在这里,它们显而易见。 毕竟,为 Web 标记片段设置必要的掩码非常容易,我们正在查找的数据(需要提取)应该位于这些掩码之间,但是我们如何强制解析器立即转到下一个类似的片段,尽管所有可能的标签和 HTML 属性都可以放置在它们之间的空格中?

经过一番思考,我决定引入几种服务模式 (%all_之前) и (%all_after),其明显目的是确保在其后面的任何模式(掩码)之前跳过源文本中可能包含的所有内容。 此外,如果 (%all_之前) 简单地忽略所有这些任意包含物,然后 (%all_after)相反,允许它们从前一个片段移动后添加到所需的片段。 听起来很简单,但是为了实现这个概念,我必须再次梳理 gorp 源代码以进行必要的修改,以免破坏已经实现的逻辑。 最后,我们成功地做到了这一点(尽管我的解析器的实现甚至是非常非常第一个,尽管有很多错误,但已经编写完成,甚至更快 - 在几周内)。 从现在开始,该系统呈现出真正通用的形式——距首次尝试使其发挥作用至少 12 年。

当然,这并不是我们梦想的结束。 您还可以使用任何可用的库来实现自由语法,用 C# 完全重写 gorp 模板解析器。 我认为应该显着简化代码,这将使我们能够摆脱现有 Java 源代码形式的遗留问题。 但是使用现有类型的引擎,也很有可能做各种有趣的事情,包括尝试实现我已经提到的元模板,更不用说解析来自各个网站的各种数据(但是,我不排除现有的专用软件工具更适合于此——我只是还没有使用它们的适当经验)。

顺便说一句,今年夏天我已经收到了一封来自一家使用 Salesforce 技术的公司(原始开发者)的电子邮件邀请 戈尔普),通过后续在里加工作的面试。 不幸的是,目前我还没有准备好进行此类重新部署。

如果此材料引起了一些兴趣,那么在第二部分中,我将尝试使用 Salesforce 中使用的实现示例更详细地描述编译和随后解析模板的技术 戈尔普 (我自己的添加,除了已经描述的几个功能词之外,几乎没有对模板语法本身进行任何更改,因此原始系统的几乎所有文档 戈尔普 也适合我的版本)。

来源: habr.com

添加评论