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

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

ただいまの
回答率

89.64%

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

解決済

回答 4

投稿 編集

  • 評価
  • クリップ 1
  • VIEW 555

yamatotakeruru

score 13

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

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

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

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

        //CSV形式で情報をファイルに出力のための準備
        $csvFileName = '/tmp/' . time() . rand() . '.csv';
        $res = fopen($csvFileName, 'w');
        $array=['あ', 'い'];
        $dataList[] = $array;
        // ループしながら出力
        foreach($dataList as $dataInfo) {

            // 文字コード変換。エクセルで開けるようにする
            mb_convert_variables('SJIS', 'UTF-8', $dataInfo);

            // ファイルに書き出しをする
            $line = implode(',' , $dataInfo);
            fwrite($res, $line . "\n");
        }
        fclose($res);

        // ダウンロード開始
        header('Content-Type: application/octet-stream');

        // ここで渡されるファイルがダウンロード時のファイル名になる
        header('Content-Disposition: attachment; filename=sample.csv'); 
        header('Content-Transfer-Encoding: binary');
        header('Content-Length: ' . filesize($csvFileName));
        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
ともなっておりました。

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

質問への追記・修正、ベストアンサー選択の依頼

  • m6u

    2019/10/24 19:49 編集

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

    キャンセル

  • yamatotakeruru

    2019/10/24 19:57 編集

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

    キャンセル

  • m.ts10806

    2019/10/24 20:03

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

    キャンセル

回答 4

checkベストアンサー

+1

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

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

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


追記:

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

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

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/10/24 19:33

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

    キャンセル

  • 2019/10/25 01:51 編集

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

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

    キャンセル

+1

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

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

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

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

以上、ご確認ください。


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

<?php

    $dataList = [];

    //CSV形式で情報をファイルに出力のための準備
    $csvFileName = __DIR__ . '/tmp' . time() . rand() . '.csv';
    $res = fopen($csvFileName, 'w');
    $array=['あ', 'い'];
    $dataList[] = $array;
    // ループしながら出力
    foreach($dataList as $dataInfo) {
        $line = implode(',' , $dataInfo);

        // 文字コード変換。エクセルで開けるようにする
        mb_convert_variables('SJIS', 'UTF-8', $line);

        // ファイルに書き出しをする
        fwrite($res, $line . "\n");
    }
    fclose($res);

    // ダウンロード開始
    header('Content-Type: application/octet-stream');

    // ここで渡されるファイルがダウンロード時のファイル名になる
    header('Content-Disposition: attachment; filename=sample.csv'); 
    header('Content-Transfer-Encoding: binary');
    header('Content-Length: ' . filesize($csvFileName));
    readfile($csvFileName);

投稿

編集

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/10/24 21:29

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

    キャンセル

  • 2019/10/25 01:54

    ご回答ありがとうございます。エラーログはでておりませんでした。

    他の回答者の方の
    while (ob_get_level()) { ob_end_clean(); }
    を試したところ改善されました。

    皆さまご協力下さり本当にありがとうございました....!!

    キャンセル

  • 2019/10/25 09:37

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

    キャンセル

0

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/10/24 19:32 編集

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

    キャンセル

0

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

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


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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2019/10/25 01:57

    ご回答ありがとうございます!
    自分が見ている限りでは
    デバッグでも原因はわかりませんでした...。

    他の回答者の方の
    while (ob_get_level()) { ob_end_clean(); }
    を試したところ改善されました。

    webフレームワークのコントローラー内のコードは完全にこれだけなのでどこか深いところで原因があるのかもしれません...。

    キャンセル

  • 2019/10/25 10:31

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

    キャンセル

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

  • ただいまの回答率 89.64%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

同じタグがついた質問を見る