🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
CSV

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

PHP

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

ファイルI/O

ファイルI/Oは、コンピューターにおけるファイルの入出力です。これは生成/削除やファイルを読み込んだり、出力をファイルに書き込むようなディレクトリやファイルの運用を含みます。

文字コード

文字コードとは、文字や記号をコンピュータ上で使用するために用いられるバイト表現を指します。

Q&A

解決済

4回答

10383閲覧

PHPのファイル書き込み時、なぜか最初の行の先頭に空白が入ってしまう

asapan

総合スコア61

CSV

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

PHP

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

ファイルI/O

ファイルI/Oは、コンピューターにおけるファイルの入出力です。これは生成/削除やファイルを読み込んだり、出力をファイルに書き込むようなディレクトリやファイルの運用を含みます。

文字コード

文字コードとは、文字や記号をコンピュータ上で使用するために用いられるバイト表現を指します。

0グッド

1クリップ

投稿2019/10/23 18:57

編集2019/10/24 10:56

(追記いたしました、よろしくお願いいたします。)

PHPでファイル書き込み(csv出力)の実装をしています。
そこで、なぜかタイトルの通り、出力をされたファイルをテキスト形式(.txt)でみると最初の行の最初に半角スペースが入ってしまっています。
そのため、CSVでも、一行目一列目のみ半角スペースが2つ入ってしまっています。(画像参照)
どうすればこれを無くせるでしょうか。

どうぞ、よろしくお願いいたします。

以下コードと画像になります。

PHP

1 //CSV形式で情報をファイルに出力のための準備 2 $csvFileName = '/tmp/' . time() . rand() . '.csv'; 3 $res = fopen($csvFileName, 'w'); 4 $array=['あ', 'い']; 5 $dataList[] = $array; 6 // ループしながら出力 7 foreach($dataList as $dataInfo) { 8 9 // 文字コード変換。エクセルで開けるようにする 10 mb_convert_variables('SJIS', 'UTF-8', $dataInfo); 11 12 // ファイルに書き出しをする 13 $line = implode(',' , $dataInfo); 14 fwrite($res, $line . "\n"); 15 } 16 fclose($res); 17 18 // ダウンロード開始 19 header('Content-Type: application/octet-stream'); 20 21 // ここで渡されるファイルがダウンロード時のファイル名になる 22 header('Content-Disposition: attachment; filename=sample.csv'); 23 header('Content-Transfer-Encoding: binary'); 24 header('Content-Length: ' . filesize($csvFileName)); 25 readfile($csvFileName);

イメージ

【追記】

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

出力する内容を隠すためにサンプルの内容を入れたり多少変数名を変えたりしておりまして、
その際コード全体として不自然になってしまいました。そのまま投稿したこと申し訳ありません。
以下、その部分も補足しつつご質問の回答と、状態を書きたいと思います。

ご質問内容⓵
「テキストエディタで開くとどうなってますか?もしかしたらバイナリエディタのほうがいいかもしれません?」

⇒以下の画像の通り、テキストエディタで開いても半角スペースが二つ入ってしまっています。また、バイナリエディタで見たところやはり20(半角スペース)が入っておりました。
イメージ説明

ご質問内容②
「ひょっとして、phpタグの前になんかはいってませんかね?」

⇒確認いたしましたが、PHPタグの前には何も入っておりませんでした。

ご質問内容③
「ファイルをバイナリエディタで開き、ゴミがどういったコードなのか調べるところからですね
また、多バイト文字ではなくシングルバイト文字ではどうなるか追記ください」

⇒上記の通り、バイナリ表示で20,半角スペースが入っておりました。シングルバイト文字でやってみましたが結果は変わらずでした。

ご質問内容④
「そのファイルがインクルードしたファイルのどこかで <?php の前か?> のあとに空白や改行、タブなどが入っていないか
ファイルをBOMつきで保存してないか」

⇒確認しましたが、そのようなことはありませんでした。

ご質問内容⑤
「phpのコードなのに「<?php」が書いていないことから、
一連の処理の一部分のコードを切り出したものだと推察します。

