解决使用phpspreadsheet导出Xlsx纯数字字符串变成科学计数法的问题

Demo:

getActiveSheet()->setCellValue('A1', '900473179110024');
$xlsx = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$xlsx->save('test.xlsx');

执行后打开生成的Excel如下图,在A1格子里本该出现的值是900473179110024,但是实际上输出的excel却变成了科学计数法,将它变成字符后也是900473179110020,与期待值并不相同
解决使用phpspreadsheet导出Xlsx纯数字字符串变成科学计数法的问题_第1张图片

解决方案:
在经过一步步追踪后发现,phpspreadsheet在记录这个值之前做了类型判断,以下截取部分源码做分析:

setValueExplicit($value, static::dataTypeForValue($value)); // 900473179110024 在dataTypeForValue这个方法里面返回的类型是DataType::TYPE_NUMERIC
        // Done!
        return true;
    }
    public static function dataTypeForValue($pValue)
    {
        // Match the value against a few data types
        if ($pValue === null) {
            return DataType::TYPE_NULL;
        } elseif ($pValue === '') {
            return DataType::TYPE_STRING;
        } elseif ($pValue instanceof RichText) {
            return DataType::TYPE_INLINE;
        } elseif ($pValue[0] === '=' && strlen($pValue) > 1) {
            return DataType::TYPE_FORMULA;
        } elseif (is_bool($pValue)) {
            return DataType::TYPE_BOOL;
        } elseif (is_float($pValue) || is_int($pValue)) {
            return DataType::TYPE_NUMERIC;
        } elseif (preg_match('/^[\+\-]?(\d+\\.?\d*|\d*\\.?\d+)([Ee][\-\+]?[0-2]?\d{1,3})?$/', $pValue)) {
            $tValue = ltrim($pValue, '+-');
            if (is_string($pValue) && $tValue[0] === '0' && strlen($tValue) > 1 && $tValue[1] !== '.') {
                return DataType::TYPE_STRING;
            } elseif ((strpos($pValue, '.') === false) && ($pValue > PHP_INT_MAX)) {
                return DataType::TYPE_STRING;
            }

            return DataType::TYPE_NUMERIC; //判断900473179110024 返回TYPE_NUMERIC
        } elseif (is_string($pValue)) {
            $errorCodes = DataType::getErrorCodes();
            if (isset($errorCodes[$pValue])) {
                return DataType::TYPE_ERROR;
            }
        }

        return DataType::TYPE_STRING;
    }
    ...
}

value = (float) $pValue; // 调用到这里时TYPE_NUMERIC类型会被强制转换为float,所以900473179110024会变成9E+14

                break;
            ...
        }
        // set the datatype
        $this->dataType = $pDataType;
        return $this->updateInCollection();
    }
}

可以发现最主要是dataTypeForValue这个方法返回的类型出了问题,所以只要重写dataTypeForValue这个方法就能比较好的解决这个问题了,由于我们不能直接去修改phpspreadsheet库的代码,所以我另写了一个类去继承DefaultValueBinder只重写相应的方法,然后把类的实例作为Cell的valueBinder即可,具体如下

自定义类:

 1) {
            return DataType::TYPE_FORMULA;
        } elseif (is_bool($pValue)) {
            return DataType::TYPE_BOOL;
        } elseif (is_float($pValue) || is_int($pValue)) {
            return DataType::TYPE_NUMERIC;
        }
        return DataType::TYPE_STRING;
    }
}

调用示例:

getActiveSheet()->setCellValue('A1', '900473179110024');
$xlsx = new \PhpOffice\PhpSpreadsheet\Writer\Xlsx($spreadsheet);
$xlsx->save('test.xlsx');

你可能感兴趣的:(Php)