[PHP]preg_split関数の使い方を完全解説!正規表現で文字列を分割する方法

PHP

はじめに

PHPで文字列を分割する際、explode関数では対応できない複雑なパターンに遭遇することがありますよね。そんな時に活躍するのが**preg_split関数**です。

正規表現を使って柔軟に文字列を分割できるこの関数は、カンマとスペースの混在、複数の区切り文字、可変長の空白など、複雑な分割処理を簡単に実現できます。

この記事では、preg_splitの基本から実践的なテクニックまで、豊富なサンプルコードとともに詳しく解説します。

preg_splitとは?

preg_splitは、正規表現パターンにマッチする部分を区切り文字として文字列を分割する関数です。explode関数の正規表現版と考えるとわかりやすいでしょう。

基本構文

preg_split(
    string $pattern,
    string $subject,
    int $limit = -1,
    int $flags = 0
): array|false

パラメータ

  • $pattern: 区切り文字として使用する正規表現パターン
  • $subject: 分割する文字列
  • $limit: 返す要素の最大数(デフォルト: -1=無制限)
  • $flags: 動作を変更するフラグ

戻り値

  • 成功時: 分割された文字列の配列
  • 失敗時: false

基本的な使い方

シンプルな分割

<?php
// カンマで分割
$text = "りんご,バナナ,オレンジ";
$fruits = preg_split('/,/', $text);
print_r($fruits);
// Array ( [0] => りんご [1] => バナナ [2] => オレンジ )

// 空白文字で分割(スペース、タブ、改行など)
$text = "Hello   World\tPHP";
$words = preg_split('/\s+/', $text);
print_r($words);
// Array ( [0] => Hello [1] => World [2] => PHP )
?>

explodeとの違い

<?php
$text = "apple, banana,  orange,melon";

// explodeの場合:スペースの扱いが難しい
$result1 = explode(',', $text);
print_r($result1);
// Array ( [0] => apple [1] =>  banana [2] =>   orange [3] => melon )
// スペースが残る!

// preg_splitの場合:カンマと空白をまとめて処理
$result2 = preg_split('/,\s*/', $text);
print_r($result2);
// Array ( [0] => apple [1] => banana [2] => orange [3] => melon )
// きれいに分割できる!
?>

実践的な使用例

例1: 複数の区切り文字で分割

<?php
// カンマ、セミコロン、パイプのいずれかで分割
$data = "東京,大阪;福岡|札幌,名古屋";
$cities = preg_split('/[,;|]/', $data);
print_r($cities);
// Array ( [0] => 東京 [1] => 大阪 [2] => 福岡 [3] => 札幌 [4] => 名古屋 )
?>

例2: 数字と文字の境界で分割

<?php
$code = "ABC123DEF456GHI789";
$parts = preg_split('/(?=[0-9])|(?<=[0-9])(?=[A-Z])/', $code);
print_r($parts);
// Array ( [0] => ABC [1] => 123 [2] => DEF [3] => 456 [4] => GHI [5] => 789 )
?>

例3: 改行コードの統一処理

<?php
// Windows、Mac、Unix の改行コードすべてに対応
$text = "1行目\r\n2行目\n3行目\r4行目";
$lines = preg_split('/\r\n|\r|\n/', $text);
print_r($lines);
// Array ( [0] => 1行目 [1] => 2行目 [2] => 3行目 [3] => 4行目 )
?>

例4: HTMLタグで分割

<?php
$html = "テキスト<br>改行<br/>また改行<p>段落</p>終わり";
$parts = preg_split('/<[^>]+>/', $html);
print_r($parts);
// Array ( [0] => テキスト [1] => 改行 [2] => また改行 [3] => 段落 [4] => 終わり )
?>

例5: CSVの高度な解析

<?php
// 引用符で囲まれた値内のカンマは無視
$csv = 'name,age,"Tokyo, Japan",notes';

// 引用符外のカンマで分割
$fields = preg_split('/,(?=(?:[^"]*"[^"]*")*[^"]*$)/', $csv);
print_r($fields);
// Array ( [0] => name [1] => age [2] => "Tokyo, Japan" [3] => notes )
?>

フラグの活用

