前提・実現したいこと
PHP正規表現で「」の内外で文字列を分別したいです。
例えば$str = "太郎「よろしくね」"
が与えられれば、["name"=>"太郎","comment"=>"よろしくね"]
に分別するというものです。
与えられる値と、目的の値
次の$ary
が与えられます。
右のコメントアウトの["name"=>"●●","comment"=>●●]
が分別された目的の値です
php
1$ary = [ 2 3 /* 4 * 正しい値が与えられた場合、一方か両方に値が入る 5 *----------------------------------------------*/ 6 7 // 以下のように「」の内外で name と comment に分別する( 尚、「」はなくても「」だけでもOK ) 8 '一郎「こんにちは」', // ["name"=>"一郎","comment"=>"こんにちは"] 9 '二郎「男の中の「男」です」', // ["name"=>"二郎","comment"=>"男の中の「男」です"] 10 '三郎', // ["name"=>"三郎","comment"=>"null"] 11 '「こんばんは」', // ["name"=>"null","comment"=>"こんばんは"] 12 13 /* 14 * 不正な値が与えられた場合、いずれも null を返す 15 *----------------------------------------------*/ 16 17 //(1)以下のように「」の後に文字がある場合 null とする 18 '「こんばんは」です', // ["name"=>"null","comment"=>"null"] 19 20 //(2)以下のように「」がセットじゃない場合 null とする 21 '五郎「男の中の「男」かも', // ["name"=>"null","comment"=>"null"] 22 '「男の中の「男」かも', // 同上 23 '「やばい', // 同上 24 'ねむい」', // 同上 25 '「', // 同上 26 '」', // 同上 27 '「「」', // 同上 28 29 //(3)以下のように記号だけの場合 null とする 30 '「」', // ["name"=>"null","comment"=>"null"] 31 '「「」」', // 同上 32 '「★」', // 同上 33 '「!!」', // 同上 34]; 35 36// 分別を実行 37foreach( $ary as $str ){ 38 sort_kakko( hoge($str) ); 39}
発生している問題
正規表現で躓いています。
該当のソースコード
作ったのはsort_kakko()
という分別の関数です。
まず正しい値を判定し、続いて不正な3つのパターンである上記(1)(2)(3)を判定する流れで書きました。
ですが早速、正しい値の判定の正規表現で躓いています。
php
1// 分別を実行 2function sort_kakko($str){ 3 4 // 正しい値の判定 5 preg_match('/^(.+)?「?(.+)?」?$/', $str, $matches); 6 $name = $matches[1] ?? null; 7 $comment = $matches[2] ?? null; 8 9 // 不正な値(1)の判定 10 $ok_1 = true; 11 if ( mb_substr_count($str,'」') === 1 && substr($str, -1) !== '」' ) { // 」があるのに最後が」じゃない場合 12 $ok_1 = false; 13 } 14 15 // 不正な値(2)の判定 16 $ok_2 = true; 17 if ( mb_substr_count($str,'「') === 1 || mb_substr_count($str,'」') === 1 ) { //「」のようにセットじゃない場合 18 $ok_2 = false; 19 } 20 21 // 不正な値(3)の判定 22 $ok_3 = true; 23 if ( kigo_remove($str) === '' ) { // すべての記号を除去した結果、空文字だった場合 24 $ok_3 = false; 25 } 26 27 // (1)(2)(3)どれかが不正なら null とする 28 if ( !$ok_1 || !$ok_2 || !$ok_3 ) { 29 $name = null; 30 $comment = null; 31 } 32 33 return ["name"=>$name,"comment"=>$comment]; 34} 35 36// すべての記号を除去する 37function kigo_remove($str){ 38 return preg_replace('/[^ぁ-んァ-ンーa-zA-Z0-9一-0-9\-\r]+/u',"" ,$str); 39}
試したこと
最初の正規表現のみで試してみました。
php
1// 分別 2function sort_kakko($str){ 3 4 // 正しい値の判定 5 preg_match('/^(.+)?「(.+)?」$/', $str, $matches); 6 $name = $matches[1] ?? null; 7 $comment = $matches[2] ?? null; 8 9 return ["name"=>$name,"comment"=>$comment]; 10}
まず「「」」
と「★」
と「!!」
ができていませんが、これらは上記$ok_3
で除外できるから問題ありません。
しかし三郎
ができない理由がわかりません。
自分の理解では上記/^(.+)?「(.+)?」$/
という正規表現は
^(.+)?
→ 先頭の(.+)
はなくてもよくて、あれば$matches
に取得する
「?(.+)?」?
→ 「」
はなくてもよくて、(.+)
もなくてもよくて、あれば$matches
に取得する
というもので、なぜ三郎
の"name"
がnull
になってしまうのか不明です。
これについては/^(.+)?「?(.+)?」?$/
のように、かっこの後にも?
をつけるなど試してみたものの分別は実現できませんでした。
そして二郎「男の中の「男」です」
も分別できず、きっと「一番外側のかっこを基準とする」という正規表現が必要そうに思えます。
これについてはteratailの質問で類似のものを見かけたので応用して/[[^「\」]*(?:[「^\」]*][^[]]*)*]/
のように試したものの、やはり分別は実現できませんでした。
回答2件
あなたの回答
tips
プレビュー
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2021/12/16 22:00
2021/12/16 22:05
2021/12/17 06:49 編集