質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

Q&A

解決済

2回答

471閲覧

phpプログラム上でのCSVファイルの読み込み書き出し時の不具合

webeee

総合スコア13

CSV

CSV(Comma-Separated Values)はコンマで区切られた明白なテキスト値のリストです。もしくは、そのフォーマットでひとつ以上のリストを含むファイルを指します。

PHP

PHPは、Webサイト構築に特化して開発されたプログラミング言語です。大きな特徴のひとつは、HTMLに直接プログラムを埋め込むことができるという点です。PHPを用いることで、HTMLを動的コンテンツとして出力できます。HTMLがそのままブラウザに表示されるのに対し、PHPプログラムはサーバ側で実行された結果がブラウザに表示されるため、PHPスクリプトは「サーバサイドスクリプト」と呼ばれています。

0グッド

0クリップ

投稿2017/07/12 03:13

編集2017/07/12 06:40

###前提・実現したいこと
スマートフォンサイトでフォーム送信時に
パラメータ毎に1日の問い合わせ件数を記録する為、
URLのパラメーター毎にメール用パラメータ識別テキスト、日付、3桁の通し番号を
csvファイルに記録するプログラムを作成しています。

日付をまたぐと通し番号は001にリセットしたいです。

例)URLとパラメータ
http://ドメイン.com/?param=example1
http://ドメイン.com/?param=example2
http://ドメイン.com/?param=example3

###該当のコード
count.csv
用意したCSVファイル(パラメータ名,メール用パラメータ識別テキスト,日付,3桁の通番)

example1,e1,0000,000 example2,e2,0000,000 example3,e3,0000,000

php
csv読み込み書き出しプログラム部分

$paramには外部ファイルでURLのパラメータ名を取得してセットしています
パラメータ?param=example1のURLからからアクセスされた場合、
下記の$paramには文字列の'example1'がはいっています。

$fp = fopen('count.csv', "r+"); if(flock($fp, LOCK_EX)) { $data = array(); while (($csv = fgetcsv($fp)) !== FALSE) { $data[] = $csv; } $search_count = 0; foreach($data as $val){ if ($val[0] === $param) { break; } ++$search_count; } $dateCount = date('md'); if($data[$search_count][2] == $dateCount) { $count = intval($data[$search_count][3]) + 1; $count = sprintf('%03d', $count); $data[$search_count][3] = $count; $subject_pram = $data[$search_count][1].$data[$search_count][2].'/'.$count; } else { $data[$search_count][2] = $dateCount; $data[$search_count][3] = 1; $subject_pram = $data[$search_count][1].$dateCount.'/001'; } rewind($fp); foreach($data as $line){ fputcsv($fp, $line); } flock($fp, LOCK_UN); } fclose($fp);

###発生している問題
私の環境では理想のcsvが書き出されているのですが、
実際本番環境で動かしてみると下記のように保存されてしまいます。

理想の更新されたCSVファイル
パラメータ?param=example1のURLからからアクセスされ、フォームが送信された場合

example1,e1,0712,001 example2,e2,0000,000 example3,e3,0000,000

送信毎に1ずつ通し番号がカウントされていく

example1,e1,0712,002 example2,e2,0000,000 example3,e3,0000,000

意図していない形式で更新されたCSVファイル

パラメータ?param=example1のURLからからアクセスされ、フォームが送信された場合

example1,e1,0712,1 example2,e2,0707,000 example3,e3,0707,000 2

送信毎に2のカウントが最後尾に追加される

example1,e1,0712,1 example2,e2,0707,000 example3,e3,0707,000 2, 2

私のテスト環境は
・macOS Sierra chromeのdevツールから
・iphone ios最新
・android 6.0.1

CSVを使ったプログラムの作成は初めてで
問題の特定ができずにいます。

お力を貸していただけますと助かります。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2017/07/12 03:32

提示されているコードは、実環境のものと違うと思います。変更箇所の記述と テストで使用した count.csv の中身があれば、回答しやすいかと。
webeee

2017/07/12 03:41

コメントありがとうございます!>実環境のものと違う とはどいうことでしょうか…?? パラメータ名の文字列が違うだけなのですが、このソース外に問題がありそうということでしょうか??
退会済みユーザー

退会済みユーザー

2017/07/12 03:46

このコードだと、一行しか更新されないと思うので、なにか削って提示していませんか?
webeee

2017/07/12 03:50

更新したいのはパラメータが一致した1行だけなんです。今ふと気づいたのですが、CSVファイルでも文字列ってクォーテーションで囲う必要があるのでしょうか…?
kunai

2017/07/12 04:44

質問ではCSVの「書き出し」となっていますが、CSVの「更新」なのであれば更新前後の期待値のCSVと、実際の動作前後のCSVを記載いただけますか。また、今提示されている「書き出したい形式」では最後のカラムが全て000になっていますが、コレで正しいのでしょうか。
webeee

2017/07/12 05:37

kunaiさま そうですね!書き出しというよりは「更新」でした。言葉足らずで申し訳ありません。。もう少し説明を詳細に書いて更新させていただきます。お手数おかけしてしまい申し訳ありあません。
guest

回答2

0

ベストアンサー

以下のコードで確認したのですが、通し番号の再現ができなかったです。

php

