脳みそスワップアウト

揮発性なもので。おもにPHPのこととか。

Windows版php-7.1 で UTF8 の CSV をパースする

WindowsPHP(xampp) の fgetcsv() が、php-5.6 と php-7.1 で挙動が変わったことがわかった。
たぶん php-7.0 から。

fgetcsv() はlocaleに依存する関数なので、
UTF8のCSVを読み込むなら ja_JP.UTF-8 を指定する必要があるとマニュアルにもあるのだけど、
Windowsではそもそも UTF-8 ロケールを指定できない。

php-5.6 では特に何も指定せず、デフォルトの Japanese_Japan.932 のままでもUTF8のファイルを読み込めていた・・たぶん。
手元の開発環境でしかないから、テストケースが少なかった可能性はあるけども。

ともかく挙動は変わっている。
php7以降では、Windowsの場合は C ロケールを設定する必要がある模様。

挙動が違う例

<?php
$strCsv = '"サーロイン","カルビ","リブロース"';

$fp = fopen('php://memory', 'wb');
fputs($fp, $strCsv);
rewind($fp);
$arrCsv = fgetcsv($fp);
fclose($fp);

var_dump($arrCsv);

実行結果(php-5.6.8 xampp)

array(3) {
  [0] =>
  string(15) "サーロイン"
  [1] =>
  string(9) "カルビ"
  [2] =>
  string(15) "リブロース"
}

実行結果(php-7.1.1 xampp)

array(2) {
  [0] =>
  string(15) "サーロイン"
  [1] =>
  string(27) "カルビ",リブロース""
}

対応策

UTF8 ロケールがないからどうしようと思ったけど、C ロケールにしたらうまくいった模様。
テストケースはこの3枚の肉だけだけども。

<?php
// php7だとこれが必要ぽい
if(0 === strpos(PHP_OS, 'WIN')) {
  setlocale(LC_CTYPE, 'C');
}
var_dump(setlocale(LC_ALL, 0));


$strCsv = '"サーロイン","カルビ","リブロース"';

$fp = fopen('php://memory', 'wb');
fputs($fp, $strCsv);
rewind($fp);
$arrCsv = fgetcsv($fp);
fclose($fp);

var_dump($arrCsv);
LC_CTYPE php5.6 php7.1
Japanese_Japan.932 (default) ×
C

Windows + PHP は2017年になっても何かと面倒。