• ベストアンサー

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した後もとにもどす、とかでしょうか。他に何か手がありましたら、ご教示お願いします。

質問者が選んだベストアンサー

  • ベストアンサー
  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.6

'\' で '"' をエスケープできるパターンだとこんな感じですか。 '\' をパターンに含めるのが面倒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。

SHlVA
質問者

お礼

sakusaker7様、ひきつづき解答いただきありがとうございます。 すごい。パーフェクトですね。'"'の内部のエスケープされたものも問題なく取り出してくれますね。 >あと微妙に動作が違います。 というのはどういう意味なんでしょうか? それにしても正規表現って難しい。まだまだ勉強が足りませんね。 大変勉強になりました。 ありがとうございます。

その他の回答 (8)

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.9

#6のお礼でのご質問に対して。 >>あと微妙に動作が違います。 >というのはどういう意味なんでしょうか? #2 のパターンだと、"" というダブルクォートのみのパターンは受け付けませんが #6のだと受け付けてしまいます。 マッチングがとんでもなく遅くなる可能性があるけど#2と同じ動作をするものにするか 速度を取るかで後者を選んだという次第です。はい。

SHlVA
質問者

お礼

なるほど。 AAA BBB "" "CCC DDD" の場合 0=>AAA 1=>BBB 2=>"" 3=>"CCC DDD" になるっていうことなんですね。そのあたりは、もう1処理加えて、空欄は省けば問題なさそうですね。 みなさまに教えていただいた方法をそれぞれ比較してみて利用させていただこうと思います。 貴重な時間を割いてお答えいただいてありがとうございました。

  • yambejp
  • ベストアンサー率51% (3827/7415)
回答No.8

おっとしまった、読み書きで開いて先頭に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); ?>

SHlVA
質問者

お礼

解答いただきありがとうございます。 たしかに、すでに実装済みのCSV関係関数を使うためにファイルに読み書きするのも手ですね。処理的にちょっとかかりそうな気もしないでもないですが、そのへん少し比較してみたいと思います。 1つの目的のためにも、いろんなアプローチがあるものですね。おもしろい。

  • yambejp
  • ベストアンサー率51% (3827/7415)
回答No.7

姑息かもしれませんが、一度テンポラリに書き出してしまうのも手です。 <?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)
回答No.5

使用した事は有りませんが、以下の関数の delimiter をスペースにする事で出来るかもしれません。 str_getcsv() http://jp.php.net/manual/ja/function.str-getcsv.php

SHlVA
質問者

お礼

解答いただきありがとうございます。 そんな関数もあるんですね。あまり情報がないようですが、私の手元(PHP5.2.5 XAMPP版)ではまだ実装されてないようでした。5.3、6.0系で加わるのかもしれませんね。これが実装されればベストの解になりそうですね。 ありがとうございます。

  • masa6272
  • ベストアンサー率66% (93/140)
回答No.4

<?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に解が求まります。 これで、どうでしょう?

SHlVA
質問者

お礼

解答いただきありがとうございます。 '"'内でエスケープされたもの(\")でも区切られてしまいました。 少し手直しすればいけそうですね。 ありがとうございます。

  • 405
  • ベストアンサー率50% (17/34)
回答No.3

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); ?>

SHlVA
質問者

お礼

解答いただきありがとうございます。 なるほど、なかなかおもしろいですね。参考にさせていただきたいと思います。

  • sakusaker7
  • ベストアンサー率62% (800/1280)
回答No.2

ダブルクォートはアイテムを区切る以外に使わないという前提で。 <?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のダブルクォートみたいに、エスケープすれば含められるという条件だと 要手直し。

SHlVA
質問者

お礼

解答いただきありがとうございます。 おお、まさにスマートなやりかたですね。 なるほど、区切りに注目するのではなく、値を抜き出す、ということですね。 エスケープがある場合にも少し正規表現を変えるだけで対応できそうですね。 ありがとうございます。

SHlVA
質問者

補足

いろいろ試行してみましたが、エスケープされたものを含めるのは難しいですね。 正規表現後半の部分で [^"] の部分を すべてOKだけど "のまえには \ がこなければならない、という感じでしょうか。 (?!) や (?=) など駆使しながら試してみましたが、なかなかうまくいかず。 もし暇だったらでけっこうですのでヒントいただけると幸いです。

  • SAYKA
  • ベストアンサー率34% (944/2776)
回答No.1

すぐに浮かぶ手抜きなやりかた ・「"」で囲まれている単語の空白を通常使わない何かの記号に置き換える ・split ・置き換えた物を再び空白に戻す。ついでに「"」を取る 「通常使わない何かの記号」をどうするかが一番の問題。 使用用途によっては「使ってはならない記号類」というのが出てくるだろうからそれを使ったら良いんじゃないかな。

SHlVA
質問者

お礼

解答いただきありがとうございます。 おっしゃるように、手順を踏めばできそうですね。 なかなか1発でするのは難しいですよね。 ただいろんなオプションがあるのはいいことです。参考にしたいと思います。

関連するQ&A

専門家に質問してみよう