Helper class for the conversion of cell addresses between A1 and R1C1 formats, and vice-versa (#1558)
* Helper class for the conversion of cell addresses between A1 and R1C1 formats, and vice-versa
This commit is contained in:
parent
10a4a95d67
commit
a264cafe4c
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheet\Cell;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Exception;
|
||||
|
||||
class AddressHelper
|
||||
{
|
||||
/**
|
||||
* Converts an R1C1 format cell address to an A1 format cell address.
|
||||
*/
|
||||
public static function convertToA1(
|
||||
string $address,
|
||||
int $currentRowNumber = 1,
|
||||
int $currentColumnNumber = 1
|
||||
): string {
|
||||
$validityCheck = preg_match('/^(R(\[?-?\d*\]?))(C(\[?-?\d*\]?))$/i', $address, $cellReference);
|
||||
|
||||
if ($validityCheck === 0) {
|
||||
throw new Exception('Invalid R1C1-format Cell Reference');
|
||||
}
|
||||
|
||||
$rowReference = $cellReference[2];
|
||||
// Empty R reference is the current row
|
||||
if ($rowReference === '') {
|
||||
$rowReference = (string) $currentRowNumber;
|
||||
}
|
||||
// Bracketed R references are relative to the current row
|
||||
if ($rowReference[0] === '[') {
|
||||
$rowReference = $currentRowNumber + trim($rowReference, '[]');
|
||||
}
|
||||
$columnReference = $cellReference[4];
|
||||
// Empty C reference is the current column
|
||||
if ($columnReference === '') {
|
||||
$columnReference = (string) $currentColumnNumber;
|
||||
}
|
||||
// Bracketed C references are relative to the current column
|
||||
if ($columnReference[0] === '[') {
|
||||
$columnReference = $currentColumnNumber + trim($columnReference, '[]');
|
||||
}
|
||||
|
||||
if ($columnReference <= 0 || $rowReference <= 0) {
|
||||
throw new Exception('Invalid R1C1-format Cell Reference, Value out of range');
|
||||
}
|
||||
$A1CellReference = Coordinate::stringFromColumnIndex($columnReference) . $rowReference;
|
||||
|
||||
return $A1CellReference;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts an A1 format cell address to an R1C1 format cell address.
|
||||
* If $currentRowNumber or $currentColumnNumber are provided, then the R1C1 address will be formatted as a relative address.
|
||||
*/
|
||||
public static function convertToR1C1(
|
||||
string $address,
|
||||
?int $currentRowNumber = null,
|
||||
?int $currentColumnNumber = null
|
||||
): string {
|
||||
$validityCheck = preg_match('/^\$?([A-Z]{1,3})\$?(\d{1,7})$/i', $address, $cellReference);
|
||||
|
||||
if ($validityCheck === 0) {
|
||||
throw new Exception('Invalid A1-format Cell Reference');
|
||||
}
|
||||
|
||||
$columnId = Coordinate::columnIndexFromString($cellReference[1]);
|
||||
$rowId = (int) $cellReference[2];
|
||||
|
||||
if ($currentRowNumber !== null) {
|
||||
if ($rowId === $currentRowNumber) {
|
||||
$rowId = '';
|
||||
} else {
|
||||
$rowId = '[' . ($rowId - $currentRowNumber) . ']';
|
||||
}
|
||||
}
|
||||
|
||||
if ($currentColumnNumber !== null) {
|
||||
if ($columnId === $currentColumnNumber) {
|
||||
$columnId = '';
|
||||
} else {
|
||||
$columnId = '[' . ($columnId - $currentColumnNumber) . ']';
|
||||
}
|
||||
}
|
||||
|
||||
$R1C1Address = "R{$rowId}C{$columnId}";
|
||||
|
||||
return $R1C1Address;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,108 @@
|
|||
<?php
|
||||
|
||||
namespace PhpOffice\PhpSpreadsheetTests\Cell;
|
||||
|
||||
use PhpOffice\PhpSpreadsheet\Cell\AddressHelper;
|
||||
use PhpOffice\PhpSpreadsheet\Exception;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class AddressHelperTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* @dataProvider providerR1C1ConversionToA1Absolute
|
||||
*/
|
||||
public function testR1C1ConversionToA1Absolute(string $expectedValue, string $address): void
|
||||
{
|
||||
$actualValue = AddressHelper::convertToA1($address);
|
||||
|
||||
self::assertSame($expectedValue, $actualValue);
|
||||
}
|
||||
|
||||
public function providerR1C1ConversionToA1Absolute()
|
||||
{
|
||||
return require 'tests/data/Cell/R1C1ConversionToA1Absolute.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerR1C1ConversionToA1Relative
|
||||
*/
|
||||
public function testR1C1ConversionToA1Relative(string $expectedValue, string $address, ?int $row = null, ?int $column = null): void
|
||||
{
|
||||
$args = [$address];
|
||||
if ($row !== null) {
|
||||
$args[] = $row;
|
||||
}
|
||||
if ($column !== null) {
|
||||
$args[] = $column;
|
||||
}
|
||||
|
||||
$actualValue = AddressHelper::convertToA1(...$args);
|
||||
|
||||
self::assertSame($expectedValue, $actualValue);
|
||||
}
|
||||
|
||||
public function providerR1C1ConversionToA1Relative()
|
||||
{
|
||||
return require 'tests/data/Cell/R1C1ConversionToA1Relative.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerR1C1ConversionToA1Exception
|
||||
*/
|
||||
public function testR1C1ConversionToA1Exception(string $address): void
|
||||
{
|
||||
$this->expectException(Exception::class);
|
||||
|
||||
AddressHelper::convertToA1($address);
|
||||
}
|
||||
|
||||
public function providerR1C1ConversionToA1Exception()
|
||||
{
|
||||
return require 'tests/data/Cell/R1C1ConversionToA1Exception.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerA1ConversionToR1C1Absolute
|
||||
*/
|
||||
public function testA1ConversionToR1C1Absolute(string $expectedValue, string $address): void
|
||||
{
|
||||
$actualValue = AddressHelper::convertToR1C1($address);
|
||||
|
||||
self::assertSame($expectedValue, $actualValue);
|
||||
}
|
||||
|
||||
public function providerA1ConversionToR1C1Absolute()
|
||||
{
|
||||
return require 'tests/data/Cell/A1ConversionToR1C1Absolute.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerA1ConversionToR1C1Relative
|
||||
*/
|
||||
public function testA1ConversionToR1C1Relative(string $expectedValue, string $address, ?int $row = null, ?int $column = null): void
|
||||
{
|
||||
$actualValue = AddressHelper::convertToR1C1($address, $row, $column);
|
||||
|
||||
self::assertSame($expectedValue, $actualValue);
|
||||
}
|
||||
|
||||
public function providerA1ConversionToR1C1Relative()
|
||||
{
|
||||
return require 'tests/data/Cell/A1ConversionToR1C1Relative.php';
|
||||
}
|
||||
|
||||
/**
|
||||
* @dataProvider providerA1ConversionToR1C1Exception
|
||||
*/
|
||||
public function testA1ConversionToR1C1Exception(string $address): void
|
||||
{
|
||||
$this->expectException(Exception::class);
|
||||
|
||||
AddressHelper::convertToR1C1($address);
|
||||
}
|
||||
|
||||
public function providerA1ConversionToR1C1Exception()
|
||||
{
|
||||
return require 'tests/data/Cell/A1ConversionToR1C1Exception.php';
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
['R1C1', 'A1'],
|
||||
['R1C1', '$A$1'],
|
||||
['R1C1', '$A1'],
|
||||
['R1C1', 'A$1'],
|
||||
['R2C2', 'B2'],
|
||||
['R5C12', 'L5'],
|
||||
['R1024C26', 'Z1024'],
|
||||
['R2048C27', 'AA2048'],
|
||||
['R4096C52', 'AZ4096'],
|
||||
['R8192C53', 'BA8192'],
|
||||
['R65535C256', 'IV65535'],
|
||||
];
|
|
@ -0,0 +1,5 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
['A11Y'],
|
||||
];
|
|
@ -0,0 +1,19 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
['R[2]C[2]', 'O18', 16, 13],
|
||||
['R[-2]C[2]', 'O14', 16, 13],
|
||||
['R[2]C[-2]', 'K18', 16, 13],
|
||||
['R[-2]C[-2]', 'K14', 16, 13],
|
||||
['RC[3]', 'P16', 16, 13],
|
||||
['RC[-3]', 'J16', 16, 13],
|
||||
['R[4]C', 'M20', 16, 13],
|
||||
['R[-4]C', 'M12', 16, 13],
|
||||
['RC', 'E5', 5, 5],
|
||||
['R5C', 'E5', null, 5],
|
||||
['RC5', 'E5', 5, null],
|
||||
['R5C[2]', 'E5', null, 3],
|
||||
['R[2]C5', 'E5', 3, null],
|
||||
['R5C[-2]', 'E5', null, 7],
|
||||
['R[-2]C5', 'E5', 7, null],
|
||||
];
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
['A1', 'R1C1'],
|
||||
['B2', 'R2C2'],
|
||||
['L5', 'R5C12'],
|
||||
['Z1024', 'R1024C26'],
|
||||
['AA2048', 'R2048C27'],
|
||||
['AZ4096', 'R4096C52'],
|
||||
['BA8192', 'R8192C53'],
|
||||
['IV65535', 'R65535C256'],
|
||||
['K1', 'RC11'],
|
||||
['A16', 'R16C'],
|
||||
];
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
['RICE'],
|
||||
['R[-1]C'],
|
||||
['RC[-1]'],
|
||||
['R-1C'],
|
||||
['RC-1'],
|
||||
];
|
|
@ -0,0 +1,14 @@
|
|||
<?php
|
||||
|
||||
return [
|
||||
['C3', 'R[2]C[2]'],
|
||||
['O18', 'R[2]C[2]', 16, 13],
|
||||
['O14', 'R[-2]C[2]', 16, 13],
|
||||
['K18', 'R[2]C[-2]', 16, 13],
|
||||
['K14', 'R[-2]C[-2]', 16, 13],
|
||||
['P16', 'RC[3]', 16, 13],
|
||||
['J16', 'RC[-3]', 16, 13],
|
||||
['M20', 'R[4]C', 16, 13],
|
||||
['M12', 'R[-4]C', 16, 13],
|
||||
['E5', 'RC', 5, 5],
|
||||
];
|
Loading…
Reference in New Issue