そして、掲載された箇所のコードでは$dataListの初期化が行われていないことが気になります。
ですが、すでに何らかのデータが含まれているのであれば
「あ」がセルA1の位置に来ない気がするのでしっくりきません。」

⇒はい、その通りでございます。具体的にはwebアプリケーションのあるコントローラー内の処理になります。$dataListの初期化は書き忘れておりました。

「文字エンコーディングをUTF-8からSJISに変換したあとにimplodeにかけているの、逆じゃないでしょうか。」

⇒本当ですね...逆でした。しかしながら、これを逆にして試しましたが結果は変わらずでした...

他に何か解決方法は考えられますでしょうか。

【追記②】

php.iniを調べてみました。

; PHP's default character set is set to UTF-8. ; http://php.net/default-charset default_charset = "UTF-8"
[mbstring] ; language for internal character representation. ; This affects mb_send_mail() and mbstrig.detect_order. ; http://php.net/mbstring.language ;mbstring.language = Japanese ; Use of this INI entry is deprecated, use global internal_encoding instead. ; internal/script encoding. ; Some encoding cannot work as internal encoding. (e.g. SJIS, BIG5, ISO-2022-*) ; If empty, default_charset or internal_encoding or iconv.internal_encoding is used. ; The precedence is: default_charset < internal_encoding < iconv.internal_encoding ;mbstring.internal_encoding = ; Use of this INI entry is deprecated, use global input_encoding instead. ; http input encoding. ; mbstring.encoding_traslation = On is needed to use this setting. ; If empty, default_charset or input_encoding or mbstring.input is used. ; The precedence is: default_charset < intput_encoding < mbsting.http_input ; http://php.net/mbstring.http-input ;mbstring.http_input = ; Use of this INI entry is deprecated, use global output_encoding instead. ; http output encoding. ; mb_output_handler must be registered as output buffer to function. ; If empty, default_charset or output_encoding or mbstring.http_output is used. ; The precedence is: default_charset < output_encoding < mbstring.http_output ; To use an output encoding conversion, mbstring's output handler must be set ; otherwise output encoding conversion cannot be performed. ; http://php.net/mbstring.http-output ;mbstring.http_output = ; enable automatic encoding translation according to ; mbstring.internal_encoding setting. Input chars are ; converted to internal encoding by setting this to On. ; Note: Do _not_ use automatic encoding translation for ; portable libs/applications. ; http://php.net/mbstring.encoding-translation ;mbstring.encoding_translation = Off ; automatic encoding detection order. ; "auto" detect order is changed according to mbstring.language ; http://php.net/mbstring.detect-order ;mbstring.detect_order = auto ; substitute_character used when character cannot be converted ; one from another ; http://php.net/mbstring.substitute-character ;mbstring.substitute_character = none ; overload(replace) single byte functions by mbstring functions. ; mail(), ereg(), etc are overloaded by mb_send_mail(), mb_ereg(), ; etc. Possible values are 0,1,2,4 or combination of them. ; For example, 7 for overload everything. ; 0: No overload ; 1: Overload mail() function ; 2: Overload str*() functions ; 4: Overload ereg*() functions ; http://php.net/mbstring.func-overload ;mbstring.func_overload = 0 ; enable strict encoding detection. ; Default: Off ;mbstring.strict_detection = On ; This directive specifies the regex pattern of content types for which mb_output_handler() ; is activated. ; Default: mbstring.http_output_conv_mimetype=^(text/|application/xhtml+xml) ;mbstring.http_output_conv_mimetype=

mbstring関係は全てコメントアウトされていました。また、
;extension=php_mbstring.dll
ともなっておりました。

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

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

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

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

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

m.ts10806

2019/10/23 21:28

Excelではなくテキストエディタで開くとどうなってますか?もしかしたらバイナリエディタのほうがいいかもしれません。
takasima20

2019/10/23 22:42

ひょっとして、phpタグの前になんかはいってませんかね?
asapan

2019/10/24 10:27

確認しましたが、そのようなことはありませんでした....ほかに何か考えられますでしょうか。
退会済みユーザー

退会済みユーザー

2019/10/24 10:34

エンコーディング周りを総点検したくなる、php.iniでのdefault_charsetやmbstring.*も調べて質問文中に示してほしいかな。
退会済みユーザー

退会済みユーザー

2019/10/24 10:50 編集

「あ,い」しか出力しないはずが、なんで2行目以降のデータがあるキャプチャ画像なのさ。示されたサンプルコードじゃないコードの出力については、面倒見きれないよ。
asapan

2019/10/24 10:57 編集

すみません、半角スペースとわかる比較になるかと勝手に判断してしまいました。 「あ、い」しか出力しないコードでもう一度実行したものと画像を差し替えました。 ご迷惑をおかけします。
m.ts10806

2019/10/24 11:03

PHPタグも含めてコード全体そのままご提示ください。 やはり一部だけではこちらも挙動確認できませんので。
guest

回答4

0

phpのコードなのに「<?php」が書いていないことから、
一連の処理の一部分のコードを切り出したものだと推察します。

そして、掲載された箇所のコードでは$dataListの初期化が行われていないことが気になります。
ですが、すでに何らかのデータが含まれているのであれば
「あ」がセルA1の位置に来ない気がするのでしっくりきません。

そもそもCSVファイルとして出力しているのであれば、
Excelが勝手に変換している部分がないかを検証するために
CSVファイルをテキストエディタの類で開いて確認するものです。

そして、一番気になったのが、
文字エンコーディングをUTF-8からSJISに変換したあと
implodeにかけているの、逆じゃないでしょうか。
SJISに変わったあとの文字列を、UTF-8的にimplodeで加工することで
エンコーディングは保証されていない状態になります。
文字化けなどが加わってもおかしくない状態です。
出力する一行分のデータを作ってから、
ファイルに書き出す直前に一行分のデータを文字エンコーディングを変えて出力する、
なら意味あります。

以上、ご確認ください。


示されたコードを自分なりに書き換えて動かしてみました。
空白2文字が入る現象が再現されず、webサーバーのレスポンスヘッダーにへんな細工がされていたりしないか、
なんてオカルト的なことを疑いたくもなり。

php

1<?php 2 3 $dataList = []; 4 5 //CSV形式で情報をファイルに出力のための準備 6 $csvFileName = __DIR__ . '/tmp' . time() . rand() . '.csv'; 7 $res = fopen($csvFileName, 'w'); 8 $array=['あ', 'い']; 9 $dataList[] = $array; 10 // ループしながら出力 11 foreach($dataList as $dataInfo) { 12 $line = implode(',' , $dataInfo); 13 14 // 文字コード変換。エクセルで開けるようにする 15 mb_convert_variables('SJIS', 'UTF-8', $line); 16 17 // ファイルに書き出しをする 18 fwrite($res, $line . "\n"); 19 } 20 fclose($res); 21 22 // ダウンロード開始 23 header('Content-Type: application/octet-stream'); 24 25 // ここで渡されるファイルがダウンロード時のファイル名になる 26 header('Content-Disposition: attachment; filename=sample.csv'); 27 header('Content-Transfer-Encoding: binary'); 28 header('Content-Length: ' . filesize($csvFileName)); 29 readfile($csvFileName);

投稿2019/10/24 01:58

編集2019/10/24 10:48
退会済みユーザー

退会済みユーザー

総合スコア0

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

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

asapan

2019/10/24 10:31

ご回答ありがとうございます。 追記でもかきましたが、 ・$dataListの初期化はここにコピペしてくる際に抜けておりました。申し訳ありません。 ・CSVファイルをテキストエディタで確認はしておりますが半角スペースが入っておりました。 ・エンコーディング変換後にimplodeは仰る通り逆でした。直してみましたが、半角スペースは消えませんでした...。 何か他に考えることはありますでしょうか?
退会済みユーザー

退会済みユーザー

2019/10/24 10:52

> なんてオカルト的なことを疑いたくもなり。 ほんとにw ダウンロードしたファイルじゃなくて、/tmp にあるファイルの確認もしてもらいたいですね。
asapan

