Knwl.js 解析器插件开发指南:插件模板、words 数据库与 Knwl.register() / knwl.get() 调用机制
NLP【免费下载链接】KnwlFind Dates, Places, Times, and More. A .js library for parsing text for specific information.项目地址https://gitcode.com/gh_mirrors/kn/Knwl点击查看免费下载本文以 Knwl.js 仓库中的 plugin_development.md 为主线完整讲解如何为一个文本解析库编写 parser plugin解析器插件包括插件构造函数签名、languages语言声明、calls()入口方法、Knwl.words三种词库形态的取用方式、结果对象的preview/found标准字段并结合 knwl.js 核心源码与default_plugins/下的真实插件实现还原Knwl.register()注册、knwl.init()分词、knwl.get()调用的完整链路。读完本文你可以独立写出一个可被knwl.get(yourParser)调用的 Knwl.js 插件并理解它在实例内部的执行位置。插件在 Knwl.js 中的定位Knwl.js 是一个 JavaScript 文本解析库目标是从一段文本中提取特定信息日期、时间、地点、电话号码、邮箱、链接等。README.md 对项目的一句话定义是Parse through text for specific information.要理解 Knwl.js 的插件体系先要抓住原文档开篇给出的一个关键判断Extending the parsing ability of Knwl.js is done through parser plugins. Parser plugins are what allow Knwl.js to parse through text.Without them, Knwl.js is merely a shell.没有插件Knwl.js 只是一个空壳。也就是说核心库 knwl.js 本身并不包含任何具体解析什么信息的逻辑它只负责三件事把输入字符串切词建立全局Knwl.words词库提供register()/get()两个 API 供插件挂接和被调用提供tasks工具方法preview()、search()、removeCharacters()等供插件复用。仓库中的插件分为两类默认插件default_plugins/目录dates.js、times.js、emails.js、links.js、phones.js、places.js。它们在Knwl构造函数末尾被自动注册见 knwl.js// load default plugins this.register(dates, require(./default_plugins/dates)); this.register(times, require(./default_plugins/times)); this.register(emails, require(./default_plugins/emails)); this.register(links, require(./default_plugins/links)); this.register(phones, require(./default_plugins/phones)); this.register(places, require(./default_plugins/places));实验性插件experimental_plugins/目录如money.js、units.js、english.js等。experimental_plugins/notice.md 明确说明这些插件不是生产级插件往往僵硬、硬编码、不够灵活不建议用于生产环境适合实验与二次开发。原文档对两者的对比是默认插件likely the most commonly parsers最常用的解析器实验插件unpolished and often lack the accuracy未打磨、精度常常不如默认插件however, they should still be stable但仍然应当是稳定的。因此如果你想为 Knwl.js 增加一种新的解析能力比如解析货币、度量衡、人名插件就是唯一正确的扩展方式。插件基本模板原文档给出的标准插件模板如下plugin_development.md Basic Plugin Template 一节这是编写任何 Knwl.js 插件的起点function PluginName(knwlInstance) { this.languages { //supported languages english: true }; this.calls function() { var words knwlInstance.words.get(words); //get the String as an array of words var resultsArray []; /* Parser Code */ return results; }; } module.exports PluginName;模板只有四行骨架但每一行都对应着 Knwl.js 核心库中的具体契约。下面逐条结合 knwl.js 源码展开。1. 构造函数参数Knwl 实例function PluginName(knwlInstance) {当使用者通过Knwl.register()加载你的插件时核心库会new你的构造函数并把 Knwl 实例作为第一个参数传入。这一点可以直接在源码中印证knwl.jsthis.register function (name, Plugin) { knwl.plugins[name] new Plugin(knwl); ...拿到knwlInstance之后插件就能访问 Knwl.js 的全部 API词库words、工具方法tasks、语言标识language等。这是插件与宿主之间唯一的耦合点——插件不需要也不应该关心实例从哪里来。2.languages语言支持声明this.languages { //supported languages english: true, spanish: true }插件应当用完整语言名作为键声明自己支持的语言集合。宿主在注册时会校验它构造Knwl时若指定了语言如new Knwl(english)this.language即为该值未指定时默认为unknown见 knwl.jsfunction Knwl(language) { this.language unknown; if (language ! undefined) this.language language;注册时的校验逻辑在 knwl.jsif (knwl.plugins[name].languages ! undefined knwl.language ! unknown) { if (knwl.plugins[name].languages[knwl.language] undefined || knwl.plugins[name].languages[knwl.language] false) { return {Knwl.js Error: Parser plugin does not seem to support the specified language.}; } }两个值得注意的细节这是文档未明说、可从源码确认的行为只有当宿主的language不是unknown时才做校验。也就是说不指定语言构造时所有插件都会被无差别注册校验失败时register()不会抛异常而是返回一个形如{Knwl.js Error: Parser plugin does not seem to support the specified language.}的对象插件对象本身仍会被放入knwl.plugins。因此插件内部若依赖语言应自行检查Knwl.language。仓库内所有默认插件都遵循同一写法例如 default_plugins/phones.js、default_plugins/emails.jsthis.languages { english: true, };3.calls()插件的入口方法this.calls function() {插件必须提供calls()方法它是knwl.get()调用插件的入口。调用链在 knwl.js 中实现this.get function(parser) { if (this.plugins[parser] ! undefined) { try { var args arguments; var data knwl.plugins[parser].calls(args); return data; } catch (error) { console.error(Knwl.js Error, Error running parser plugin parser , error); return false; } } else { console.error(Knwl.js Error, Parser plugin parser not found.); return false; } };从中可以读出三点实现事实按名字查找插件knwl.get(dates)会先查this.plugins[dates]查不到则打印Parser plugin xxx not found.并返回false参数整体透传从源码看get()把整个arguments对象作为第一个参数传给calls(args)。也就是说若插件想接收knwl.get(phones, parameter2, three)里的额外参数应在calls中读取argsargs[0]是插件名args[1]起为透传参数。默认插件均未使用透传参数其calls()不接收形参错误被隔离插件内抛出的异常会被try/catch捕获打印Error running parser plugin xxx后返回false不会让宿主或页面崩溃。这与 README 对实验插件shouldnt crash the page不应让页面崩溃的稳定性要求是一致的。4.words词库三种形态的取用var words knwlInstance.words.get(words);原文档强调插件应当使用 Knwl.js 实例提供的 words 数据库来解析而不是自己重新分词。Knwl.init(str)被调用时宿主把字符串拆分成组件并写入全局对象Knwl.words{}。knwl.js 中可以看到三种形态是怎么生成的this.init function(str) { var lowercase str.toLowerCase(); var linkWords lowercase.split(/[ \n]/); var linkWordsCaseSensitive str.split(/[ \n]/); lowercase lowercase.split(/[\n ]/); for (var ii 0; ii lowercase.length; ii) lowercase[ii] lowercase[ii].replace(/[ ,?!]/g, ).replace(/[]/g, ); var words []; for (var ii 0; ii lowercase.length; ii) words[ii] lowercase[ii].split(/[.,!?]/)[0]; knwl.words.linkWordsCasesensitive linkWordsCaseSensitive; knwl.words.linkWords linkWords; knwl.words.words words; return knwl.words; };knwl.words.get(typeStr)可以取出三种形态knwl.js键名形态适用场景words小写、去除标点的单词数组日期、电话号码等纯语义匹配linkWords小写、保留标点的单词数组需要看标点结构的解析如7/16/1999里的斜杠linkWordsCasesensitive保留标点且保留原始大小写邮箱、URL 等大小写敏感的内容其中get()的实现有一个对插件开发者友好的细节this.words.get function(typeStr) { //retrieve words from database if (knwl.words[typeStr] ! undefined) { return knwl.words[typeStr].concat([]); } };它返回的是concat([])产生的浅拷贝插件对返回列表的增删改不会影响宿主的词库——这与后文插件不得修改Knwl.words数据库的规范互为表里数组层是防御性拷贝但插件仍不应绕过get()直接操作knwl.words.xxx。形态选择有真实案例default_plugins/dates.js 同时取两种形态——words用于月份名、节假日词匹配linkWords用于处理7/16/1999这类含斜杠的结构化日期words形态下标点已被剥离斜杠会丢失var words knwl.words.get(words); var wordsWithPunc knwl.words.get(linkWords);default_plugins/emails.js 和 default_plugins/links.js 则使用linkWordsCasesensitive因为邮箱与 URL 的大小写和标点必须原样保留var words knwl.words.get(linkWordsCasesensitive);5. 结果格式必须返回结果数组return results;插件解析时应预期存在多个结果因此calls()必须返回一个数组每个元素是一个结果对象。除插件自定义属性外有两个标准属性是必需的var result { label: value, // 插件自定义属性如 phone / address / year label: value, preview: knwlInstance.tasks.preview(foundPosition), found: foundPosition };preview结果在原字符串中位置的人类可读片段——一整句或句子的一部分由Knwl.tasks.preview(position)生成参数是结果在词库中的位置found结果在词库中的位置下标。preview()的实现值得看一眼knwl.js它以linkWordsCasesensitive为基准从给定位置向左右两侧扩展遇到?!.等句子边界标点即停或者最多扩展 10 个词为止最后拼出原始大小写的句子片段。这就是为什么返回结果里的preview永远是接近原文的一小段话。README 中knwl.get()的通用返回格式与此一致var results [ { //result //plugin-specific properties preview: This was written on the 2nd of June of 2015., found: integer //the position (in words) of the result in the String } ]Knwl.register()使用者如何加载你的插件Knwl.register()是使用者把插件加载进应用的入口。原文档的描述是该方法会创建你的插件的一个新实例并把 Knwl.js 实例作为第一个参数传入。完整源码见 knwl.jsthis.plugins {}; this.register function (name, Plugin) { knwl.plugins[name] new Plugin(knwl); if (knwl.plugins[name].languages ! undefined knwl.language ! unknown) { if (knwl.plugins[name].languages[knwl.language] undefined || knwl.plugins[name].languages[knwl.language] false) { return {Knwl.js Error: Parser plugin does not seem to support the specified language.}; } } return knwl; };使用方式与 README.md 的 Node.js 安装步骤一致var Knwl require(./knwl.js); var knwlInstance new Knwl(english); // 第一个参数是插件的标签名后续 knwl.get() 用它查找第二个参数是 require() 加载的插件构造函数 knwlInstance.register(yourParser, require(./your_parser.js));注意 README 特别说明default_plugins/目录下的插件会被自动加载即上文构造函数末尾的自动注册无需使用者再次register只有自定义插件或实验性插件需要手动注册。成功注册时register()返回knwl实例本身便于链式调用。knwl.get()调用流程与参数透传knwl.get()是负责调用插件的方法。原文档给出的端到端示例knwl.init(This is a string that contains a phone number: 5223241324); //initialized on String var results knwl.get(phones, parameter2, three); /* # Passed to plugin knwl.get(phones, parameter2, three) -- plugin(phones, parameter2, three) # Plugin returns results plugin -- knwl.get() -- app */ console.log(results);其执行顺序可以概括为一条完整调用链knwlInstance.init(str) // 1. 切词填充 knwl.words 三种形态 knwl.get(phones, ...) // 2. 按名字查 plugins[phones] └─ phonePlugin.calls(args) // 3. 以 arguments 对象调用插件入口 └─ words.get(words) // 4. 插件从词库取数解析 └─ return data // 5. 结果数组原样返回给应用两点前提约束均可在源码确认必须先initREADME 明确提示 Plugins will not function without this method first being called on the String。init()之前knwl.words三个数组都是空的初始化为[]见 knwl.js插件解析不出任何内容插件名必须已注册get()查不到插件名时返回false并打印错误而不是抛异常调用方应以false或undefined作为防御判断。标准与最佳实践原文档列出的规范共有四条逐一对照仓库实现说明。1. 插件应封装在单个文件内仓库内每个插件都是一个独立模块且末尾统一module.exports PluginName例如 default_plugins/times.js、default_plugins/places.js。单文件约定让require(./default_plugins/dates)这样的加载方式保持稳定也便于browserify打包package.json的build/test脚本与浏览器打包流程依赖它。2. 不得修改全局Knwl.words数据库这是插件开发中最重要的纪律。原文档的警告逻辑是Knwl.init()会把字符串拆分后存入全局Knwl.words{}你的插件应当从这个数据库取数但绝不能修改它——If this happens, plugins that run after your own will not access the originally intended data如果发生了这种情况排在你后面运行的插件将访问不到原本预期的数据从而导致后续插件解析失准。实现层面提供了部分保护knwl.words.get()返回的是浅拷贝见前文concat([])因此通过正规 API 取回的数组被修改是安全的但插件若直接写knwl.words.words[...] ...就会破坏共享词库。多插件共用一份词库是该架构的基本假设——dates、times、phones等插件可能对同一条init结果依次被get()调用任何一处污染都会影响全体。3. 公开发布前提供使用文档若打算公开发布插件应当附带文档核心是告诉使用者如何用Knwl.get()发起调用例如knwl.get(dates)。参照 README 中各默认插件的文档写法给出knwl.get(xxx)一行调用 返回什么内容的注释即是最小可用的插件文档模板。4. Make something awesome原文档的第四条规范原文如此。可以理解为规范只约束下限解析质量与创意空间留给作者。参考实现三个默认插件的写法对照原文档最后建议Its recommended that anyone developing plugins for Knwl.js look at preexisting plugins first for inspiration.建议开发 Knwl.js 插件的人先参考现有插件。以下按复杂度递进地拆解三个默认插件作为你写插件时的直接范本。emails.js最小形态——单遍扫描 正则default_plugins/emails.js 是最简短的默认插件结构上几乎就是模板本尊function Emails(knwl) { this.languages { english: true }; this.test /\b[A-Z0-9._%-]([A-Z0-9.-]\.[A-Z]{2,4}|...)\b/i; this.calls function() { var results [], match ; var words knwl.words.get(linkWordsCasesensitive); for (var i 0; i words.length; i) { // 对每个词先剥离 , | ( ) ? 等粘连标点再跑邮箱正则 ... var emailObj { address: match, preview: knwl.tasks.preview(i), found: i }; results.push(emailObj); ... } return results; }; var emails this; }; module.exports Emails;对照模板可以看到全部契约要素languages声明、calls()入口、words.get(linkWordsCasesensitive)取词邮箱大小写敏感、结果对象含插件自定义字段address 标准字段preview/found、返回数组、module.exports导出。写你的第一个插件照这个骨架填解析逻辑即可。phones.js多词回溯 tasks 工具方法default_plugins/phones.js 展示了两种进阶技巧跨词拼装电话号码可能拆在多个词里区号、本地号、国家代码分离插件从当前词向前回溯合并如currWord.length 7时检查words[i - 1]是否为 3 位区号、words[i - 2]是否为国家代码复用宿主工具方法knwl.tasks.removeCharacters([-, (, )], words[i])剥离分隔符该方法定义在 knwl.js避免插件自己写一套正则清洗。结果对象形如{ phone: formatted, preview: ..., found: i }格式化后的号码如1 (522) 324-1324放在插件自定义字段里preview与found保持标准格式。dates.js多策略并行的复杂解析default_plugins/dates.js 的calls()内并行运行了五种识别策略覆盖五种日期写法july 16th 1999月份名 日 年份检索7/16/1999依赖linkWords形态保留斜杠见 dates.js24th of decemberof结构节假日词thanksgiving、christmas、new years等词表定义在 dates.js2013-12-15YMD 数字格式。每个分支命中后都执行同一套收尾constructDateObj()构造{year, month, day, preview}再补dateObj.preview knwl.tasks.preview(i)与dateObj.found ipush 进results数组最后统一return results。它还展示了插件内部维护私有状态this.db缓存词库引用而不触碰宿主词库的惯例。该插件的行为由 test/dates-spec.js 用 Jasmine 用例锁定例如knwl.init( the 28th of december.); var output knwl.get(dates)[0]; expect(output.month).toBe(12);测试入口是 scripts/test.sh./node_modules/.bin/jasmine-node test对应package.json中的npm test。开发自己的插件时照test/目录的既有 spec 文件dates-spec.js、phones-spec.js、emails-spec.js等补一份 spec是验证插件可被knwl.get()正确调用、结果含preview/found的最低成本方式。发布与求助渠道关于发布原文档的建议是如果打算公开发布解析器插件the best and most approachable way is to create a GitHub Repository for the plugin最佳且最易上手的方式是为插件建一个独立的代码仓库——即单文件插件 独立仓库 附带knwl.get(yourParser)用法文档构成完整的发布形态。关于求助渠道原文档给出两条先读现有插件——如上文对照的三个范本这是最主要的自我提升途径若文档看不明白或需要更细粒度的帮助可联系项目作者也可加入项目社区讨论插件开发原文档给出的是作者邮箱与 Google 社区入口随着 Google 服务关闭现阶段的社区互动应以仓库自身的 issue/讨论渠道为准本文不展开外部链接。小结一份插件检查清单结合原文档与源码交付前可以按以下清单自检你的 Knwl.js 插件构造函数接收 Knwl 实例并命名为knwlInstance或knwl以完整语言名声明this.languages与宿主new Knwl(english)的语言标识匹配提供this.calls()从knwlInstance.words.get(...)取词解析且选对三种词库形态语义匹配用words、需标点用linkWords、大小写敏感用linkWordsCasesensitive返回结果数组每个元素含插件自定义字段 标准previewknwlInstance.tasks.preview(foundPosition)found词库下标不修改Knwl.words不依赖执行顺序单文件封装module.exports导出构造函数先knwl.init(str)再knwl.get(yourParser)验证并确认插件异常时get()返回false而非崩溃宿主。满足以上各点你的插件就与default_plugins/下的六个默认插件处在同一契约层面使用者可以完全以registerinitget的既有方式接入它。赞分享NLP【免费下载链接】KnwlFind Dates, Places, Times, and More. A .js library for parsing text for specific information.项目地址https://gitcode.com/gh_mirrors/kn/Knwl点击查看免费下载相关推荐Knwl.js 插件开发终极指南创建自定义文本解析器Knwl.js 插件开发终极指南创建自定义文本解析器 Knwl.js 是一个功能强大的 JavaScript 文本解析库能够从文本中智能提取日期、时间、电话NLPx64dbg 插件开发使用 PLUG_CB_LOADSAVEDB 回调实现插件数据随数据库持久化x64dbg 插件开发使用 PLUG_CB_LOADSAVEDB 回调实现插件数据随数据库持久化 本指南面向 x64dbg 插件开发者讲解如何通过 PLUG逆向工程调试器开发工具应用安全WSABuilds 安装教程在 Windows 上运行带 Google Play 商店和 Root 的安卓子系统WSABuilds 安装教程在 Windows 上运行带 Google Play 商店和 Root 的安卓子系统 WSABuilds 是一套预打包的 Wind开发工具上一篇GetQzonehistory3分钟快速备份QQ空间历史说说的完整教程下一篇如何用XXMI启动器5分钟搞定多游戏模组管理告别繁琐配置的终极指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考