人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

PHPの正規表現の質問です
=======
みかん,愛媛産,1,和歌山産,4,海外産,3,
メロン,北海道産,1,大阪産,2,
レモン,・・・・



=======
というテキストがあります
これを
=======
みかん,愛媛産,1,
みかん,和歌山産,4
みかん,海外産,3,
メロン,北海道産,1,
メロン,大阪産,2,



=======
というように 各行の冒頭を各産別の前につけて 改行したいんですがどうすればいいですか?


●質問者: gaugjiuaej
●カテゴリ:ウェブ制作
○ 状態 :終了
└ 回答数 : 2/2件

▽最新の回答へ

1 ● a-kuma3
●150ポイント

こんな感じで。

<?php
$data = file_get_contents(...); // CSV をファイルから読み込み
$in_array = explode("\n", $data); // 一行ずつ配列にばらす

$re = "/([^,]+,)/";
for ($i = 0 ; $i < count($in_array) ; ++$i) {
 preg_match_all($re, $in_array[$i], $tmp, PREG_SET_ORDER);
 for ($j = 1 ; $j < count($tmp) ; $j += 2) {
 $out_array[] = $tmp[0][0] . $tmp[$j][0] . $tmp[$j + 1][0];
 }
}

// 配列で
print_r($out_array);

// 改行でくっつけた文字列が欲しいなら、implode() で
$result = implode("\n", $out_array);
print_r($result);

?>

/([^,]+,)(([^,]+,)([^,]+,))+/ ってな感じの正規表現でいけるはずだったのですが、他の処理系と違って、繰り返しはマッチした最後のものしか返してくれないという...
# ちょっと、はまったのは、内緒だ


gaugjiuaejさんのコメント
回答ありがとうございます 実行してみましたが真っ白で何も表示されませんでした

a-kuma3さんのコメント
質問にあるデータが正しく読みこめていれば、分解できます。 ideone.com で試したのがこちら。 http://ideone.com/yptaYs 以下のような辺りを確認してください。 - ファイルの内容が読めてない - 質問で提示されたデータと、実際のデータが違う - 文字コードが合ってない

gaugjiuaejさんのコメント
できました ありがとうございました

a-kuma3さんのコメント
因みに、先のコメントの時点でできなかったのは何が原因だったのでしょうか。 そういった辺りを書き込んでもらえると、後でこのページに迷い込んできた人にも有益なものになるんじゃないかと思うのですが <tt>:-)</tt>

2 ● tezcello
●150ポイント ベストアンサー

> 正規表現の質問
正規表現の練習が目的でしょうか?
それとも
> 各行の冒頭を各産別の前につけて 改行したい
こちらが目的でしょうか?
こちらが目的なら、正規表現は使わなくても出来そうですけど。

正規表現を使わなければならない理由があるとします。

<?php
$rows = file('index.dat', FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);

$result = '';

foreach ($rows as $row){
 // 品目と産地(と入荷数?)とに分ける
 preg_match('/([^,]+,)(.+)/', $row, $mch);
 // 産地から産地と数字の1ペア単位で取り出す(配列に収める)
 preg_match_all('/[^,]+,[^,]+,/', $mch[2], $habitats);

 // 産地の配列から一つずつ取り出して、品目と連結して出力用変数に収める
 foreach($habitats[0] as $habitat){
 $result .= $mch[1] . $habitat . "\n";
 }
}

var_dump($result);
?>

余程何かの理由が無い限り、テキストファイルから配列に入れるのなら file() を使うのがお手軽でしょう。
そして、配列の全要素について端から順にやっていく際には、for() ではなく foreach() を使うのが、見やすく、やろうとしている事が直感的に読み易いだろうと思います。

行内の並びが、単なる羅列ではなく意味があるので、その意味に従って取り出すというのが、後から読む際にも関連が分かり易いと思います。
なので、1行のデータから品目部分を取出して、後は別に処理します。

行の後半部分は、産地と数字がペアになっているので、そのような形で取り出します。複数ある場合があるので、preg_match_all() で該当する部分を全部取り出すようにします。

ここまで分解できれば、後は関係する部分を連結するだけです。


この流れを理解して頂ければ、正規表現を使う必要が無かったというのもお分かり頂けると思います。
1行を品目と産地関連部とに分けるのはカンマ( , )です。
産地と数値を分けるのも同じです。
ならば、1行をカンマで分けて配列にしてしまえば上と同じようなパーツが得られます。
(最初の例のループ内だけを示しています。)

<?php
 $habitats = explode(',', $row);
 $kind = array_shift($habitats);
 while(count($habitats) > 1){
 $place = array_shift($habitats);
 $qty = array_shift($habitats);
 $result .= $kind . ',' . $place . ',' . $qty . ',' . "\n";
 }
?>


別に分解しなくても、部分文字列を取り出すと考えれば、カンマの位置を strpos() を使って探れば、取り出すべき部分は判ります。
最初の探査結果では、品目の区切り位置。
次の探査結果は産地の、その次はペアになっている数値。
(同じくループ内のみ示します)

<?php
 $pos1 = 0;
 $pos2 = strpos($row, ',');
 $pos2++;
 $kind = substr($row, $pos1, $pos2 - $pos1);
 $pos1 = $pos2;
 while($pos2 = strpos($row, ',', $pos1)) {
 $result .= $kind;
 $pos2++;
 $pos2 = strpos($row, ',', $pos2);
 $pos2++;
 $result .= substr($row, $pos1, $pos2 - $pos1);
 $pos1 = $pos2;
 $result .= "\n";
 }
?>

区切り文字のカンマを含むように取り出す都合上、$pos2++; が目障りですが仕方ないです。
__strpos() や substr() で使う際に +1 する手もアリですが大差ないですよね

何れも結果はこんな感じ。

string 'みかん,愛媛産,1,
みかん,和歌山産,4,
みかん,海外産,3,
メロン,北海道産,1,
メロン,大阪産,2,
レモン,瀬戸内,2,
' (length=144)

関連質問

●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