2019/10/24 11:01

お手元で確認していただき、本当にありがとうございます....!! コード自体に問題はないということが分かっただけでも大変助かります。 /tmp下も確認して参ります。
asapan

2019/10/24 12:01

/tmp以下に保存されるファイルは半角スペースが一つも入っておりませんでした。 レスポンスヘッダーに問題があるということになるのでしょうか....!!
Y.H.

2019/10/24 12:29

エラーログに以下が出力されてませんか? Warning: Cannot modify header information - headers already sent by ほげほげほげほげ これが出力されていると<?php ?>の外で出力されていると云うことになります。
asapan

2019/10/24 16:54

ご回答ありがとうございます。エラーログはでておりませんでした。 他の回答者の方の while (ob_get_level()) { ob_end_clean(); } を試したところ改善されました。 皆さまご協力下さり本当にありがとうございました....!!
退会済みユーザー

退会済みユーザー

2019/10/25 00:37

エラーログは出てない→エラーログ出力レベルを調整して細かいのも見逃さない
guest

0

ベストアンサー

下記、確認してみてください。

  1. そのファイルがインクルードしたファイルのどこかで <?php の前か?> のあとに空白や改行、タブなどが入っていないか
  2. ファイルをBOMつきで保存してないか

※ 空白2つなのであれば前者の気はしますが。


追記:

違いましたか・・。
他には、、、 readfile() 前にバッファが残っているとか・・・?

php

1while (ob_get_level()) { ob_end_clean(); }

投稿2019/10/23 22:38

編集2019/10/24 13:20
tanishi_a

総合スコア484

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

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

asapan

2019/10/24 10:33

ご回答ありがとうございます。 確認してみましたが、そのようなことはありませんでした...。 他に何か原因となりそうなものは考えられるでしょうか?
asapan

2019/10/24 18:21 編集

while (ob_get_level()) { ob_end_clean(); } を試したところ改善されました。バッファ溜まる部分が見たらず意味が分からないですが...コードを追って原因を突き止めたいと思います。ありがとうございます!
guest

0

記述にある範囲だと、問題に直結する箇所はないように思います。
以下で、意図した出力になっていることを確認しました。

php

1<?php 2$csvFileName = '/tmp/' . time() . rand() . '.csv'; 3$res = fopen($csvFileName, 'w'); 4$array=['あ', 'い']; 5$dataList[] = $array; 6// ループしながら出力 7foreach($dataList as $dataInfo) { 8 // 文字コード変換。エクセルで開けるようにする 9 mb_convert_variables('SJIS', 'UTF-8', $dataInfo); 10 // ファイルに書き出しをする 11 $line = implode(',' , $dataInfo); 12 fwrite($res, $line . "\n"); 13} 14fclose($res);

問題箇所は他にありそうですが。。。
質問のコードは実際のコードの完全な部分切り抜きなのでしょうか?
デバッグのため、$line を「表示」させて確認してみてください。

投稿2019/10/24 10:43

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

asapan

2019/10/24 16:57

ご回答ありがとうございます! 自分が見ている限りでは デバッグでも原因はわかりませんでした...。 他の回答者の方の while (ob_get_level()) { ob_end_clean(); } を試したところ改善されました。 webフレームワークのコントローラー内のコードは完全にこれだけなのでどこか深いところで原因があるのかもしれません...。
退会済みユーザー

退会済みユーザー

2019/10/25 01:31

ステップ実行しながら出力用のバッファ確認すればイイと覆うよ。
guest

0

ファイルをバイナリエディタで開き、ゴミがどういったコードなのか
調べるところからですね
また、多バイト文字ではなくシングルバイト文字ではどうなるか
追記ください

投稿2019/10/24 00:22

yambejp

総合スコア116694

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

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

asapan

2019/10/24 10:32 編集

ご回答ありがとうございます。追記した通りですが、 やはり半角スペースに変わらず、シングルバイト文字でも結果は変わらずでした...。 他に何か考えることはありますでしょうか?
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問