Skip to content

Instantly share code, notes, and snippets.

@agwlvssainokuni
Created May 9, 2025 12:34
Show Gist options
  • Save agwlvssainokuni/01eb50975ab46d78e090806e4cf08539 to your computer and use it in GitHub Desktop.
Save agwlvssainokuni/01eb50975ab46d78e090806e4cf08539 to your computer and use it in GitHub Desktop.
expected の特殊文字をエスケープして正規表現として actual と照合する。
String expected = "....";
String actual = "....";
String REGEXP_METACHAR_PATTERN = "[.?*+^$|\\\\(){}\\[\\]]";
Matcher matcher = Pattern.compile(REGEXP_METACHAR_PATTERN).matcher(expected);
boolean result = Pattern.matches(
"^" + matcher.replaceAll(mr -> "\\\\\\" + mr.group()) + "$",
actual
);
@agwlvssainokuni
Copy link
Author

"\\\\\\" の背景

  • もともとやりたいのは「特殊文字(1文字)に「\」(1文字)をつけてエスケープする」こと。
  • Javaの文字列リテラルで「\」(1文字)は "\\"(1文字のエスケープ文字「\」と1文字のエスケープ対象「\」)で表される。
  • シンプルに考えると「mr -> "\\" + mr.group()」で特殊文字の前に「\」(1文字)が付くように見える。
  • しかし、ここで Matcher#replaceAll() のAPI仕様が邪魔をする。
    • https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/util/regex/Matcher.html#replaceAll(java.util.function.Function)
    • Note that backslashes (\) and dollar signs ($) in a replacement string may cause the results to be different than if it were being treated as a literal replacement string. Dollar signs may be treated as references to captured subsequences as described above, and backslashes are used to escape literal characters in the replacement string.
    • (MS Copilot訳) 置換文字列内のバックスラッシュ (\) やドル記号 ($) は、リテラルな置換文字列として扱われる場合と異なる結果を引き起こす可能性があります。ドル記号は、上記で説明したように捕捉された部分文字列への参照として扱われる場合があり、バックスラッシュは置換文字列内のリテラル文字をエスケープするために使用されます。
  • 少し分かりにくいかも知れないが、置換文字列 (= replaceAll() の引数の Function の返却値) のバックスラッシュ (\) はエスケープ文字として扱われ、ドル記号 ($) は正規表現でマッチしたグループを参照するものとして扱われる。
  • 即ち、「mr -> "\\" + mr.group()」だと Function は「\+特殊文字(1文字)」を返却するが、replaceAll() の仕様により「エスケープ文字(\)+特殊文字(1文字) => 特殊文字(1文字)」となって置換される。(特殊文字(1文字)を特殊文字(1文字)で置換することになるので結果としては元と同じ文字列が返却される)
  • この仕様を踏まえ「mr -> "\\\\\\" + mr.group()」とする。この Function は「\\\(3文字)+特殊文字(1文字)」を返却する。こうすると replaceAll() の仕様により前半は「 \\(2文字) = エスケープ文字(1文字)+エスケープ対象(\ 1文字) => \(1文字)」、後半は「\(1文字)+特殊文字(1文字) = エスケープ文字(1文字)+特殊文字(1文字) => 特殊文字(1文字)」となり、期待する「\(1文字)+特殊文字(1文字)」に置換される。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment