文字列に「,」を含むcsvファイルをstr_getcsvで読み込むとうまく動かない
解決済
回答 3
投稿
- 評価
- クリップ 0
- VIEW 5,088
phpで、以下のコードで、csvのヘッダー行と項目数が異なる場合はエラーとしているのですが、
foreach ($lines as $key => $line) {
$line_count = count(str_getcsv($line));
//$headerLineNumはヘッダー行項目数
if ($line_count != $headerLineNum) {
return 'ヘッダー行と項目数が異なります。';
}
}
以下のデータの場合、sgr_getcsvが正しく動かず、項目数を6としてしまいます。
".,",データ2,データ3,データ4,データ5
ちなみに、以下の場合は、正しく機能し、項目数が5となります。
データ2,".,",データ3,データ4,データ5
先頭にダブルクォーテーションで囲まれたものがある場合、何かしら処理が必要なのでしょうか?
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 過去に投稿した質問と同じ内容の質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+1
<?php
$input = '".,",データ2,データ3,データ4,データ5';
$result = str_getcsv($input);
print_r($result);
?>
Array
(
[0] => .,
[1] => データ2
[2] => データ3
[3] => データ4
[4] => データ5
)
あれ?上手くいきますけど。。。
データにゴミでも入っているんですかね?文字コードとか?
PHP 5.4.16 で試しました。
追記(まとめ)
つまりstr_getcsvを普通の使い方をして、
"デ"ータ1,データ2
こういうデータがあった時に
エンクロージャが外れて
データ1
データ2
こう解釈されるのはおかしいのでは?
という疑問ですよね。
確かにおかしい気はしますが、おかしいのはデータであって、
ではどう解釈するのが正しいかというのも微妙で
csvのフォーマットは歴史的に方言が沢山あるのが実情ですから
「PHPの実装はこういうものだ」と理解するのがよいかと個人的には思いました。
どうしてもこの余計な解釈を回避したい場合には、
データをcsvフォーマットとしてふさわしいものに修正変更するか、
getcsvをあきらめて自前でパースするしかなさそうです。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
str_getcsv()の第2、第3引数で対応するか、CSVのデータ自体を調整する必要があると思います。
実データの内容にもよりますので、質問者さんの方で色々と調整してみてください。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
0
第2引数以降の設定でダメなら、裏技的なことかも知れませんが、$lineの中身を加工して、str_getcsv()で正常に処理できるようにする必要があるかも知れません。
≪案1≫
ダブルクォーテーションで囲まれた要素を別の文字に置き換え、列データに分割後に元のデータに戻す。
≪案2≫
ダブルクォーテーションで囲まれた要素以外のすべての要素をダブルクォーテーションで囲むように加工する。
(これが本来のCSVの書式だが、str_getcsv()の第2引数以降の設定が必要かどうかは不明)
※元々ダブルクォーテーションで囲まれている要素は、正規表現などで検出します。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 88.21%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2018/09/05 16:33
本当ですね。。
私もプログラムの中で、$line = '".,",データ2,データ3,データ4,データ5';としたら、うまくいきました。
ファイルの場合だけ、おかしくなるようです。
2018/09/05 16:57 編集
もしそうなら、行($line)の状態でその様な文字を捨てるか置換するなど加工して回避してから、
getcsvするのが解決法だと思います。
エラーになる行を特定して、実データのファイル上でその行を(場合によってはバイナリエディタ等で)よく調べてみてください。
2018/09/05 17:58
頂いた回答で、色々さかのぼって調べてみました。
各行を読み込むforeachの前にcsvファイル自体を読み込んでいる部分がありまして、
$lines = str_getcsv($fileContent, "\n",'"');
どうやらこれを実行すると、おかしくなっているようでした。
ユーザのCSVファイルはサイズが大きいのでエラーになった箇所だけをコピーして1行だけのファイルを作り、
-----------------
フィールド1,フィールド2,フィールド3,フィールド4,フィールド5
".,",データ2,データ3,データ4,データ5
-----------------
として、上記のCSVファイルを、$lines = str_getcsv($fileContent, "\n",'"');で実行すると、
$linesの結果が、
array (
0 => 'フィールド1,フィールド2,フィールド3,フィールド4,フィールド5',
1 => '.,,データ2,データ3,データ4,データ5',
)
となってしまい、ダブルクォーテーションが外れていました。
なぜ外れてしまうのかは、また謎なのですが・・・。
ちなみに、ファイルの内容を、
---------------------
フィールド1,フィールド2,フィールド3,フィールド4,フィールド5
データ2,".,",データ3,データ4,データ5
---------------------
とすると、
[2018-09-05 17:53:58] local.DEBUG: array (
0 => 'フィールド1,フィールド2,フィールド3,フィールド4,フィールド5',
1 => 'データ2,".,",データ3,データ4,データ5',
)
のように、ダブルクォーテーションが外れません。
2018/09/05 18:29
> なぜ外れてしまうのかは、また謎なのですが・・・。
$lines = str_getcsv($fileContent, "\n",'"');
この処理でエンクロージャであるダブルクォーテーションは外れますよ。
改行をデリミタとした大きなcsvにみなしてるわけですから。
「ちなみに」の方はカラムの先頭がエンクロージャでないので
はずれないのは当然です。1行が1カラムとして扱ってるんですから。
こうしたらきっと直ります。
$lines = str_getcsv($fileContent, "\n",'');
しかし、行を分割するのにstr_getcsv()を用いたのが失敗でしたね。
fgets()を使って1行ずつ読み込みながら$linesに追加していくのが一般的で
その方が多少処理効率も良いです。
謎が分かってすっきりしました^^
2018/09/05 18:47
つまりstr_getcsvを普通の使い方をして、
"デ"ータ1,データ2
こういうデータがあった時に
エンクロージャが外れて
データ1
データ2
こう解釈されるのはおかしいのでは?
という疑問ですよね。
確かにおかしい気はしますが、おかしいのはデータであって、
ではどう解釈するのが正しいかというのも微妙で
csvのフォーマットは歴史的に方言が沢山あるのが実情ですから
「PHPの実装はこういうものだ」と理解するのがよいかと個人的には思いました。
2018/09/05 19:44
動きました!!
fgetsに変更しましたら、無事に反応するようになりました。
>$lines = str_getcsv($fileContent, "\n",'"');
>この処理でエンクロージャであるダブルクォーテーションは外れますよ。
>改行をデリミタとした大きなcsvにみなしてるわけですから。
>「ちなみに」の方はカラムの先頭がエンクロージャでないので
>はずれないのは当然です。1行が1カラムとして扱ってるんですから。
そうなのですね。。
よくわからず使っていました。
本当にありがとうございます。助かりました。