できない.dev

正規表現で部分文字列を取り出すには

丸括弧で囲んだ部分(キャプチャグループ)を取り出す基本形を各言語で示す。
番号での参照と名前付きグループの両方を扱い、一致しなかったときの戻り値が言語ごとに違う点までを扱う。

公開:

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

Python 実行確認済み

import re
 
log = "2026-08-31 12:34:56 ERROR disk full"
m = re.search(r"(?P<date>\d{4}-\d{2}-\d{2}) (?P<time>[\d:]+) (?P<level>[A-Z]+)", log)
 
if m:
    print(m.group("date"))  # 2026-08-31
    print(m.group(3))       # ERROR(番号は 1 始まり、0 は一致全体)
    print(m.groupdict())    # {'date': ..., 'time': ..., 'level': 'ERROR'}
 
print(re.findall(r"\d+", log))  # ['2026', '08', '31', '12', '34', '56']

丸括弧で囲んだ部分が group(1) から順に取り出せ、group(0) は一致した全体になる。
(?P<name>...) と書くと名前でも引けるようになり、番号のずれに強くなる。
re.search は一致しないと None を返すので、if で受けてから group を呼ぶ。

JavaScript 実行確認済み

const log = "2026-08-31 12:34:56 ERROR disk full";
const m = log.match(/(?<date>\d{4}-\d{2}-\d{2}) (?<time>[\d:]+) (?<level>[A-Z]+)/);
 
if (m) {
  console.log(m.groups.date); // 2026-08-31
  console.log(m[3]); // ERROR(番号は 1 始まり、m[0] は一致全体)
}
 
// すべての一致を取り出すなら matchAll(g フラグが必須)
console.log([...log.matchAll(/\d+/g)].map((x) => x[0]));

match の戻り値は配列で、m[0] が一致全体、m[1] 以降がグループである。
(?<name>...) と書けば m.groups.name でも引ける。
全件を取り出すなら matchAll を使うが、こちらは g フラグを付けないと実行時エラーになる。

TypeScript 実行確認済み

const log: string = "2026-08-31 12:34:56 ERROR disk full";
const m: RegExpMatchArray | null = log.match(
  /(?<date>\d{4}-\d{2}-\d{2}) (?<time>[\d:]+) (?<level>[A-Z]+)/
);
 
// groups は string | undefined を持つ型なので、既定値を用意しておくと安全である
const date: string = m?.groups?.date ?? "unknown";
const level: string = m?.groups?.level ?? "unknown";
console.log(date, level); // 2026-08-31 ERROR

match の戻り値は RegExpMatchArray | null、groups は string | undefined を要素に持つ型なので、オプショナルチェーンと既定値を組み合わせると分岐を 1 行に収められる。
型の上ではグループが存在する保証は無いため、名前を書き間違えてもコンパイルは通る点に注意する。

Go 静的確認

package main
 
import (
	"fmt"
	"regexp"
)
 
func main() {
	log := "2026-08-31 12:34:56 ERROR disk full"
	re := regexp.MustCompile(`(?P<date>\d{4}-\d{2}-\d{2}) (?P<time>[\d:]+) (?P<level>[A-Z]+)`)
 
	m := re.FindStringSubmatch(log)
	if m == nil {
		return // 一致しなければ nil が返る
	}
	fmt.Println(m[0])                       // 一致した全体
	fmt.Println(m[1])                       // 2026-08-31
	fmt.Println(m[re.SubexpIndex("level")]) // 名前から添字を引く
}

FindStringSubmatch は一致全体を先頭に、以降へグループを並べたスライスを返す。
一致しなければ nil が返るので、添字でアクセスする前に確認する。
名前付きグループは SubexpIndex で添字に変換すると、パターンを直したときの取り違えを防げる。

Rust 静的確認

use regex::Regex;
 
fn main() {
    let log = "2026-08-31 12:34:56 ERROR disk full";
    let re = Regex::new(r"(?P<date>\d{4}-\d{2}-\d{2}) (?P<time>[\d:]+) (?P<level>[A-Z]+)").unwrap();
 
    if let Some(caps) = re.captures(log) {
        println!("{}", &caps["date"]); // 2026-08-31
        println!("{}", &caps[3]);      // ERROR(添字は 1 始まり、0 は一致全体)
    }
}

captures は Option<Captures> を返すので、if let で取り出してから添字を引く。
番号での添字と名前での添字のどちらも使えるが、存在しない添字はパニックになるため、パターンとコードを同時に直す。
値が無いかもしれないグループは get を使って Option で受ける。

Java 実行確認済み

import java.util.regex.Matcher;
import java.util.regex.Pattern;
 
public class RegexGroups {
    public static void main(String[] args) {
        String log = "2026-08-31 12:34:56 ERROR disk full";
        Pattern p = Pattern.compile("(?<date>\\d{4}-\\d{2}-\\d{2}) (?<time>[\\d:]+) (?<level>[A-Z]+)");
        Matcher m = p.matcher(log);
 
        if (m.find()) {                       // find() を呼ぶ前に group() を使うと例外になる
            System.out.println(m.group("date"));  // 2026-08-31
            System.out.println(m.group(3));       // ERROR
            System.out.println(m.group(0));       // 一致した全体
        }
    }
}

Matcher は find か matches を呼んで一致を確定させてから group を呼ぶ。
順番を逆にすると IllegalStateException になる。
(?<name>...) で付けた名前は group(String) で引け、番号のずれに強くなる。

C# 実行確認済み

using System.Text.RegularExpressions;
 
var log = "2026-08-31 12:34:56 ERROR disk full";
var m = Regex.Match(log, @"(?<date>\d{4}-\d{2}-\d{2}) (?<time>[\d:]+) (?<level>[A-Z]+)");
 
if (m.Success)
{
    Console.WriteLine(m.Groups["date"].Value); // 2026-08-31
    Console.WriteLine(m.Groups[3].Value);      // ERROR
}
 
// 存在しない名前でも例外にならず、空文字と Success=false が返る
Console.WriteLine(m.Groups["missing"].Success); // False

Match.Groups は名前でも番号でも引ける。
存在しない名前を渡しても例外にはならず、Success が false で Value が空文字のグループが返るため、必要なら Success を確認する。
Match.Success が false のときは全体として一致していない。

つまずき

グループの番号は丸括弧が現れた順に 1 から振られ、0 は一致した全体を指す。
パターンの途中に括弧を足すと以降の番号がすべてずれるため、番号での参照はパターンの変更に弱い。
長く保守するパターンでは名前付きグループにしておくと安全である。

一致しなかったときの形

戻り値の形は言語ごとに違う。
Python は None、JavaScript は null、Go は nil スライス、Rust は Option、Java は例外、C# は Success が false の Match を返す。
いずれも「取り出す前に確認する」のは同じで、確認を省くと入力が想定外だったときにその場で落ちる。

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