1<?php 2// $fp = fopen('count.csv', "r+"); 3// if(flock($fp, LOCK_EX)) { 4 5$csv_org = [ 6'example1,e1,0707,001', 7'example2,e2,0707,000', 8'example3,e3,0707,000' 9]; 10$param = 'example1'; 11 12$data = array(); 13// while (($csv = fgetcsv($fp)) !== FALSE) { 14// $data[] = $csv; 15// } 16 17foreach ($csv_org as $key => $val) { 18 $csv_org[$key] = explode(',', $val); 19} 20$data = $csv_org; 21var_dump($data); 22 23$search_count = 0; 24foreach ($data as $val) { 25 if ($val[0] === $param) { 26 break; 27 } 28 ++$search_count; 29} 30 31$dateCount = date('md'); 32// $dateCount = '0707'; 33if ($data[$search_count][2] == $dateCount) { 34 $count = intval($data[$search_count][3]) + 1; 35 $count = sprintf('%03d', $count); 36 $data[$search_count][3] = $count; 37 $subject_pram = $data[$search_count][1].$data[$search_count][2].'/'.$count; 38} else { 39 $data[$search_count][2] = $dateCount; 40 // $data[$search_count][3] = 1; 41 $data[$search_count][3] = '001'; 42 $subject_pram = $data[$search_count][1].$dateCount.'/001'; 43} 44 45// rewind($fp); 46foreach ($data as $line) { 47 // fputcsv($fp, $line); 48 echo join(',', $line).'<br>'; 49} 50// flock($fp, LOCK_UN); 51// } 52// fclose($fp);

ゼロ埋めは$data[$search_count][3] = 1が原因なので、多分テスト環境のコードでも同じ結果が出力されると思います。

追記
foreach の中に全部突っ込んだほうが分かりやすいと思います。

php

1foreach ($data as $key => $val) { 2 if ($val[0] === $param) { 3 $dateCount = date('md'); 4 if ($data[$key][2] === $dateCount) { 5 $count = (int)($data[$key][3]) + 1; 6 $count = sprintf('%03d', $count); 7 $data[$key][3] = $count; 8 $subject_pram = $data[$key][1].$data[$key][2].'/'.$count; 9 } else { 10 $data[$key][2] = $dateCount; 11 $data[$key][3] = '001'; 12 $subject_pram = $data[$key][1].$dateCount.'/001'; 13 } 14 } 15}

投稿2017/07/12 05:10

編集2017/07/12 05:46
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

webeee

2017/07/12 05:34

ご回答ありがとうございます! >通し番号の再現ができなかったです。 理想の形式にならなかったということですかね?? >ゼロ埋めは$data[$search_count][3] = 1が原因なので、多分テスト環境のコードでも同じ結果が出力されると思います。 elseのte2jiさまがコメントアウトしている箇所の処理が行われていたということですね・・! 少しコード整理してみます。
退会済みユーザー

退会済みユーザー

2017/07/12 05:43

>>通し番号の再現ができなかったです。 > 理想の形式にならなかったということですかね?? 2が追加される現象が再現しなかったということです。 開発環境では、 $data[$search_count][3] = 1 → 001 となっていると思います。 あと、プログラム的には、 foreach ($data as $val) { if ($val[0] === $param) { の中に全部突っ込んだほうが、コードの意図が伝わりやすいと思います。 サンプルとして、回答に追記しておきます。
webeee

2017/07/12 07:25

回答ありがとうございます! >foreach の中に全部突っ込んだほうが分かりやすいと思います。 また追記ありがとうございます!大変参考になります。 いただいたコードで整理してみます! >2が追加される現象が再現しなかったということです。 フォーム送信したスマートフォンorブラウザによってなったり、ならなかったりするんですよね・・・ 回答の中でCSVを定義している箇所をクォーテーションでくくってありますが、 行の中身が全て文字列で扱われるということなのでしょうか? 初歩的な質問ですみません。。 ↓ 'example1,e1,0707,001', 私が作成CSVファイルは 仕組みがよくわからず、クォーテーションでくくっていないため 文字列として扱われず、バグってしまったのかなぁと ↓ example1,e1,0000,000
guest

0

テスト環境は、PHP5.4以前の古いPHPが動いていませんか?
そしてregister_globals=onで使われていないでしょうか。
http://php.net/manual/ja/language.variables.external.php

PHP

1 $search_count = 0; 2 foreach($data as $val){ 3 if ($val[0] === $param) { 4 break; 5 } 6 ++$search_count; 7 }

この部分で、パラメータとして渡されている'param'が、いきなり$paramという変数で登場しています。
正しくは、

PHP

1$_GET['param']

として取得すべきです。
(先のURL参照)
$paramが正しく取得できていない為、マッチせずに$search_countが進み、最終行の次の行に書き込まれているんだと思います。

細かいところを言うと色々書き直したくなる部分の多いソースですので、もう少し安全で効率的な書き方が出来ないかどうか、検討して見て下さい。

投稿2017/07/12 04:56

kunai

総合スコア5405

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

webeee

2017/07/12 06:54

回答ありがとうございます! 全体的に載せていない部分のコードが多く、前後関係がわかりづらくて大変申し訳ないです。。。! 説明では$paramがいきなり登場しているようで、本当にわかりにくかったと思いますが、 載せていない部分でファイルでパラメータ取得して変数にセットする処理を行っています。 申し訳ありません! $paramに意図する文字列が入っているのはデバックして確認しましたので間違いないです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問