diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a61951991..9e43131048 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/). - Support to read Xlsm templates with form elements, macros, printer settings, protected elements and back compatibility drawing, and save result without losing important elements of document - [#435](https://github.com/PHPOffice/PhpSpreadsheet/issues/435) - Expose sheet title maximum length as `Worksheet::SHEET_TITLE_MAXIMUM_LENGTH` - [#482](https://github.com/PHPOffice/PhpSpreadsheet/issues/482) +- Allow escape character to be set in CSV reader – [#492](https://github.com/PHPOffice/PhpSpreadsheet/issues/492) ### Fixed diff --git a/src/PhpSpreadsheet/Reader/Csv.php b/src/PhpSpreadsheet/Reader/Csv.php index 6adb6ee66e..eaece1d251 100644 --- a/src/PhpSpreadsheet/Reader/Csv.php +++ b/src/PhpSpreadsheet/Reader/Csv.php @@ -50,6 +50,13 @@ class Csv extends BaseReader */ private $contiguousRow = -1; + /** + * The character that can escape the enclosure. + * + * @var string + */ + private $escapeCharacter = '\\'; + /** * Create a new CSV Reader instance. */ @@ -254,7 +261,7 @@ public function listWorksheetInfo($pFilename) $worksheetInfo[0]['totalColumns'] = 0; // Loop through each line of the file in turn - while (($rowData = fgetcsv($fileHandle, 0, $this->delimiter, $this->enclosure)) !== false) { + while (($rowData = fgetcsv($fileHandle, 0, $this->delimiter, $this->enclosure, $this->escapeCharacter)) !== false) { ++$worksheetInfo[0]['totalRows']; $worksheetInfo[0]['lastColumnIndex'] = max($worksheetInfo[0]['lastColumnIndex'], count($rowData) - 1); } @@ -326,7 +333,7 @@ public function loadIntoExisting($pFilename, Spreadsheet $spreadsheet) } // Loop through each line of the file in turn - while (($rowData = fgetcsv($fileHandle, 0, $this->delimiter, $this->enclosure)) !== false) { + while (($rowData = fgetcsv($fileHandle, 0, $this->delimiter, $this->enclosure, $this->escapeCharacter)) !== false) { $columnLetter = 'A'; foreach ($rowData as $rowDatum) { if ($rowDatum != '' && $this->readFilter->readCell($columnLetter, $currentRow)) { @@ -458,6 +465,30 @@ public function getContiguous() return $this->contiguous; } + /** + * Set escape backslashes. + * + * @param string $escapeCharacter + * + * @return $this + */ + public function setEscapeCharacter($escapeCharacter) + { + $this->escapeCharacter = $escapeCharacter; + + return $this; + } + + /** + * Get escape backslashes. + * + * @return string + */ + public function getEscapeCharacter() + { + return $this->escapeCharacter; + } + /** * Can the current IReader read the file? * diff --git a/tests/PhpSpreadsheetTests/Reader/CsvTest.php b/tests/PhpSpreadsheetTests/Reader/CsvTest.php index 85f607213c..c42b610106 100644 --- a/tests/PhpSpreadsheetTests/Reader/CsvTest.php +++ b/tests/PhpSpreadsheetTests/Reader/CsvTest.php @@ -89,4 +89,19 @@ public function providerCanLoad() [true, '../samples/Reader/sampleData/example2.csv'], ]; } + + public function testEscapeCharacters() + { + $reader = (new Csv())->setEscapeCharacter('"'); + $worksheet = $reader->load(__DIR__ . '/../../data/Reader/CSV/backslash.csv') + ->getActiveSheet(); + + $expected = [ + ['field 1', 'field 2\\'], + ['field 3\\', 'field 4'], + ]; + + $this->assertSame('"', $reader->getEscapeCharacter()); + $this->assertSame($expected, $worksheet->toArray()); + } } diff --git a/tests/data/Reader/CSV/backslash.csv b/tests/data/Reader/CSV/backslash.csv new file mode 100644 index 0000000000..1642e50a51 --- /dev/null +++ b/tests/data/Reader/CSV/backslash.csv @@ -0,0 +1,2 @@ +"field 1","field 2\" +"field 3\","field 4"