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

phpでCSV形式のデータを操作するロジックを教えて下さい。

CSVファイルの中身
太郎,男,22
花子,女,21
桃子,女,23
次郎,男,25

のように項目は「名前」「性別」「年齢」とします。

個人毎のデータの追加、削除は出来るのですが、
名前からデータを検索し、性別、年齢を変更したいのですが
どのように考えれば実現できるのでしょうか?

自分なりに色々と考えたのですが、もっとスマートでプログラマ的な考え方
ありましたらアドバスをお願いします。

素人のため、ソースの再利用や簡素化が出来ればと考えています。

<?php
$array = file("csv.csv");

$fp = fopen("csv.csv", "w");
flock($fp, LOCK_EX);

$c = count($array);
$i = 0;

while($i < $c){
$array[$i] = trim($array[$i]);
list($name[$i],$age[$i],$sex[$i]) = split("\,",$array[$i]);
$i++;
}

$key = array_search("花子", $name);
$sex[$key] = 20;

$reg = implode("\n", $array);

fputs($fp, "$reg\n");
flock($fp, LOCK_UN);
fclose($fp);

?>

花子の年齢を21から20にしてCSVファイルの上書きを行いたいです。

自分なりに思いついたソースが上記です。



●質問者: akide
●カテゴリ:ウェブ制作
✍キーワード:23 CSV Flock FP Key
○ 状態 :終了
└ 回答数 : 3/3件

▽最新の回答へ

1 ● pahoo
●27ポイント

CSV形式ファイルを読み込む関数として、fgetcsv関数があります。使い方は「PHPでCSVファイルを読み込む」を参考にしてください。

一方、書き込む関数としては、fputcsv関数があります。


読み込んだデータの構造ですが、項目名を連想配列にして、

$row['name'][$num] = 名前
$row['sex'][$num] = 性別
$row['age'][$num] = 年齢

にすれば、項目が増えたときにも変数名を増やす必要がなく楽です。($numは行番号)

◎質問者からの返答

回答ありがとうございます。

fgetcsvに関してはバグがあるとの事でしたので利用を控えておりました。

連想配列で考えるのも1つなんですね。

私のサンプルスクリプトではなぜうまく動作しないのでしょうか?


2 ● pahoo
●27ポイント

#1のコメント:

私のサンプルスクリプトではなぜうまく動作しないのでしょうか?

変更した配列 $sex の値を $array に反映していないからです。


※設定された回答回数の上限になりました。さらにフォローが必要でしたら、コメント欄を開けていただくか、回答回数を増やしてください。

◎質問者からの返答

$sex[$key] = 20;

の部分で$arrayまで上書きされていると勘違いしていました。

pahooさんのおかげで見えていない部分が見えてきました。ありがとうございます。

一度連想配列で考えてみます。


3 ● tobeoscontinue
●26ポイント

ファイルのロックをするためにこうしているのでしょうがファイルのロックを無視すると

  1. csvデータを取り込む
  2. 花子を探して年齢を20にする
  3. csvデータを書き出す

と明確にした方が解りやすいと思います

<?php
$array = file("csv.csv");
$c = count($array);
$i = 0;
while($i < $c) {
 $array[$i] = trim($array[$i]);
 list($name[$i],$sex[$i],$age[$i]) = split("\,",$array[$i]);
 $i++;
}

$key = array_search("花子", $name);
if ($key !== FALSE) {
 $age[$key] = 20;
 $array[$key] = implode(',',array($name[$key],$sex[$key],$age[$key]));
}

$fp = fopen("csv.csv", "w");
$reg = implode("\n", $array);
fputs($fp, "$reg\n");
fclose($fp);

配列の全て要素にアクセスする場合はforやwhileよりもforeachが便利です

trim()で$arrayの各要素の最後の\nは取ってません

splitはexplodeに替えています

<?php
list($name, $age, $sex) = array(array(),array(),array());
foreach ($array as $row)
 list($name[],$sex[],$age[]) = explode(",",trim($row));

php5ならファイルへの書き出しはfile_put_contents()が便利です

$arrayの各要素の最後に\nが付加されていると仮定

file_put_contents("csv.csv",$array)

以上をふまえて書き直すと下記のようになります

<?php
$array = file("csv.csv");
list($name, $sex, $age) = array(array(),array(),array());
foreach ($array as $row)
 list($name[],$sex[],$age[]) = explode(",",trim($row));

$key = array_search("花子", $name);
if ($key !== FALSE) {
 $age["$key"] = 20;
 $array["$key"] = implode(',',array($name["$key"],$sex["$key"],$age["$key"]))."\n";
}

file_put_contents("csv.csv",$array);

該当する部分の年齢だけを変更するだけならわざわざ$name,$age,$sexを配列に展開する必要もないように思います

<?php
$array = file("csv.csv");
foreach ($array as $i=>$row) {
 list($name,$sex,$age) = explode(",",trim($row));
 if ($name == '花子') {
 $age = 20;
 $array[$i] = implode(',',array($name,$sex,$age))."\n";
 break;
} }
file_put_contents("csv.csv",$array);

ソースの再利用を考えるならforeachの部分を関数にしておけば再利用できるかもしれません

csv_lookup($array, $name, $col, $value)

$arrayの各要素は,で区切られたcsv形式で1列目が$nameと同じ行を見付け、$col列の値を$valueで置き換えるとします

<?php
function csv_lookup($array, $name, $col, $value) {
 foreach ($array as $i=>$row) {
 $data = explode(",",trim($row));
 if ($data[0] == $name) {
 $data["$col"] = $value;
 $array["$i"] = implode(',',$data)."\n";
 break;
 } }
 return $array;
}

file_put_contents("csv.csv",csv_lookup(file("csv.csv"), '花子', 2, 20));

"csv.csv"ファイルをfile()で配列にし

csv_lookup()で花子の年齢(2列目)を20に代えた配列を

file_put_contents()で"csv.csv"ファイルに書き出す

となります。


正規表現による実装

CSV形式と考えず平坦な文字列と考え正規表現を使って該当する行を入手する方法です

preg_splitでPREG_SPLIT_DELIM_CAPTUREとすることで該当する部分が一つだけだと三つの要素が返ります

該当する以前の部分と、該当した部分と、該当した以降の部分です

検索文字として非英数字を使っているのでUTF-8にしてuのパターン修飾子を付けています。

また複数行のm(PCRE_MULTILINE)も付加してみました

<?php
function csv_lookup($file, $name, $col, $value) {
 $splt = preg_split("/^(".$name.".*)$/mu",$file, -1, PREG_SPLIT_DELIM_CAPTURE);
 if (2 < count($splt)) {
 $data = explode(',',$splt[1]);
 $data["$col"] = $value;
 $splt[1] = implode(',',$data);
 }
 return implode('',$splt);
}

file_put_contents("csv.csv",csv_lookup(file_get_contents("csv.csv"), '花子', 2, 20));

ファイルロックについて

ここにもあるようにシビアに考えると結構やっかいです。ただこのCSV形式のデータ操作でそれほどの機能が必要なのでしょうか。

また必要だとしても、CSV形式のデータファイルにアクセスする以前にアクセス制限して排他制御する方がいいように思います。(ファイルをロックするのではなくファイルを操作する方を排他する)

関連質問


●質問をもっと探す●



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