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