- ベストアンサー
PHPで、"内は分割せずに、配列に分割する方法はありませんか?
explodeや正規表現など、いろいろ考えたのですが、スマートな方法が見つからず、ヒントでもいただけるとありがたいです。 やりたいことは、 ABC 123 "BBB HHH" 456 789 "あい うえお" DDD という文字列を分割して配列に入れたいのですが、 "内は1つの文字列として分割せずに取り出したいのです。 結果として、 array( 0=>"ABC", 1=>123, 2=>"BBB HHH",// 元の"があってもなくてもいい 3=>456, 4=>789 5=>"あい うえお", 6=>DDD ) というものを得たいわけです。 単純に explode や split ではダメですし、正規表現だとどうなるのやらと。 "内の (スペース)を他のモノに置き換えて、explodeした後もとにもどす、とかでしょうか。他に何か手がありましたら、ご教示お願いします。
- みんなの回答 (9)
- 専門家の回答
質問者が選んだベストアンサー
'\' で '"' をエスケープできるパターンだとこんな感じですか。 '\' をパターンに含めるのが面倒w あと微妙に動作が違います。 <?php $string ='ABC 123 "BBB HHH" 456 789 "あい \\"うえお\\"" DDD'; print("$string ->\n"); preg_match_all('/[^ "]+|"[^\\\\"]*(?:\\\\"|[^"])*"/', $string, $items, PREG_SET_ORDER); print_r($items); ABC 123 "BBB HHH" 456 789 "あい \"うえお\"" DDD -> Array ( [0] => Array ( [0] => ABC ) [1] => Array ( [0] => 123 ) [2] => Array ( [0] => "BBB HHH" ) [3] => Array ( [0] => 456 ) [4] => Array ( [0] => 789 ) [5] => Array ( [0] => "あい \"うえお\"" ) [6] => Array ( [0] => DDD ) ) #str_getcsv() ですか。なんでもあるなPHP。
その他の回答 (8)
- sakusaker7
- ベストアンサー率62% (800/1280)
#6のお礼でのご質問に対して。 >>あと微妙に動作が違います。 >というのはどういう意味なんでしょうか? #2 のパターンだと、"" というダブルクォートのみのパターンは受け付けませんが #6のだと受け付けてしまいます。 マッチングがとんでもなく遅くなる可能性があるけど#2と同じ動作をするものにするか 速度を取るかで後者を選んだという次第です。はい。
お礼
なるほど。 AAA BBB "" "CCC DDD" の場合 0=>AAA 1=>BBB 2=>"" 3=>"CCC DDD" になるっていうことなんですね。そのあたりは、もう1処理加えて、空欄は省けば問題なさそうですね。 みなさまに教えていただいた方法をそれぞれ比較してみて利用させていただこうと思います。 貴重な時間を割いてお答えいただいてありがとうございました。
- yambejp
- ベストアンサー率51% (3827/7415)
おっとしまった、読み書きで開いて先頭にseekすればよかったですね <?php $string ='ABC 123 "BBB HHH" 456 789 "あい \"うえお\"" DDD'; $tmpfname = tempnam("/tmp", "FOO"); $handle = fopen($tmpfname, "r+"); fwrite($handle, $string); rewind($handle); while (($data = fgetcsv($handle, 1000, " ")) !== FALSE) { print_r($data); } fclose($handle); if(file_exists($tmpfname)) unlink($tmpfname); ?>
お礼
解答いただきありがとうございます。 たしかに、すでに実装済みのCSV関係関数を使うためにファイルに読み書きするのも手ですね。処理的にちょっとかかりそうな気もしないでもないですが、そのへん少し比較してみたいと思います。 1つの目的のためにも、いろんなアプローチがあるものですね。おもしろい。
- yambejp
- ベストアンサー率51% (3827/7415)
姑息かもしれませんが、一度テンポラリに書き出してしまうのも手です。 <?php $string ='ABC 123 "BBB HHH" 456 789 "あい \"うえお\"" DDD'; $tmpfname = tempnam("/tmp", "FOO"); $handle = fopen($tmpfname, "w"); fwrite($handle, $string); fclose($handle); $handle = fopen($tmpfname, "r"); while (($data = fgetcsv($handle, 1000, " ")) !== FALSE) { print_r($data); } fclose($handle); if(file_exists($tmpfname)) unlink($tmpfname); ?>
- little-m
- ベストアンサー率44% (45/102)
使用した事は有りませんが、以下の関数の delimiter をスペースにする事で出来るかもしれません。 str_getcsv() http://jp.php.net/manual/ja/function.str-getcsv.php
お礼
解答いただきありがとうございます。 そんな関数もあるんですね。あまり情報がないようですが、私の手元(PHP5.2.5 XAMPP版)ではまだ実装されてないようでした。5.3、6.0系で加わるのかもしれませんね。これが実装されればベストの解になりそうですね。 ありがとうございます。
- masa6272
- ベストアンサー率66% (93/140)
<?php $str='ABC 123 "BBB HHH" 456 789 "あい うえお" DDD'; preg_match_all('/"[^"]+"|\s?[^"\s]+\s?/', $str, $res); $res0 = array_map(trim, $res[0]); var_dump($res0); ?> $res0に解が求まります。 これで、どうでしょう?
お礼
解答いただきありがとうございます。 '"'内でエスケープされたもの(\")でも区切られてしまいました。 少し手直しすればいけそうですね。 ありがとうございます。
- 405
- ベストアンサー率50% (17/34)
explodeを2段で実行するとかどうでしょうか? <?php $str='ABC 123 "BBB HHH" 456 789 "あい うえお" DDD'; $words=array(); $temp=explode('"',$str); foreach($temp AS $i => $word) { if($i&1) $words[]=$word; else $words=array_merge($words,explode(" ",trim($word))); } print_r($words); ?>
お礼
解答いただきありがとうございます。 なるほど、なかなかおもしろいですね。参考にさせていただきたいと思います。
- sakusaker7
- ベストアンサー率62% (800/1280)
ダブルクォートはアイテムを区切る以外に使わないという前提で。 <?php $string ='ABC 123 "BBB HHH" 456 789 "あい うえお" DDD'; preg_match_all('/[^ "]+|"[^"]+"/', $string, $items, PREG_SET_ORDER); print_r($items); Array ( [0] => Array ( [0] => ABC ) [1] => Array ( [0] => 123 ) [2] => Array ( [0] => "BBB HHH" ) [3] => Array ( [0] => 456 ) [4] => Array ( [0] => 789 ) [5] => Array ( [0] => "あい うえお" ) [6] => Array ( [0] => DDD ) ) CSVのダブルクォートみたいに、エスケープすれば含められるという条件だと 要手直し。
お礼
解答いただきありがとうございます。 おお、まさにスマートなやりかたですね。 なるほど、区切りに注目するのではなく、値を抜き出す、ということですね。 エスケープがある場合にも少し正規表現を変えるだけで対応できそうですね。 ありがとうございます。
補足
いろいろ試行してみましたが、エスケープされたものを含めるのは難しいですね。 正規表現後半の部分で [^"] の部分を すべてOKだけど "のまえには \ がこなければならない、という感じでしょうか。 (?!) や (?=) など駆使しながら試してみましたが、なかなかうまくいかず。 もし暇だったらでけっこうですのでヒントいただけると幸いです。
- SAYKA
- ベストアンサー率34% (944/2776)
すぐに浮かぶ手抜きなやりかた ・「"」で囲まれている単語の空白を通常使わない何かの記号に置き換える ・split ・置き換えた物を再び空白に戻す。ついでに「"」を取る 「通常使わない何かの記号」をどうするかが一番の問題。 使用用途によっては「使ってはならない記号類」というのが出てくるだろうからそれを使ったら良いんじゃないかな。
お礼
解答いただきありがとうございます。 おっしゃるように、手順を踏めばできそうですね。 なかなか1発でするのは難しいですよね。 ただいろんなオプションがあるのはいいことです。参考にしたいと思います。
お礼
sakusaker7様、ひきつづき解答いただきありがとうございます。 すごい。パーフェクトですね。'"'の内部のエスケープされたものも問題なく取り出してくれますね。 >あと微妙に動作が違います。 というのはどういう意味なんでしょうか? それにしても正規表現って難しい。まだまだ勉強が足りませんね。 大変勉強になりました。 ありがとうございます。