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ファイルの上書きを行いたいです。

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

回答の条件
  • URL必須
  • 1人3回まで
  • 登録:2009/01/07 19:21:31
  • 終了:2009/01/14 19:25:02

回答(3件)

id:pahoo No.1

pahoo回答回数5960ベストアンサー獲得回数6332009/01/07 20:50:21

ポイント27pt

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

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


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

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

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

id:akide

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

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

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

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

2009/01/07 21:07:14
id:pahoo No.2

pahoo回答回数5960ベストアンサー獲得回数6332009/01/07 22:45:30

ポイント27pt

#1のコメント:

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

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


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

id:akide

$sex[$key] = 20;

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

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

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

2009/01/07 23:16:46
id:tobeoscontinue No.3

tobeoscontinue回答回数212ベストアンサー獲得回数522009/01/08 14:32:02

ポイント26pt

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

  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形式のデータファイルにアクセスする以前にアクセス制限して排他制御する方がいいように思います。(ファイルをロックするのではなくファイルを操作する方を排他する)

コメントはまだありません

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

絞り込み :
はてなココの「ともだち」を表示します。
回答リクエストを送信したユーザーはいません