PREG_SPLIT_NO_EMPTY

空の要素を除外します。

<?php
$text = "apple,,banana,,,orange";

// フラグなし
$result1 = preg_split('/,/', $text);
print_r($result1);
// Array ( [0] => apple [1] => [2] => banana [3] => [4] => [5] => orange )

// PREG_SPLIT_NO_EMPTYを使用
$result2 = preg_split('/,/', $text, -1, PREG_SPLIT_NO_EMPTY);
print_r($result2);
// Array ( [0] => apple [1] => banana [2] => orange )
?>

PREG_SPLIT_DELIM_CAPTURE

区切り文字もキャプチャして返します。

<?php
$text = "2024年10月29日";

// 区切り文字をキャプチャ
$parts = preg_split('/([年月日])/', $text, -1, PREG_SPLIT_DELIM_CAPTURE);
print_r($parts);
// Array ( [0] => 2024 [1] => 年 [2] => 10 [3] => 月 [4] => 29 [5] => 日 [6] => )
?>

PREG_SPLIT_OFFSET_CAPTURE

各要素の元の位置(オフセット)も返します。

<?php
$text = "Hello World PHP";
$parts = preg_split('/\s+/', $text, -1, PREG_SPLIT_OFFSET_CAPTURE);
print_r($parts);
// Array (
//     [0] => Array ( [0] => Hello [1] => 0 )
//     [1] => Array ( [0] => World [1] => 6 )
//     [2] => Array ( [0] => PHP [1] => 12 )
// )
?>

フラグの組み合わせ

<?php
$text = "apple, , banana,  ,orange";

// 複数フラグを組み合わせ
$parts = preg_split(
    '/,\s*/', 
    $text, 
    -1, 
    PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE
);
print_r($parts);
?>

分割数の制限

limitパラメータの使用

<?php
$text = "one,two,three,four,five";

// 最大3つに分割
$parts = preg_split('/,/', $text, 3);
print_r($parts);
// Array ( [0] => one [1] => two [2] => three,four,five )
// 残りはすべて最後の要素に含まれる

// 最初の2つだけ取得
$parts = preg_split('/,/', $text, 2);
print_r($parts);
// Array ( [0] => one [1] => two,three,four,five )
?>

実用的なパターン集

パターン1: メールアドレスの分割

<?php
function parseEmail($email) {
    $parts = preg_split('/@/', $email, 2);
    return [
        'local' => $parts[0],
        'domain' => $parts[1] ?? ''
    ];
}

$info = parseEmail('user@example.com');
print_r($info);
// Array ( [local] => user [domain] => example.com )
?>

パターン2: パスの分割

<?php
// WindowsとUnix両対応
$path = "/var/www/html/index.php";
$parts = preg_split('#[/\\\\]#', $path, -1, PREG_SPLIT_NO_EMPTY);
print_r($parts);
// Array ( [0] => var [1] => www [2] => html [3] => index.php )
?>

パターン3: キャメルケースの分割

<?php
$camelCase = "getUserNameById";
$words = preg_split('/(?=[A-Z])/', $camelCase, -1, PREG_SPLIT_NO_EMPTY);
print_r($words);
// Array ( [0] => get [1] => User [2] => Name [3] => By [4] => Id )

// 小文字に変換
$words = array_map('strtolower', $words);
$snakeCase = implode('_', $words);
echo $snakeCase;  // get_user_name_by_id
?>

パターン4: 複雑な区切りパターン

<?php
// AND、OR、カンマで区切られた検索条件の解析
$query = "apple AND banana OR orange, melon";
$terms = preg_split('/\s+(?:AND|OR)\s+|,\s*/', $query);
print_r($terms);
// Array ( [0] => apple [1] => banana [2] => orange [3] => melon )
?>

パターン5: 日本語の文章を文単位で分割

<?php
$text = "これは最初の文です。これが2番目の文。そして最後の文!";
$sentences = preg_split('/[。.!?]+/', $text, -1, PREG_SPLIT_NO_EMPTY);
print_r($sentences);
// Array ( [0] => これは最初の文です [1] => これが2番目の文 [2] => そして最後の文 )
?>

よくあるエラーと対処法

