将整数转换为Excel列(字母或字母组合)-完整的PHP示例、单元测试和解释
假设我们有这样一个函数,它接受PDO语句(任何查询)并自动生成excel文件(使用PHPExcel库): 然后打开与excel文件的连接:将整数转换为Excel列(字母或字母组合)-完整的PHP示例、单元测试和解释,php,excel,auto-generate,letter,pdostatement,Php,Excel,Auto Generate,Letter,Pdostatement,假设我们有这样一个函数,它接受PDO语句(任何查询)并自动生成excel文件(使用PHPExcel库): 然后打开与excel文件的连接: $excel = new PHPExcel(); $writer = PHPExcel_IOFactory::createWriter($excel, "Excel2007"); $sheet = $excel->getActiveSheet(); $sheet->setTitle($name); 此函数用于确保列名数组的长度与行数组中的
$excel = new PHPExcel();
$writer = PHPExcel_IOFactory::createWriter($excel, "Excel2007");
$sheet = $excel->getActiveSheet();
$sheet->setTitle($name);
此函数用于确保列名数组的长度与行数组中的字段数相同
/**
* Take the array containing the $columnName for data export (CSV, Excel) and make sure
* that it is the number of entry as there are fields in $row.
*
* If column name are missing, we will use the column name used in the query.
*
* Return the merged array
*
* @param $columnName Array containing the column names
* @param $row Array produce by fetch(PDO::FETCH_ASSOC).
*
* @return Array ($columnName)
*/
private static function validateColumnNameArray(array &$columnName, array &$row){
$buffer = array();
$colPDO = count($row);
$count = count($columnName);
if ($count < $colPDO){
foreach ($row as $key => $value){
$buffer[] = $key;
}
for($index = $count; $index < $colPDO; $index++){
$columnName[] = $buffer[$index];
}
}
unset($buffer);
return $columnName;
}
如果它是我们添加到excel文件的第一行,那么我们设置基本格式:
if ($rows === 0){
$columnName = self::validateColumnNameArray($columnName, $row);
$totalCols = count($row);
$sheet->getStyle('A1:' . self::convertNumberToExcelCol($totalCols) . '1')->getFont()->setBold(true)->setSize(12);
for ($column = 1; $column <= $totalCols; $column++){
$sheet->getCell(self::convertNumberToExcelCol($column) . '1')->setValue($columnName[$column - 1]);
$sheet->getColumnDimension(self::convertNumberToExcelCol($column))->setAutoSize(true);
}
$rows = 1;
}
我个人只在午夜对源文件和数据库进行自动备份时才使用这种方法。白天运行它会增加删除其他用户使用的文件的机会
这就是为什么我正在考虑最佳做法,即在通过浏览器发送给用户时立即删除文件,并将清理方法仅用于维护目的
如果没有错误,我将行数减少1,因为我不想计算标题行。如果将标题行看作数据行,则可以删除该行。
最后,这些方法返回访问新创建文件的路径:
return (empty($errorMessage) ? $path : "");
但前提是没有错误。因此,如果函数返回空字符串,则表示发生了错误
由于PHP的类型松散,您可以返回任何内容,包括布尔值,甚至错误消息,但出于一致性目的,我更喜欢始终返回相同的数据类型。我个人最喜欢的方法是布尔返回值和通过引用传递的错误消息变量。所以我可以使用这样的代码:
$errorMessage = "";
if ($_SESSION["adminAccount"]->updateAccountInfo($errorMessage,
(isset($_POST['FIRST_NAME_TEXT']) ? $_POST['FIRST_NAME_TEXT'] : $_SESSION["adminAccount"]->getFirstName()),
(isset($_POST['LAST_NAME_TEXT']) ? $_POST['LAST_NAME_TEXT'] : $_SESSION["adminAccount"]->getLastName()),
(isset($_POST['EMAIL_TEXT']) ? $_POST['EMAIL_TEXT'] : $_SESSION["adminAccount"]->getEmail()))){
PageManager::displaySuccessMessage("Your account information were saved with success.", "USER_ACCOUNT_INFORMATION_SAVED");
}
else{
PageManager::displayErrorMessage($errorMessage);
}
这样,错误由类方法在内部管理,并且可以根据视图上下文调整成功消息。布尔返回值用于确定是否必须显示错误或成功消息
注意:单元测试将包含在我的答案中
来自蒙特利尔的Jonathan Parent-Lévesque现在有一种方法可以将整数转换为excel列 excel列可以是一到三个字母的组合,最多可达16383(XFD),这是excel文件中列的当前限制:
$excel = new PHPExcel();
$writer = PHPExcel_IOFactory::createWriter($excel, "Excel2007");
$sheet = $excel->getActiveSheet();
$sheet->setTitle($name);
我在函数旁边使用一个包含所有英文字母的数组:
public static $letters = array(1 => "A", 2 => "B", 3=> "C", 4 => "D", 5 => "E", 6 => "F", 7 => "G", 8=> "H", 9=> "I", 10 => "J", 11 =>"K", 12 => "L", 13 => "M", 14 => "N", 15=> "O", 16 => "P", 17 => "Q", 18 => "R", 19 => "S", 20 => "T", 21 => "U", 22 => "V", 23 => "W", 24 => "X", 25 => "Y", 26 => "Z");
该函数接受单个参数,并断言该值为数值,并且在以下限制之间:
public static function convertNumberToExcelCol($number){
$column = "";
if (is_numeric($number) and $number > 0 and $number < 16385){
让我们测试一下:
for ($index = 1; $index < 27; $index++){
$this->assertEquals(FileManager::$letters[$index], FileManager::convertNumberToExcelCol($index));
}
由于英文字母表包含26个字母,因此整个算法也基于26个字母
对于大多数值,我们只需将[number]/26四舍五入即可得到第一个字母,使用数字[number]%26的整个除法(除法的剩余部分)即可得到第二个字母
$first = floor($number / 26);
$second = $number % 26;
以下是一些示例:
- 27=AA
- 28=AB
- 51=AY
- 53=BA
$first = floor($number / 26) - 1;
$second = 26;
测试时间:
for ($first = 1; $first < 27; $first++){
$temp = $first * 26;
for ($second = 1; $second < 27; $second++){
$this->assertEquals(FileManager::$letters[$first] . FileManager::$letters[$second], FileManager::convertNumberToExcelCol($temp + $second));
}
}
($first=1;$first<27;$first++)的{
$temp=$first*26;
对于($second=1;$second<27;$second++){
$this->assertEquals(文件管理器::$letters[$first]。文件管理器::$letters[$second],文件管理器::convertNumberToExcelCol($temp+$second));
}
}
当我们试图管理三封信时,真正的挑战来了。当然,您可能不会有一个超过702个字段的查询,但是为了该方法的完整性和一些真正的编程挑战,让我们检查一下如何做到这一点
起初,我不得不尝试进行错误测试,最后进行了以下编码:
elseif($number < 1379){
$column = self::$letters[floor($number / 702)] . self::convertNumberToExcelCol($number % 702 + 26);
}
elseif($number < 2028){
$column = self::$letters[floor($number / 676)] . self::convertNumberToExcelCol($number % 676);
}
elseif ($number < 2055){
$column = self::$letters[floor($number / 702)] . self::convertNumberToExcelCol($number % 702 + 52);
}
elseif($number < 2704){
$column = self::$letters[floor($number / 676)] . self::convertNumberToExcelCol($number % 676);
}
elseif ($number < 2731) {
$column = self::$letters[floor($number / 702)] . self::convertNumberToExcelCol($number % 702 + 78);
}
elseif ($number < 3380) {
$column = self::$letters[floor($number / 676)] . self::convertNumberToExcelCol($number % 676);
}
elseif ($number < 3407){
$column = self::$letters[floor($number / 702)] . self::convertNumberToExcelCol($number % 702 + 104);
}
elseif($number<1379){
$column=self::$letters[楼层($number/702)]。self:$convertNumberToExcelCol($number%702+26);
}
elseif(数量<2028){
$column=self::$letters[floor($number/676)]。self::convertNumberToExcelCol($number%676);
}
elseif(数量<2055美元){
$column=self::$letters[楼层($number/702)]。self:$convertNumberToExcelCol($number%702+52);
}
elseif(数量<2704美元){
$column=self::$letters[floor($number/676)]。self::convertNumberToExcelCol($number%676);
}
elseif(数量<2731){
$column=self::$letters[楼层($number/702)]。self:$convertNumberToExcelCol($number%702+78);
}
elseif(数量<3380美元){
$column=self::$letters[floor($number/676)]。self::convertNumberToExcelCol($number%676);
}
elseif($3407){
$column=self::$letters[楼层($number/702)]。self:$convertNumberToExcelCol($number%702+104);
}
是的,这并不严重,你可以像这样一直走到16k
如果你仔细观察,你会发现有一个模式从中画出来。有一个数字可以除以676得到第一个字母,模676得到第二个和第三个字母。例如,2027=BBY
第二种模式是数字,第一个字母可以除以702+a补偿(26、52、78、104,…)的模。包括像703=AAA这样的数字
当然,676和702都是26的倍数
我花了很多时间计算,但我开始意识到第二种模式总是27个数字的范围,这些数字总是产生一个低于27(0-26)的数字作为这个数字的模
elseif($number < 2028){
$column = self::$letters[floor($number / 676)] . self::convertNumberToExcelCol($number % 676);
}
elseif ($number < 2055){
elseif($number<2028){
$column=self::$letters[floor($number/676)]。self::convertNumberToExcelCol($number%676);
}
elseif(数量<2055美元){
举例来说:
- 2028%676=0
- 2029%676=1
- 2054%676=26
- 2055%676=27(现在超出范围)
if ($number % 26 === 0){
$first = floor($number / 26) - 1;
$second = 26;
}
else{
$first = floor($number / 26);
$second = $number % 26;
}
$column = self::$letters[$first] . self::$letters[$second];
$first = floor($number / 26);
$second = $number % 26;
$first = floor($number / 26) - 1;
$second = 26;
for ($first = 1; $first < 27; $first++){
$temp = $first * 26;
for ($second = 1; $second < 27; $second++){
$this->assertEquals(FileManager::$letters[$first] . FileManager::$letters[$second], FileManager::convertNumberToExcelCol($temp + $second));
}
}
elseif($number < 1379){
$column = self::$letters[floor($number / 702)] . self::convertNumberToExcelCol($number % 702 + 26);
}
elseif($number < 2028){
$column = self::$letters[floor($number / 676)] . self::convertNumberToExcelCol($number % 676);
}
elseif ($number < 2055){
$column = self::$letters[floor($number / 702)] . self::convertNumberToExcelCol($number % 702 + 52);
}
elseif($number < 2704){
$column = self::$letters[floor($number / 676)] . self::convertNumberToExcelCol($number % 676);
}
elseif ($number < 2731) {
$column = self::$letters[floor($number / 702)] . self::convertNumberToExcelCol($number % 702 + 78);
}
elseif ($number < 3380) {
$column = self::$letters[floor($number / 676)] . self::convertNumberToExcelCol($number % 676);
}
elseif ($number < 3407){
$column = self::$letters[floor($number / 702)] . self::convertNumberToExcelCol($number % 702 + 104);
}
elseif($number < 2028){
$column = self::$letters[floor($number / 676)] . self::convertNumberToExcelCol($number % 676);
}
elseif ($number < 2055){
if($number % 676 < 27){
$compensation = floor($number / 26) - 26;
$column = self::$letters[floor($number / 702)] . self::convertNumberToExcelCol($number % 702 + ($compensation % 26 === 0 ? $compensation : $compensation - 1));
}
else{
$column = self::$letters[floor($number / 676)] . self::convertNumberToExcelCol($number % 676);
}
for ($first = 1; $first < 27; $first++){
for ($second = 1; $second < 27; $second++){
for ($third = 1; $third < 27; $third++){
$temp = $first * 676 + (($second * 26) + $third);
if ($temp < 16385){
$this->assertEquals(FileManager::$letters[$first] . FileManager::$letters[$second] . FileManager::$letters[$third], FileManager::convertNumberToExcelCol($temp));
}
else{
$this->assertEmpty(FileManager::convertNumberToExcelCol($temp + $index));
}
}
}
}
/**
* Convert a $number to the letter (or combination of letters) representing a column in excel.
* Will return an empty string if $number is not a valid value.
*
* @param number Int must be is_numeric() and > 0 and < 16,385.
*
* @return String
*/
public static function convertNumberToExcelCol($number){
$column = "";
if (is_numeric($number) and $number > 0 and $number < 16385){
if ($number < 27){
$column = self::$letters[$number];
}
elseif ($number < 703){
if ($number % 26 === 0){
$first = floor($number / 26) - 1;
$second = 26;
}
else{
$first = floor($number / 26);
$second = $number % 26;
}
$column = self::$letters[$first] . self::$letters[$second];
}
else{
if($number % 676 < 27){
$compensation = floor($number / 26) - 26;
$column = self::$letters[floor($number / 702)] . self::convertNumberToExcelCol($number % 702 + ($compensation % 26 === 0 ? $compensation : $compensation - 1));
}
else{
$column = self::$letters[floor($number / 676)] . self::convertNumberToExcelCol($number % 676);
}
}
}
return $column;
}
public function testvalidateFileExtention(){
//invalid extension
$this->assertEquals("woot", FileManager::validateFileExtention("woot", true));
$this->assertEquals("woot", FileManager::validateFileExtention("woot", ""));
$this->assertEquals("woot", FileManager::validateFileExtention("woot", "."));
$this->assertEquals("woot.blu", FileManager::validateFileExtention("woot", ".b.l.u.."));
//invalid name
$this->assertEquals("my_file.blu", FileManager::validateFileExtention(true, ".blu"));
$this->assertEquals("my_file.blu", FileManager::validateFileExtention("", ".blu"));
$this->assertEquals("my_file.blu", FileManager::validateFileExtention(".woot", ".blu"));
$this->assertEquals("woot.blu", FileManager::validateFileExtention("w.o.o.t.", ".blu"));
//valid file name and extension
$this->assertEquals("woot.blu", FileManager::validateFileExtention("woot", "blu"));
$this->assertEquals("woot.blu", FileManager::validateFileExtention("woot", ".blu"));
}
public function testCreateCSVFromRS(){
FileManager::emptyArchiveFolder();
$errorMessage = "";
$path = realpath(dirname(__FILE__)) . '/../Archive/woot.csv';
$rows = 0;
//no data to export
$rs = $this->fetchData("SELECT id, field_id, field_value, language FROM error_message_table WHERE field_id='woot for loots'");
$this->assertInstanceOf('PDOStatement', $rs);
$this->assertEmpty(FileManager::createCSVFromRS($errorMessage, $rs, $rows, "woot"));
$this->assertNotEmpty($errorMessage);
$this->assertEquals(0, $rows);
$this->assertFileNotExists($path);
//data, but missing columns in the header array
$rs = $this->fetchData("SELECT id, field_id, field_value, language FROM error_message_table LIMIT 100");
$this->assertNotEmpty(FileManager::createCSVFromRS($errorMessage, $rs, $rows, "woot", array("homer", "simpson")));
$this->assertInstanceOf('PDOStatement', $rs);
$this->assertEmpty($errorMessage);
$this->assertEquals(100, $rows);
$this->assertFileExists($path);
$handle = fopen($path, "r");
$this->assertNotEquals(false, $handle);
$row = fgetcsv($handle);
$this->assertContains("homer", $row);
$this->assertNotContains("id", $row);
$this->assertContains("simpson", $row);
$this->assertNotContains("field_id", $row);
$this->assertContains("field_value", $row);
$this->assertContains("language", $row);
fclose($handle);
//data, changing all columns in the header array
$rs = $this->fetchData("SELECT id, field_id, field_value, language FROM error_message_table LIMIT 10");
$this->assertNotEmpty(FileManager::createCSVFromRS($errorMessage, $rs, $rows, "woot", array("kyle", "eric", "kenny", "stan")));
$this->assertInstanceOf('PDOStatement', $rs);
$this->assertEmpty($errorMessage);
$this->assertEquals(10, $rows);
$this->assertFileExists($path);
$handle = fopen($path, "r");
$this->assertNotEquals(false, $handle);
$row = fgetcsv($handle);
$this->assertContains("kyle", $row);
$this->assertNotContains("id", $row);
$this->assertContains("eric", $row);
$this->assertNotContains("field_id", $row);
$this->assertContains("kenny", $row);
$this->assertNotContains("field_value", $row);
$this->assertContains("stan", $row);
$this->assertNotContains("language", $row);
fclose($handle);
unlink($path);
}
public function testConvertNumberToExcelCol(){
//invalid paramter
$this->assertEmpty(FileManager::convertNumberToExcelCol("a"));
$this->assertEmpty(FileManager::convertNumberToExcelCol(array()));
$this->assertEmpty(FileManager::convertNumberToExcelCol(-1));
$this->assertEmpty(FileManager::convertNumberToExcelCol(1000000000));
//single letter
for ($index = 1; $index < 27; $index++){
$this->assertEquals(FileManager::$letters[$index], FileManager::convertNumberToExcelCol($index));
}
//double letters
for ($first = 1; $first < 27; $first++){
$temp = $first * 26;
for ($second = 1; $second < 27; $second++){
$this->assertEquals(FileManager::$letters[$first] . FileManager::$letters[$second], FileManager::convertNumberToExcelCol($temp + $second));
}
}
//tripple letters
for ($first = 1; $first < 27; $first++){
for ($second = 1; $second < 27; $second++){
for ($third = 1; $third < 27; $third++){
$temp = $first * 676 + (($second * 26) + $third);
if ($temp < 16385){
$this->assertEquals(FileManager::$letters[$first] . FileManager::$letters[$second] . FileManager::$letters[$third], FileManager::convertNumberToExcelCol($temp));
}
else{
$this->assertEmpty(FileManager::convertNumberToExcelCol($temp + $index));
}
}
}
}
}
public function testCreateExcelFromRS(){
$errorMessage = "";
$path = realpath(dirname(__FILE__)) . '/../Archive/woot.xlsx';
$rows = 0;
//no data to export
$rs = $this->fetchData("SELECT id, field_id, field_value, language FROM error_message_table WHERE field_id='woot for loots'");
$this->assertInstanceOf('PDOStatement', $rs);
$this->assertEmpty(FileManager::createExcelFromRS($errorMessage, $rs, $rows, "woot"));
$this->assertNotEmpty($errorMessage);
$this->assertEquals(0, $rows);
$this->assertFileNotExists($path);
//data, but missing columns in the header array
$rs = $this->fetchData("SELECT id, field_id, field_value, language FROM error_message_table LIMIT 100");
$this->assertNotEmpty(FileManager::createExcelFromRS($errorMessage, $rs, $rows, "woot", array("homer", "simpson")));
$this->assertInstanceOf('PDOStatement', $rs);
$this->assertEmpty($errorMessage);
$this->assertEquals(100, $rows);
$this->assertFileExists($path);
$reader = PHPExcel_IOFactory::createReaderForFile($path);
$reader->setReadDataOnly(true);
$excel = $reader->load($path);
$this->assertEquals("homer", $excel->getSheet(0)->getCell('A1')->getValue());
$this->assertEquals("simpson", $excel->getSheet(0)->getCell('B1')->getValue());
$this->assertEquals("field_value", $excel->getSheet(0)->getCell('C1')->getValue());
$this->assertContains("language", $excel->getSheet(0)->getCell('D1')->getValue());
$excel->disconnectWorksheets();
unset($excel);
//data, changing all columns in the header array
$rs = $this->fetchData("SELECT id, field_id, field_value, language FROM error_message_table LIMIT 10");
$this->assertNotEmpty(FileManager::createExcelFromRS($errorMessage, $rs, $rows, "woot", array("kyle", "eric", "kenny", "stan")));
$this->assertInstanceOf('PDOStatement', $rs);
$this->assertEmpty($errorMessage);
$this->assertEquals(10, $rows);
$this->assertFileExists($path);
$reader = PHPExcel_IOFactory::createReaderForFile($path);
$reader->setReadDataOnly(true);
$excel = $reader->load($path);
$this->assertEquals("kyle", $excel->getSheet(0)->getCell('A1')->getValue());
$this->assertEquals("eric", $excel->getSheet(0)->getCell('B1')->getValue());
$this->assertEquals("kenny", $excel->getSheet(0)->getCell('C1')->getValue());
$this->assertContains("stan", $excel->getSheet(0)->getCell('D1')->getValue());
$excel->disconnectWorksheets();
unlink($path);
}
private function fetchData($query, $db = "language_manager"){
$_SESSION['domain'] = $db;
$errorMessage = "";
$dbManager = GeneralDBManager::getInstance();
$rs = $dbManager->fetchData($query . ";/*th1s 1s a v4l1d qu3ry*/", $errorMessage);
unset($dbManager);
return $rs;
}