できない.dev

正規表現で大文字小文字を区別せずに一致させるには

正規表現の照合で大文字小文字の違いを無視する基本形を各言語で示す。
フラグ引数で渡す方法とパターンの中に (?i) を書き込む方法の 2 通りがあり、どちらが使えるかとフラグの効く範囲が言語ごとに違うので、その違いまで含めて扱う。

公開:

各言語見出しの横のバッジは検証状態を表す。実行確認済みはコードを実際に実行して確認したもの、静的確認は構文と公式 API ドキュメントで確認したものである。

Python 実行確認済み

import re
 
text = "Error: FILE Not Found"
 
print(bool(re.search(r"file", text, re.IGNORECASE)))   # True
print(re.findall(r"(?i)not\s+found", text))             # ['Not Found']
 
pat = re.compile(r"error", re.I)
print(pat.sub("WARN", text))                            # WARN: FILE Not Found

re.IGNORECASE(短縮形は re.I)を関数の flags 引数か re.compile に渡すと照合全体で大文字小文字を無視する。
パターン先頭に (?i) と書いても同じだが、こちらはパターン文字列だけで完結するので、フラグを渡せない API に文字列を渡すときに使える。

JavaScript 実行確認済み

const text = "Error: FILE Not Found";
 
console.log(/file/i.test(text));                 // true
console.log(text.match(/not\s+found/i)?.[0]);     // Not Found
 
const pat = new RegExp("error", "i");
console.log(text.replace(pat, "WARN"));           // WARN: FILE Not Found

リテラルなら閉じスラッシュの後ろに i フラグを付け、文字列からつくるなら new RegExp の第 2 引数に "i" を渡す。
JavaScript は (?i) のインラインフラグに対応しておらず、パターンに書くと構文エラーになるのでフラグ側で指定する。

TypeScript 実行確認済み

const text: string = "Error: FILE Not Found";
 
const re = /file/i;
console.log(re.test(text));                       // true
 
const m: RegExpMatchArray | null = text.match(/not\s+found/i);
console.log(m?.[0]);                              // Not Found

書き方は JavaScript と同じで、i フラグはリテラルか new RegExp の第 2 引数で指定する。
String.prototype.match は一致しなければ null を返す型なので、結果をそのまま添字で読まずオプショナルチェーンか null 判定で絞り込む。

Go 静的確認

package main
 
import (
	"fmt"
	"regexp"
)
 
func main() {
	text := "Error: FILE Not Found"
 
	re := regexp.MustCompile(`(?i)file`)
	fmt.Println(re.MatchString(text)) // true
 
	fmt.Println(regexp.MustCompile(`(?i)not\s+found`).FindString(text)) // Not Found
}

Go の regexp にはフラグを渡す引数が無いため、パターンの先頭に (?i) と書いて指定する。
(?i:...) の形にすればグループの内側だけに効かせられるので、一部だけ大文字小文字を無視したいときはそちらを使う。

Rust 静的確認

use regex::{Regex, RegexBuilder};
 
fn main() {
    let text = "Error: FILE Not Found";
 
    let re = Regex::new(r"(?i)file").unwrap();
    println!("{}", re.is_match(text)); // true
 
    let re2 = RegexBuilder::new(r"not\s+found")
        .case_insensitive(true)
        .build()
        .unwrap();
    println!("{:?}", re2.find(text).map(|m| m.as_str())); // Some("Not Found")
}

regex クレートはパターン内の (?i) と RegexBuilder の case_insensitive(true) の両方に対応しており、どちらでも同じ結果になる。
パターンを設定ファイルなどから受け取る場合はビルダー側で指定すると、利用者が書いたパターンを書き換えずに済む。

Java 実行確認済み

import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class Main {
    public static void main(String[] args) {
        String text = "Error: FILE Not Found";
 
        Pattern p = Pattern.compile("file", Pattern.CASE_INSENSITIVE);
        System.out.println(p.matcher(text).find());              // true
 
        Matcher m = Pattern.compile("(?i)not\\s+found").matcher(text);
        System.out.println(m.find() ? m.group() : "no match");   // Not Found
    }
}

Pattern.compile の第 2 引数に Pattern.CASE_INSENSITIVE を渡すか、パターンの先頭に (?i) と書く。
CASE_INSENSITIVE だけでは ASCII の範囲しか対象にならないため、非 ASCII の文字も無視したいときは Pattern.UNICODE_CASE を併せて指定する。

C# 実行確認済み

using System.Text.RegularExpressions;
 
string text = "Error: FILE Not Found";
 
Console.WriteLine(Regex.IsMatch(text, "file", RegexOptions.IgnoreCase));   // True
 
Match m = Regex.Match(text, @"(?i)not\s+found");
Console.WriteLine(m.Success ? m.Value : "no match");                      // Not Found

RegexOptions.IgnoreCase を引数に渡すか、パターンの先頭に (?i) と書く。
IgnoreCase は現在のカルチャの規則で比較するため、ロケールに左右されたくない処理では RegexOptions.CultureInvariant を併せて指定する。

つまずき

指定の仕方は「フラグ引数」と「パターン内の (?i)」の 2 系統があり、言語によって使える方が違う。
JavaScript と TypeScript は (?i) に対応しておらず、パターンに書くと構文エラーになるのでフラグで指定するしかない。
逆に Go にはフラグ引数が無いので (?i) で書く。
Python・Java・C#・Rust は両方使えるため、パターン文字列を外部から受け取るかどうかで選べばよい。

フラグが効く範囲

フラグ引数で渡した指定はそのパターン全体に効く。
一方 (?i) はパターンの途中に書くとそこから後ろだけに効き、(?i:abc) のようにグループの形にすればその中だけに効く。
「ファイル名は大文字小文字を無視したいが拡張子は厳密に見たい」といった混在した条件は、この範囲指定を使うと 1 本のパターンで書ける。

ASCII の外では既定が異なる

大文字小文字の対応付けは言語ごとに既定の範囲が違う。
Java の CASE_INSENSITIVE は ASCII の範囲しか見ないので、非 ASCII の文字も対象にするには UNICODE_CASE を足す。
C# の IgnoreCase は現在のカルチャに従うため、トルコ語ロケールの i と I のように環境で結果が変わりうる場面では CultureInvariant を併せて指定する。
英数字だけを扱っているうちは差が出ないが、ユーザー入力を照合するなら先に確認しておきたい。

この記事は役立ちましたか?