文本和日常工具·文本工具包
从 Kleene 到 JavaScript:正则表达式简史
· 背景
正则表达式 JavaScript 计算历史
追溯从 1950 年代的自动机理论到 ed、grep 和 Perl 的正则表达式,再到每个浏览器中的 JavaScript 风格,解释了为什么语法看起来是这样的以及哪些功能何时到达。
每个人都一知半解的奇怪小语言 - 为什么正则表达式语法感觉古老且不一致
正则表达式感觉像是一种跨越时代的语言,因为它们本质上就是这样。交替、重复和分组的紧凑核心从数学符号发展为编辑器命令、命令行过滤器和编程语言功能。标点符号保留下来,但每个主机都添加了自己的便利、限制和术语。
这段历史解释了为什么 grep、Perl、Python 和 JavaScript 之间的模式看起来很熟悉,但行为却有所不同。 “Regex”是一个家族名称,而不是一种通用语法。对于自学成才的分析师来说,有用的教训不是记住每种方言,而是在信任借用的模式之前识别引擎、标志和替换规则。
Kleene 的常规事件 — 20 世纪 50 年代的有限自动机数学给了我们星星
Stephen Cole Kleene 在有限自动机和“常规事件”方面的工作为 20 世纪 50 年代提供了理论根源。他的符号使用并集、串联和闭包等操作描述了符号序列集。闭包操作成为 Kleene 星:`A*` 意味着从 A 中提取零次或多次重复,而不仅仅是“重复一次或多次”。
有限自动机识别的正式正则语言比现在以正则表达式标签出售的许多结构更窄。例如,反向引用可以表达超出经典模型的条件。因此,现代引擎保留了历史名称和大部分符号,同时实现了模式语言,其功能和执行策略超出了 Kleene 最初的数学对象。
Thompson、ed 和 grep — 正则表达式如何在 20 世纪 60 年代末和 1970 年代初的 Unix 工具中进入文本编辑
Ken Thompson 将理论与工作文本工具联系起来。他的 1968 Communications of the ACM 论文描述了将正则表达式编译为机器代码以搜索文本,他早期的编辑工作帮助将模式匹配置于 Unix 谱系中。 `ed` 编辑器在选择和转换匹配行的命令中使用正则表达式。
名称 `grep` 来自 `ed` 命令,通常呈现为 `g/re/p`:全局选择与正则表达式匹配的行并打印它们。早期的 grep 并不是今天的 GNU 选项集合,后来的基本和扩展 POSIX 形式有所不同。这种持久的变化是实用的:一种小型的符号语言成为查找文本的日常界面。
Perl 和 PCRE — 添加了非贪婪量词、环视和当今大多数工具复制的语法的扩展
Perl 创造了一种更丰富的模式语言,成为通用编程的核心。在其各个版本中,程序员在一个高度可见的生态系统中遇到了捕获组、反向引用、断言、惰性量词和模式修饰符。 Perl 5 文档记录了诸如用于最小匹配的 `*?` 和用于正向预测的 `(?=...)` 等构造,以及旧 Unix 形式中缺少的许多功能。
更安全的说法是 Perl 普及了这种风格,而不是相信它发明了每一个扩展。 PCRE 故意提供与 Perl 兼容的语法,而其他引擎则采用选定的想法并拒绝其他想法。共享标点符号可以隐藏不同的语义、Unicode 行为或性能。因此,“类似 Perl”描述了广泛的影响,但并不能保证 Perl 模式是可移植的。
Perl 普及了一种更大的实用模式语言;后来有选择地借用了发动机
JavaScript 标准化了自己的 `RegExp` 对象和文字语法,例如 `/pattern/gi`,用于在浏览器和其他 ECMAScript 环境中运行的程序。它的风格包括捕获和非捕获组、反向引用、前瞻、惰性量词和字符类。后续版本在 ES2018 规范中添加了命名捕获组和后向断言。
JavaScript 不是具有不同分隔符的 PCRE 或 Python。功能可用性取决于引擎实现的 ECMAScript 版本,并且标志是行为的一部分而不是装饰。 MDN 的正则表达式指南是浏览器语法的相关实用参考,但即使有效的 JavaScript 示例也可能依赖于特定接口未公开的标志。
JavaScript 在 ES2018 中获得了命名组和lookbehind,但引擎和标志仍然不同
浏览器将 JavaScript 正则表达式引擎置于普通文本工作附近。页面可以编译模式、计算匹配并将其传递到 `String.prototype.replace` ,而无需将文本发送到专门的正则表达式服务。这种可用性使得浏览器端查找和替换界面成为可能,尽管仍必须单独检查周围页面以获取更广泛的隐私声明。
ToolAcre 的实现在 `compilePattern` 中调用 `new RegExp`,捕获编译失败并返回错误而不是抛出错误。 `findReplace` 在应用标准替换操作之前对匹配进行计数。因此,JavaScript 替换标记(例如捕获引用)遵循主机字符串 API;正则表达式语法和替换语法是相关但不同的语言。
这不包括什么——超越基础知识和引擎性能内部的形式语言理论
这个简短的历史并不能证明实际引擎和有限自动机、调查正则表达式执行算法或按速度排名实现之间的等效性。回溯、线性时间技术和病态模式值得单独对待。 ToolAcre 防护会捕获语法错误,但它不会检测到执行过多回溯并停止主浏览器线程的有效表达式。
时间线也不会将每个元字符分配给单个发明人。软件功能通常是通过论文、编辑、语言发布和兼容的重新实现来实现的,而不是一次干净的交接。来源支持特定的里程碑;它们并不能证明一个简单的故事是合理的,即一种产品批量创建了现代正则表达式,或者后来的口味继承了相同的行为。
要点 — 文本工具包的正则表达式模式是 JavaScript 风格,因此浏览器文档中的模式按书面形式工作
实用的继承在 ToolAcre 中可见:打开 Regex,搜索文本由浏览器的 JavaScript 引擎编译。关闭正则表达式,元字符将被转义,使搜索按字面意思进行。整个字用 ASCII 样式的 `` 边界包裹表达式,而区分大小写控制 `i` 标志是否伴随始终存在的全局 `g` 标志。
最后一个细节纠正了大纲的广泛承诺,即浏览器文档模式按书面方式工作。 ToolAcre 不公开多行、全点、粘性或 Unicode 标志,因此需要 `m`、`s`、`y`、`u` 或 `v` 的示例需要进行调整,并且某些示例无法在此处重现。使用该工具测试支持的 JavaScript 模式、读取替换计数并在优化之前撤消。