エラー1: 正規表現の区切り文字の衝突

<?php
// 悪い例:スラッシュが正規表現区切り文字と衝突
// $parts = preg_split('/\//', $path);  // エラー!

// 良い例:別の区切り文字を使用
$parts = preg_split('#/#', $path);  // OK!

// または、エスケープする
$parts = preg_split('/\//', $path);  // OK!
?>

エラー2: マルチバイト文字の扱い

<?php
// UTF-8でのマルチバイト文字対応
$text = "こんにちは、世界!元気ですか?";
$parts = preg_split('/[、。!?]/u', $text, -1, PREG_SPLIT_NO_EMPTY);
//                              ↑ uフラグが重要!
print_r($parts);
?>

パフォーマンスの比較

explode vs preg_split

<?php
$text = "a,b,c,d,e,f,g,h,i,j";

// シンプルな分割ならexplodeの方が高速
$start = microtime(true);
for ($i = 0; $i < 10000; $i++) {
    $result = explode(',', $text);
}
echo "explode: " . (microtime(true) - $start) . "秒\n";

// 複雑なパターンならpreg_splitが必要
$start = microtime(true);
for ($i = 0; $i < 10000; $i++) {
    $result = preg_split('/,/', $text);
}
echo "preg_split: " . (microtime(true) - $start) . "秒\n";

// ポイント:単純な固定文字列ならexplodeを使う!
?>

ベストプラクティス

1. 適切な関数の選択

<?php
// 単純な分割 → explode
$simple = explode(',', 'a,b,c');

// 正規表現が必要 → preg_split
$complex = preg_split('/[,;]\s*/', 'a, b; c');
?>

2. エラーハンドリング

<?php
$text = "sample text";
$pattern = '/\s+/';

$result = preg_split($pattern, $text);

if ($result === false) {
    // 正規表現エラー
    $error = preg_last_error_msg();
    echo "エラー: {$error}";
} else {
    print_r($result);
}
?>

3. 可読性を重視

<?php
// 悪い例:複雑すぎる正規表現
$parts = preg_split('/(?:,\s*|\s+(?:and|or)\s+|[;|])+/i', $text);

// 良い例:段階的に処理
$temp = preg_replace('/\s+(?:and|or)\s+/i', ',', $text);
$temp = preg_replace('/[;|]/', ',', $temp);
$parts = preg_split('/,\s*/', $temp, -1, PREG_SPLIT_NO_EMPTY);
?>

実践:ログファイルの解析

<?php
function parseLogLine($line) {
    // Apache風のログ形式を解析
    // 127.0.0.1 - - [29/Oct/2024:10:00:00 +0900] "GET /index.php HTTP/1.1" 200 1234
    
    $pattern = '/\s+(?=(?:[^\[\]]*\[[^\[\]]*\])*[^\[\]]*$)(?=(?:[^"]*"[^"]*")*[^"]*$)/';
    $parts = preg_split($pattern, $line, -1, PREG_SPLIT_NO_EMPTY);
    
    return [
        'ip' => $parts[0] ?? '',
        'datetime' => trim($parts[3] ?? '', '[]'),
        'request' => trim($parts[4] ?? '', '"'),
        'status' => $parts[5] ?? '',
        'size' => $parts[6] ?? ''
    ];
}

$log = '127.0.0.1 - - [29/Oct/2024:10:00:00 +0900] "GET /index.php HTTP/1.1" 200 1234';
print_r(parseLogLine($log));
?>

まとめ

preg_splitは正規表現を使った柔軟な文字列分割を実現する強力な関数です。

主な特徴:

  • ✅ 複雑なパターンで文字列を分割できる
  • ✅ 複数の区切り文字に対応
  • ✅ フラグで動作をカスタマイズ可能
  • ✅ 分割数の制限やオフセット取得も可能

使い分けのポイント:

  • 単純な固定文字列の分割 → explode
  • 複雑なパターンや複数の区切り文字 → preg_split
  • 大量データの高速処理 → explode優先

複雑なテキスト処理が必要な場面では、preg_splitを活用して効率的なコードを書きましょう!

参考リンク


この記事が役に立ったら、ぜひシェアしてください!

タイトルとURLをコピーしました