{"id":13466282,"url":"https://github.com/noprompt/frak","last_synced_at":"2025-05-15T07:05:57.147Z","repository":{"id":9774246,"uuid":"11745622","full_name":"noprompt/frak","owner":"noprompt","description":"Transform collections of strings into regular expressions.","archived":false,"fork":false,"pushed_at":"2019-04-10T22:38:00.000Z","size":63,"stargazers_count":1134,"open_issues_count":1,"forks_count":38,"subscribers_count":47,"default_branch":"master","last_synced_at":"2025-04-14T12:58:48.884Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Clojure","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/noprompt.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2013-07-29T17:41:10.000Z","updated_at":"2025-03-30T15:39:45.000Z","dependencies_parsed_at":"2022-07-12T15:03:51.597Z","dependency_job_id":null,"html_url":"https://github.com/noprompt/frak","commit_stats":null,"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noprompt%2Ffrak","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noprompt%2Ffrak/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noprompt%2Ffrak/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/noprompt%2Ffrak/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/noprompt","download_url":"https://codeload.github.com/noprompt/frak/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254198372,"owners_count":22030954,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-07-31T15:00:42.009Z","updated_at":"2025-05-15T07:05:52.111Z","avatar_url":"https://github.com/noprompt.png","language":"Clojure","funding_links":[],"categories":["Clojure","解析库"],"sub_categories":["文件同步"],"readme":"# frak\n\nfrak transforms collections of strings into regular expressions for\nmatching those strings. The primary goal of this library is to\ngenerate regular expressions from a known set of inputs which avoid\nbacktracking as much as possible. It is available as a [command line\nutility](#command-line-usage) and for the [browser](#browser-usage)\nas a JavaScript library.\n\n## \"Installation\"\n\nAdd frak as a dependency to your `project.clj` file.\n\n[![Clojars Project](https://img.shields.io/clojars/v/frak.svg)](https://clojars.org/frak)\n\n\n## Clojure(Script) usage\n\n```clojure\nuser\u003e (require 'frak)\nnil\nuser\u003e (frak/pattern [\"foo\" \"bar\" \"baz\" \"quux\"])\n#\"(?:ba[rz]|foo|quux)\"\nuser\u003e (frak/pattern [\"Clojure\" \"Clojars\" \"ClojureScript\"])\n#\"Cloj(?:ure(?:Script)?|ars)\"\nuser\u003e (frak/pattern [\"skill\" \"skills\" \"skull\" \"skulls\"])\n#\"sk(?:[ui]lls?)\"\n```\n\n## Options\nFrak's `pattern` function can accept an options map as its second argument, the available options are:\n\n* `:capture?` - boolean (default false), whether rendered regex should create capture groups for each match\n* `:escape-chars` - vector (default see [`frak/metacharacters`](https://github.com/noprompt/frak/blob/3c22d9b33646ca4bc681a5746ac0d11fd06e579b/src/cljx/frak.cljx#L54)), characters to escape when rendering a regular expression.\n* `:exact?` - boolean (default false), whether the rendered regex should only produces matches when the entire input string matches.\n* `:whole-words?` - boolean (default false), whether the rendered regex should match only whole words (word boundary at both ends of the match) in the input string.\n\n## Command line usage\n\nfrak can be used from the command line with either Leiningen or NodeJS.\n\n### With Leiningen\n\nUse the `lein run` command:\n\n```shell\n$ lein run -e foo bar baz quux\n^(?:ba[rz]|foo|quux)$\n```\n\n### With NodeJS\n\nCompile the NodeJS version\n\n```shell\n$ lein do cljx once, cljsbuild once node\n$ chmod +x bin/frak\n$ bin/frak -e foo bar baz quux\n^(?:ba[rz]|foo|quux)$\n```\n\n## Browser usage\n\nTo use frak as a standalone library in the browser with JavaScript\ncompile the browser version:\n\n```shell\n$ lein do cljx once, cljsbuild once browser\n$ mv ./target/js/frak.min.js \u003cdestination\u003e\n```\n\nTry it using this HTML:\n\n```html\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n\u003chead\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n  \u003cpre\u003eInput: \u003cspan id=\"input\"\u003e\u003c/span\u003e\u003c/pre\u003e\n  \u003cpre\u003eOutput: \u003cspan id=\"output\"\u003e\u003c/span\u003e\u003c/pre\u003e\n  \u003cscript src=\"http://code.jquery.com/jquery-2.0.3.min.js\"\u003e\u003c/script\u003e\n  \u003cscript src=\"frak.min.js\"\u003e\u003c/script\u003e\n  \u003cscript\u003e\n    var strings = [\"foo\", \"bar\", \"baz\", \"quux\"];\n    // It's a good idea to use the `\"exact?\"` option.\n    var pattern = frak.pattern(strings, {\"exact?\": true})\n    jQuery(\"#input\").text(strings.join(\" \"));\n    jQuery(\"#output\").text(pattern);\n  \u003c/script\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nFor even more fun try it with [AngularJS](http://angularjs.org/)!\n\n## How?\n\nA frak pattern is constructed from a trie of characters and a\nrenderer which processes it. As characters are added to the trie, data\nsuch as such as which characters are terminal are stored in it's\nbranches.\n\nDuring the rendering process frak analyzes each branch and attempts to\nemit the most concise regular expression possible. Additional post\noperations are applied after rendering to improve the expression where\npossible.\n\n## Why?\n\n[Here's](https://github.com/guns/vim-clojure-static/blob/249328ee659190babe2b14cd119f972b21b80538/syntax/clojure.vim#L91-L92)\nwhy. Also because.\n\n## And now for something completely different\n\nLet's build a regular expression for matching any word in\n`/usr/share/dict/words`.\n\n```clojure\nuser\u003e (require '[clojure.java.io :as io])\nnil\nuser\u003e (def words\n           (-\u003e (io/file \"/usr/share/dict/words\")\n               io/reader\n               line-seq))\n#'user/words\nuser\u003e (def word-re (frak/pattern words))\n#'user/word-re\nuser\u003e (every? #(re-matches word-re %) words)\ntrue\n```\n\nThe last two operations will take a moment since there are over\n235,000 words to consider.\n\nYou can view the full expression\n[here](https://gist.github.com/noprompt/6106573/raw/fcb683834bb2e171618ca91bf0b234014b5b957d/word-re.clj)\n(it's approximately `1.5M`!).\n\n## Benchmarks\n\n```clojure\n(use 'criterium.core)\n\n(def words\n  (-\u003e (io/file \"/usr/share/dict/words\")\n      io/reader\n      line-seq))\n\n(defn naive-pattern\n  \"Create a naive regular expression pattern for matching every string\n   in strs.\"\n  [strs]\n  (-\u003e\u003e strs\n       (clojure.string/join \"|\")\n       (format \"(?:%s)\")\n       re-pattern))\n\n;; Shuffle 10000 words and build a naive and frak pattern from them.\n(def ws (shuffle (take 10000 words)))\n\n(def n-pat (naive-pattern ws))\n(def f-pat (frak/pattern ws))\n\n;; Verify the naive pattern matches everything it was constructed from.\n(every? #(re-matches n-pat %) ws)\n;; =\u003e true\n\n;; Shuffle the words again since the naive pattern is built in the\n;; same order as it's inputs.\n(def ws' (shuffle ws))\n\n;;;; Benchmarks\n\n;; Naive pattern\n\n(bench (doseq [w ws'] (re-matches n-pat w)))\n;;             Execution time mean : 1.499489 sec\n;;    Execution time std-deviation : 181.365166 ms\n;;   Execution time lower quantile : 1.337817 sec ( 2.5%)\n;;   Execution time upper quantile : 1.828733 sec (97.5%)\n\n;; frak pattern\n\n(bench (doseq [w ws'] (re-matches f-pat w)))\n;;             Execution time mean : 155.515855 ms\n;;    Execution time std-deviation : 5.663346 ms\n;;   Execution time lower quantile : 148.168855 ms ( 2.5%)\n;;   Execution time upper quantile : 164.164294 ms (97.5%)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoprompt%2Ffrak","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnoprompt%2Ffrak","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnoprompt%2Ffrak/lists"}