繁體中文

文字與日常工具·文字工具包

從 Kleene 到 JavaScript:正規表示式簡史

· 背景

正規表示式 JavaScript 計算歷史

連接有限自動機、Unix grep、Perl 和 JavaScript 正規表示式的時間軸
原始 ToolAcre 向量圖

追溯從 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的實作調用`new RegExp`裡面`compilePattern`,捕獲編譯失敗並傳回錯誤而不是拋出錯誤。`findReplace`在應用標準替換操作之前對匹配項進行計數。因此,JavaScript 替換標記(例如捕獲引用)遵循主機字串 API;正規表示式語法和替換語法是相關但不同的語言。

這不包括什麼-超越基礎知識和引擎表現內部的形式語言理論

這個簡短的歷史並不能證明實際引擎和有限自動機、調查正則表達式執行演演算法或按速度排名實現之間的等效性。回溯、線性時間技術和病態模式值得單獨對待。 ToolAcre 防護會捕捉語法錯誤,但它不會偵測到執行過多回溯並停止主瀏覽器執行緒的有效表達式。

時間軸也不會將每個元字元指派給單一發明人。軟體功能通常是透過論文、編輯、語言發布和相容的重新實現來實現的,而不是一次乾淨的交接。來源支持特定的里程碑;它們並不能證明一個簡單的故事是合理的,即一種產品批量創建了現代正則表達式,或者後來的口味繼承了相同的行為。

重點 — 文字工具包的正規表示式模式是 JavaScript 風格,因此瀏覽器檔案中的模式以書面形式運作

實用的繼承在 ToolAcre 中可見:開啟 Regex,搜尋文字由瀏覽器的 JavaScript 引擎編譯。關閉正規表示式,元字元將被轉義,使搜尋按字面意思進行。整個字用 ASCII 樣式的 `` 邊界包裹表達式,而區分大小寫控制 `i` 標誌是否伴隨始終存在的全域 `g` 標誌。

最後一個細節糾正了大綱的廣泛承諾,即瀏覽器檔案模式按書面方式運作。 ToolAcre 不會公開多行、全點、黏性或 Unicode 標誌,因此需要 `m`、`s`、`y`、`u` 或 `v` 的範例需要進行調整,且某些範例無法在此重現。使用該工具測試支援的 JavaScript 模式、讀取替換計數並在優化之前撤銷。