Excelで作られたCSVファイルの改行コード「CR」を処理する前に「LF」に置換してしまう。

f:id:nakahashi_h:20171112235342p:plain

Wordpressプラグインを改造しないといけない状況になり、Windowsの特有の仕様に悩まされました。 既存のプラグインに影響を与えないような改善となると、題名のような処理が要求される事になったのでその事について触れます。

注意

プラグイン本来の使い方をしないので、環境やバージョンによってはプラグイン自体の動作に影響があるかもしれません。 もし、影響があっても責任を取れませんので、自己責任で行ってください。 また、今回は運用でカバーできないという判断があって既存のプラグインを改造しています。 既存のプラグインを変更する事は、動作の保証ができなくなるという事です。 安易に改造せず、運用でカバーする事も視野に入れてください。

状況

Wordpressのユーザー情報のインポートに「import-users-from-csv」というプラグインを使用していました。 実際に運用する人は、インポートするCSVを作る時、WindowsExcelを使用しています。 (設定次第かもしれませんが)そのためか、改行コードがCR(\r)になってしまいます。 実際に動くサーバーがlinuxな事、このプラグインがCRの改行に対応していない事もありプラグインで改行できない現象が起きました。

実践

本来のプラグインについては、import-users-from-csv」をみてください。 また、実際の変更箇所にについてはgithubにソースを載せています。

github

https://github.com/websandbag/issue_update_Import_Users_from_csv

改修方針

  • fopenして、fgetcで解析をするまでの間に、改行コードをLF(\n)に書き換える。
  • 既存の処理を出来る限り書き換えない。

この対応をするにあたり、2つの方法を試みました。 結局、後者の方で対応したのですが、このやり方は後々使えそうなので、備忘録としてどちらも記載します。

方法1 テンポラリファイルを作ってそこにCSVの内容を流し込む。

POSTされたfileデータのテンポラリを書き換えられるのか疑問に思った時に思いついた方法です。 下記のように、テンポラリを作ってfwriteで、置換したファイルを書き出しそれを処理のファイルに渡すという方法です。 理論上は、同じ型で中身は改行コード以外同じなので、期待した結果が得られると思いましたが、ファイルを解析した結果NULLで帰ってきました。

public function changeEndOfLineFromRToN($filename)
{
    $temp = tmpfile();
    $strings = file_get_contents($filename);
    fwrite($temp, preg_replace("/\r\n|\r|\n/", "\n", $strings));
    return $temp;
}

解決できなかった気になる点

  • 同じresource型のファイルですが、fopenで開いたresourceは「ストリームリソース」と呼ばれる状態のようです。 これは引き続き調査しています。

    resource(8) of type (stream)

  • 仮に、fopenしたとしてfcloseはどうするのか?
    参照渡しをすれば解決出来るかもしれませんが、上記が解決できなかったので保留にしています。

方法2 POSTのテンポラリファイルを直に書き換える。

POSTされたテンポラリファイルを直接書き換えてしまいます。  有効期限の心配もありましたが、書き換えは下記の処理で実現できました。 テンポラリを直接書き換えているので、ライブラリの処理でもれなくリソースの解放をします。

public function changeEndOfLineFromCRToLF($filename)
{
    $current = file_get_contents($filename);
    $current = preg_replace("/\r\n|\r|\n/", "\n", $current);
    file_put_contents($filename, $current);
    return $fileName;
}

置換をする時に注意する事

preg_replaceで改行コードを置換するのですが、改行コードを扱う場合は、ダブルコーテーション(")で要素を囲む必要があります。 シングルコーテーションで囲んだ場合、「\n」という文字で出力されてしまうからです。

$current = preg_replace("/\r\n|\r|\n/", "\n", $current);

参考URL

https://qiita.com/dayone80/items/00ab49e2d02be6a3bc87