文字コード判定が上手く行かないケースがあったので、もう少し粘るようなアルゴリズムにした

This commit is contained in:
shibafu 2020-02-16 17:01:16 +09:00
parent 7259ee3647
commit 45eba30528
1 changed files with 48 additions and 5 deletions

View File

@ -28,11 +28,7 @@ class CheckinCsvImporter
public function execute()
{
// Guess charset
$head = file_get_contents($this->filename, false, null, 0, 1024);
$charset = mb_detect_encoding($head, ['ASCII', 'UTF-8', 'SJIS-win'], true);
if (array_search($charset, ['UTF-8', 'SJIS-win'], true) === false) {
throw new CsvImportException(['文字コード判定に失敗しました。UTF-8 (BOM無し) または Shift_JIS をお使いください。']);
}
$charset = $this->guessCharset($this->filename);
// Open CSV
$csv = Reader::createFromPath($this->filename, 'r');
@ -101,4 +97,51 @@ class CheckinCsvImporter
}
});
}
/**
* 指定されたファイルを読み込み、文字コードの判定を行います。
* @param string $filename CSVファイル名
* @param int $samplingLength ファイルの先頭から何バイトを判定に使用するかを指定
* @return string 検出した文字コード (UTF-8, SJIS-win, ...)
* @throws CsvImportException ファイルの読み込みに失敗した、文字コードを判定できなかった、または非対応文字コードを検出した場合にスロー
*/
private function guessCharset(string $filename, int $samplingLength = 1024): string
{
$fp = fopen($filename, 'rb');
if (!$fp) {
throw new CsvImportException(['CSVファイルの読み込み中にエラーが発生しました。']);
}
try {
$head = fread($fp, $samplingLength);
if ($head === false) {
throw new CsvImportException(['CSVファイルの読み込み中にエラーが発生しました。']);
}
for ($addition = 0; $addition < 4; $addition++) {
$charset = mb_detect_encoding($head, ['ASCII', 'UTF-8', 'SJIS-win'], true);
if ($charset) {
if (array_search($charset, ['UTF-8', 'SJIS-win'], true) === false) {
throw new CsvImportException(['文字コード判定に失敗しました。UTF-8 (BOM無し) または Shift_JIS をお使いください。']);
} else {
return $charset;
}
}
// 1バイト追加で読み込んだら、文字境界に到達して上手く判定できるかもしれない
if (feof($fp)) {
break;
}
$next = fread($fp, 1);
if ($next === false) {
throw new CsvImportException(['CSVファイルの読み込み中にエラーが発生しました。']);
}
$head .= $next;
}
throw new CsvImportException(['文字コード判定に失敗しました。UTF-8 (BOM無し) または Shift_JIS をお使いください。']);
} finally {
fclose($fp);
}
}
}