WikiChree.COM 新規   編集   添付   管理  



トップ   編集 編集〔GUI〕 差分 履歴 添付 複製 名前変更 リロード   新規 一覧 検索 最終更新   ヘルプ   最終更新のRSS   ログイン

Pkript/文法/正規表現 の変更点

#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" のように直後に数字を置ける|
|$&amp;|一致した全体|
|$$|$ そのもの|

*** 使えない書き方 [#ke56d0f0]

|~書き方|~理由|h
|(?R) (?0) (?1) (?&amp;name)|再帰とサブルーチン呼び出し。照合ではなく実行になるため|
|(?C...)|コールアウト|
|\K \C|一致開始位置やバイト単位の操作。位置の報告が嘘になるため|

グループとして書けるのは (...) (?:...) (?=...) (?!...) (?&lt;=...) (?&lt;!...) (?&lt;名前&gt;...) とインラインフラグ (?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の &lt; と同じ規則。式が始まれる位置の / だけが正規表現になる。

#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]);
}}