#author("2026-08-31T22:16:12+09:00","default:guide","guide") * Pkript/文法/正規表現 [#f1f0d146] #author("2026-09-04T00:05:48+09:00","default:mcp","bot-mcp") * Pkript/文法/正規表現 [#s13b1325] 正規表現リテラルの構文と照合時の制限事項。 正規表現リテラルの構文、使えるメソッド、照合時の制限。 ** リファレンス [#i8f378e5] ** リファレンス [#mff0e3fc] *** 構文 [#ib2d1250] *** 構文 [#adf6361f] #pkript(highlight){{ #highlight{{ /パターン/フラグ }} - 値としての型は RegExp。変数に代入できる - 設定定数 PKRIPT_REGEX が 1(既定)の場合にリテラルとして解釈される。0 にすると / は除算としてだけ働く #highlight{{ const DATE = /(\d{4})-(\d{2})-(\d{2})/; function plugin_hello_convert(e) { const line = e.args[0]; if (!DATE.test(line)) return "<p>日付ではありません</p>"; return "<p>" + htmlsc(line.replace(DATE, "$1年$2月$3日")) + "</p>"; } }} *** フラグ [#f74f7aae] |~フラグ|~説明|h |g|全件一致| |i|大文字・小文字の区別を無視| |m|複数行モード(^ と $ が行頭・行末に一致)| |s|単一行モード(. が改行にも一致)| |g|1件目で止めず全件を対象にする| |i|大文字と小文字を区別しない| |m|^ と $ が行頭と行末にも一致する| |s|. が改行にも一致する| - UTF-8解釈は常に有効 - 設定定数 PKRIPT_REGEX が 1 の場合にリテラルとして解釈される UTF-8としての解釈は常に有効(PCREの u 修飾子が必ず付く)。これが無いと . が1文字の途中に一致してしまうため、切り替えられない。 *** バックトラック制限 [#z4e998f6] JavaScript の y(sticky)は、対応する lastIndex を持たないため用意していない。 ReDoS(破滅的バックトラック)を防止するため、1回の照合で許されるバックトラック量は PKRIPT_REGEX_BACKTRACK(既定 100,000)に制限されている。 上限を超えた照合はエラーとなり、try / catch で捕捉することはできない。 *** メソッド [#pbdd2da1] |~呼び出し|~戻り値|h |re.test(str)|一致すれば true| |re.exec(str)|最初の一致の配列(全体, グループ1, ...)。無ければ null| |re.source() / re.flags() / re.global()|書いたパターン / フラグ / g の有無| |str.match(re)|g 無し: 最初の一致の配列。g 有り: 一致した文字列すべて。無ければ null| |str.matchAll(re)|一致ごとの配列の配列。無ければ空配列| |str.search(re)|最初の一致の位置(文字数)。無ければ -1| |str.replace(re, to)|g 有りなら全件、無ければ1件を置換| |str.replaceAll(re, to)|g の有無によらず全件を置換| |str.split(re)|一致で分割した配列| ** 使用法 [#g9b3b28a] match が g の有無で形を変えるのは JavaScript と同じ挙動に合わせたもの。グループが要る場合は matchAll か exec を使う。 *** 文字列の置換を行う [#q5bd2207] #highlight{{ const m = "2024-05-06".match(/(\d+)-(\d+)/); // m は ["2024-05", "2024", "05"] #pkript(highlight){{ const all = "a1 b2".matchAll(/([a-z])(\d)/g); // all は [["a1","a","1"], ["b2","b","2"]] }} **** 置換文字列 [#replacement] |~書き方|~意味|h |$1 〜 $99|グループ| |${1}|番号の切れ目を明示したグループ。"${1}0" のように直後に数字を置ける| |$&|一致した全体| |$$|$ そのもの| *** 使えない書き方 [#ke56d0f0] |~書き方|~理由|h |(?R) (?0) (?1) (?&name)|再帰とサブルーチン呼び出し。照合ではなく実行になるため| |(?C...)|コールアウト| |\K \C|一致開始位置やバイト単位の操作。位置の報告が嘘になるため| グループとして書けるのは (...) (?:...) (?=...) (?!...) (?<=...) (?<!...) (?<名前>...) とインラインフラグ (?i) だけ。ホワイトリストなので、PCREが今後増やす構文も既定では通らない。 *** 暴走の止め方 [#vfea6d87] 正規表現は、スクリプトが「そこに辿り着くまでのステップ数」とまったく無関係な時間を使える唯一の場所。/(a+)+$/ のようなパターンは1回の照合の中で何分でも走り得るし、その間はステップ数も実行時間も数える機会がない。 そのためすべての照合をPCREのバックトラック上限の下で走らせる(PKRIPT_REGEX_BACKTRACK、既定100,000。PHPの既定値は1,000,000)。 上限に達した照合は「一致しなかった」ではなくリソース上限エラーになる。false を返すとスクリプトに「一致しなかった」と嘘をついたことになり、ページの内容が黙って変わってしまうため。したがって try / catch でも捕まえられない。 パターンの検査は解析時に行う。書き方の誤りや使えない構文は実行前にエラーになり、ASTキャッシュには検査を通ったパターンしか載らない。パターンの長さは PKRIPT_MAX_REGEX(既定512バイト)まで。 *** / の解釈 [#a6e4513d] JSXの < と同じ規則。式が始まれる位置の / だけが正規表現になる。 #highlight{{ let a = 10 / 2; // 除算 return /a+/.test(s); // 正規表現(return の直後) const r = /a+/; // 正規表現(= の直後) return f(10) / 2; // 除算() の直後) }} 識別子、数値、)、] の直後の / は除算のままなので、既存のスクリプトの意味は変わらない。// と /* はどの位置でもコメントとして先に処理される。 ** 使用法 [#d4d55f09] *** 文字列の置換を行う [#usage_replace] #highlight{{ const text = "2026-08-31"; const formatted = text.replace(/(\d{4})-(\d{2})-(\d{2})/, "$1年$2月$3日"); return "<p>" + formatted + "</p>"; return "<p>" + text.replace(/(\d{4})-(\d{2})-(\d{2})/, "$1年$2月$3日") + "</p>"; }} *** 入力形式を検証する [#usage_test] #highlight{{ const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/; if (!ISO_DATE.test(e.args[0])) return "<p>日付の形式が正しくありません</p>"; }} *** 全件マッチを取り出す [#usage_matchall] #highlight{{ const LINK = /\[\[(.+?)\]\]/g; const names = text.matchAll(LINK).map((m) => m[1]); }}