dポイントプレゼントキャンペーン実施中!

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

A 回答 (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。
    • good
    • 0
この回答へのお礼

sakusaker7様、ひきつづき解答いただきありがとうございます。

すごい。パーフェクトですね。'"'の内部のエスケープされたものも問題なく取り出してくれますね。

>あと微妙に動作が違います。

というのはどういう意味なんでしょうか?
それにしても正規表現って難しい。まだまだ勉強が足りませんね。
大変勉強になりました。

ありがとうございます。

お礼日時:2008/05/12 01:33

#6のお礼でのご質問に対して。



>>あと微妙に動作が違います。
>というのはどういう意味なんでしょうか?

#2 のパターンだと、"" というダブルクォートのみのパターンは受け付けませんが
#6のだと受け付けてしまいます。

マッチングがとんでもなく遅くなる可能性があるけど#2と同じ動作をするものにするか
速度を取るかで後者を選んだという次第です。はい。
    • good
    • 0
この回答へのお礼

なるほど。

AAA BBB "" "CCC DDD"

の場合
0=>AAA
1=>BBB
2=>""
3=>"CCC DDD"

になるっていうことなんですね。そのあたりは、もう1処理加えて、空欄は省けば問題なさそうですね。

みなさまに教えていただいた方法をそれぞれ比較してみて利用させていただこうと思います。

貴重な時間を割いてお答えいただいてありがとうございました。

お礼日時:2008/05/12 16:09

おっとしまった、読み書きで開いて先頭に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);
?>
    • good
    • 0
この回答へのお礼

解答いただきありがとうございます。

たしかに、すでに実装済みのCSV関係関数を使うためにファイルに読み書きするのも手ですね。処理的にちょっとかかりそうな気もしないでもないですが、そのへん少し比較してみたいと思います。

1つの目的のためにも、いろんなアプローチがあるものですね。おもしろい。

お礼日時:2008/05/12 15:48

姑息かもしれませんが、一度テンポラリに書き出してしまうのも手です。



<?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);
?>
    • good
    • 0

使用した事は有りませんが、以下の関数の delimiter をスペースにする事で出来るかもしれません。



str_getcsv()

http://jp.php.net/manual/ja/function.str-getcsv. …
    • good
    • 0
この回答へのお礼

解答いただきありがとうございます。

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

ありがとうございます。

お礼日時:2008/05/12 01:23

<?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に解が求まります。
これで、どうでしょう?
    • good
    • 0
この回答へのお礼

解答いただきありがとうございます。

'"'内でエスケープされたもの(\")でも区切られてしまいました。
少し手直しすればいけそうですね。

ありがとうございます。

お礼日時:2008/05/12 01:20

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

?>
    • good
    • 0
この回答へのお礼

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

お礼日時:2008/05/11 01:01

ダブルクォートはアイテムを区切る以外に使わないという前提で。



<?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だけど "のまえには \ がこなければならない、という感じでしょうか。

(?!) や (?=) など駆使しながら試してみましたが、なかなかうまくいかず。

もし暇だったらでけっこうですのでヒントいただけると幸いです。

補足日時:2008/05/11 02:48
    • good
    • 0
この回答へのお礼

解答いただきありがとうございます。

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

ありがとうございます。

お礼日時:2008/05/11 01:14

すぐに浮かぶ手抜きなやりかた



・「"」で囲まれている単語の空白を通常使わない何かの記号に置き換える
・split
・置き換えた物を再び空白に戻す。ついでに「"」を取る

「通常使わない何かの記号」をどうするかが一番の問題。
使用用途によっては「使ってはならない記号類」というのが出てくるだろうからそれを使ったら良いんじゃないかな。
    • good
    • 0
この回答へのお礼

解答いただきありがとうございます。

おっしゃるように、手順を踏めばできそうですね。
なかなか1発でするのは難しいですよね。

ただいろんなオプションがあるのはいいことです。参考にしたいと思います。

お礼日時:2008/05/11 00:58

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!