1 |
efrain |
1 |
<?php
|
|
|
2 |
|
|
|
3 |
declare(strict_types=1);
|
|
|
4 |
|
|
|
5 |
namespace OpenSpout\Reader\XLSX\Helper;
|
|
|
6 |
|
|
|
7 |
use OpenSpout\Common\Exception\InvalidArgumentException;
|
|
|
8 |
|
|
|
9 |
/**
|
|
|
10 |
* @internal
|
|
|
11 |
*/
|
|
|
12 |
final class CellHelper
|
|
|
13 |
{
|
|
|
14 |
// Using ord() is super slow... Using a pre-computed hash table instead.
|
|
|
15 |
private const columnLetterToIndexMapping = [
|
|
|
16 |
'A' => 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6,
|
|
|
17 |
'H' => 7, 'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13,
|
|
|
18 |
'O' => 14, 'P' => 15, 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20,
|
|
|
19 |
'V' => 21, 'W' => 22, 'X' => 23, 'Y' => 24, 'Z' => 25,
|
|
|
20 |
];
|
|
|
21 |
|
|
|
22 |
/**
|
|
|
23 |
* Returns the base 10 column index associated to the cell index (base 26).
|
|
|
24 |
* Excel uses A to Z letters for column indexing, where A is the 1st column,
|
|
|
25 |
* Z is the 26th and AA is the 27th.
|
|
|
26 |
* The mapping is zero based, so that A1 maps to 0, B2 maps to 1, Z13 to 25 and AA4 to 26.
|
|
|
27 |
*
|
|
|
28 |
* @param string $cellIndex The Excel cell index ('A1', 'BC13', ...)
|
|
|
29 |
*
|
|
|
30 |
* @throws InvalidArgumentException When the given cell index is invalid
|
|
|
31 |
*/
|
|
|
32 |
public static function getColumnIndexFromCellIndex(string $cellIndex): int
|
|
|
33 |
{
|
|
|
34 |
if (!self::isValidCellIndex($cellIndex)) {
|
|
|
35 |
throw new InvalidArgumentException('Cannot get column index from an invalid cell index.');
|
|
|
36 |
}
|
|
|
37 |
|
|
|
38 |
$columnIndex = 0;
|
|
|
39 |
|
|
|
40 |
// Remove row information
|
|
|
41 |
$columnLetters = preg_replace('/\d/', '', $cellIndex);
|
|
|
42 |
|
|
|
43 |
// strlen() is super slow too... Using isset() is way faster and not too unreadable,
|
|
|
44 |
// since we checked before that there are between 1 and 3 letters.
|
|
|
45 |
$columnLength = isset($columnLetters[1]) ? (isset($columnLetters[2]) ? 3 : 2) : 1;
|
|
|
46 |
|
|
|
47 |
// Looping over the different letters of the column is slower than this method.
|
|
|
48 |
// Also, not using the pow() function because it's slooooow...
|
|
|
49 |
switch ($columnLength) {
|
|
|
50 |
case 1:
|
|
|
51 |
$columnIndex = self::columnLetterToIndexMapping[$columnLetters];
|
|
|
52 |
|
|
|
53 |
break;
|
|
|
54 |
|
|
|
55 |
case 2:
|
|
|
56 |
$firstLetterIndex = (self::columnLetterToIndexMapping[$columnLetters[0]] + 1) * 26;
|
|
|
57 |
$secondLetterIndex = self::columnLetterToIndexMapping[$columnLetters[1]];
|
|
|
58 |
$columnIndex = $firstLetterIndex + $secondLetterIndex;
|
|
|
59 |
|
|
|
60 |
break;
|
|
|
61 |
|
|
|
62 |
case 3:
|
|
|
63 |
$firstLetterIndex = (self::columnLetterToIndexMapping[$columnLetters[0]] + 1) * 676;
|
|
|
64 |
$secondLetterIndex = (self::columnLetterToIndexMapping[$columnLetters[1]] + 1) * 26;
|
|
|
65 |
$thirdLetterIndex = self::columnLetterToIndexMapping[$columnLetters[2]];
|
|
|
66 |
$columnIndex = $firstLetterIndex + $secondLetterIndex + $thirdLetterIndex;
|
|
|
67 |
|
|
|
68 |
break;
|
|
|
69 |
}
|
|
|
70 |
|
|
|
71 |
return $columnIndex;
|
|
|
72 |
}
|
|
|
73 |
|
|
|
74 |
/**
|
|
|
75 |
* Returns whether a cell index is valid, in an Excel world.
|
|
|
76 |
* To be valid, the cell index should start with capital letters and be followed by numbers.
|
|
|
77 |
* There can only be 3 letters, as there can only be 16,384 rows, which is equivalent to 'XFE'.
|
|
|
78 |
*
|
|
|
79 |
* @param string $cellIndex The Excel cell index ('A1', 'BC13', ...)
|
|
|
80 |
*/
|
|
|
81 |
private static function isValidCellIndex(string $cellIndex): bool
|
|
|
82 |
{
|
|
|
83 |
return 1 === preg_match('/^[A-Z]{1,3}\d+$/', $cellIndex);
|
|
|
84 |
}
|
|
|
85 |
}
|