3 namespace Org\Mxchange\CoreFramework\Filesystem\Input\Csv;
5 // Import framework stuff
6 use Org\Mxchange\CoreFramework\Filesystem\Text\BaseInputTextFile;
7 use Org\Mxchange\CoreFramework\Generic\FrameworkInterface;
8 use Org\Mxchange\CoreFramework\Stream\Filesystem\CsvInputStreamer;
11 use \InvalidArgumentException;
13 use \UnexpectedValueException;
16 * A CSV file input class for writing CSV files
18 * @author Roland Haeder <webmaster@ship-simu.org>
20 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2023 Core Developer Team
21 * @license GNU GPL 3.0 or any newer version
22 * @link http://www.ship-simu.org
24 * This program is free software: you can redistribute it and/or modify
25 * it under the terms of the GNU General Public License as published by
26 * the Free Software Foundation, either version 3 of the License, or
27 * (at your option) any later version.
29 * This program is distributed in the hope that it will be useful,
30 * but WITHOUT ANY WARRANTY; without even the implied warranty of
31 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 * GNU General Public License for more details.
34 * You should have received a copy of the GNU General Public License
35 * along with this program. If not, see <http://www.gnu.org/licenses/>.
37 class CsvInputFile extends BaseInputTextFile implements CsvInputStreamer {
39 * Protected constructor
43 private function __construct () {
44 // Call parent constructor
45 parent::__construct(__CLASS__);
49 * Creates an instance of this File class and prepares it for usage
51 * @param $infoInstance An instance of a SplFileInfo class
52 * @return $fileInstance An instance of this File class
54 public final static function createCsvInputFile (SplFileInfo $infoInstance) {
56 $fileInstance = new CsvInputFile();
58 // Init this abstract file
59 $fileInstance->initFile($infoInstance);
61 // Return the prepared instance
66 * Reads a line from CSV file and returns it as an indexed array. Please
67 * note that strings *must* be always in double-quotes, else any found
68 * column separators will be parsed or they may be interpreted incorrectly.
70 * @param $columnSeparator Character to use separting columns
71 * @param $expectedMatches Expected matches, 0 is default and means flexible
72 * @return $lineArray An indexed array with the read line
73 * @throws InvalidArgumentException If a parameter is invalid
74 * @throws UnexpectedValueException If the array count is not matching expected count
76 public function readCsvFileLine (string $columnSeparator, int $expectedMatches = 0) {
78 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] columnSeparator=%s,expectedMatches=%d - CALLED!', __METHOD__, __LINE__, $columnSeparator, $expectedMatches));
79 if (strlen($columnSeparator) === 0) {
80 // No empty column separator
81 throw new InvalidArgumentException('Parameter "columnSeparator" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
82 } elseif ($expectedMatches < 0) {
83 // Below zero is not valid
84 throw new InvalidArgumentException(sprintf('expectedMatches=%d is below zero', $expectedMatches));
87 // Read raw line and trim anything unwanted away
88 $data = trim($this->getPointerInstance()->readLine());
91 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] data(%d)=%s', __METHOD__, __LINE__, strlen($data), $data));
93 // Yes, then skip below code
94 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] Read data is an empty line - EXIT!', __METHOD__, __LINE__));
99 $lineArray = $this->parseDataToIndexedArray($data, $columnSeparator);
101 // Is the expected count found?
102 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] expectedMatches=%d,lineArray()=%d', __METHOD__, __LINE__, $expectedMatches, count($lineArray)));
103 if (($expectedMatches > 0) && (count($lineArray) !== $expectedMatches)) {
104 // Invalid line found as strict count matching is requested
105 throw new UnexpectedValueException(sprintf('lineArray()=%d has not expected count %d', count($lineArray), $expectedMatches), FrameworkInterface::EXCEPTION_UNEXPECTED_VALUE);
109 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] lineArray()=%d - EXIT!', __METHOD__, __LINE__, count($lineArray)));
114 * Parses given data into an array
116 * @param $data Raw data e.g. returned from readLine()
117 * @param $columnSeparator Character to use separting columns
118 * @return $lineArray An indexed array with the read line
120 private function parseDataToIndexedArray (string $data, string $columnSeparator) {
122 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] data()=%d,columnSeparator=%s - CALLED!', __METHOD__, __LINE__, strlen($data), $columnSeparator));
125 // Whether the parser reads a quoted string (which may contain the column separator again)
131 // Now parse the line
132 for ($idx = 0; $idx < strlen($data); $idx++) {
134 $char = substr($data, $idx, 1);
136 // Is the column separator found and not within quotes?
137 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] idx=%d,char=%s ...', __METHOD__, __LINE__, $idx, $char));
138 if (($isInQuotes === false) && ($char == $columnSeparator)) {
139 // Add this line to the array
140 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] Adding column=%s ...', __METHOD__, __LINE__, $column));
141 array_push($lineArray, $column);
143 // Clear variable ...
144 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] line[]=%d - After add!', __METHOD__, __LINE__, count($lineArray)));
149 } elseif ($char == chr(34)) {
150 // $column must be empty at this point if we are at starting quote
151 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] column=%s ...', __METHOD__, __LINE__, $column));
152 assert(($isInQuotes === true) || (empty($column)));
154 // Double-quote found, so flip variable
155 $isInQuotes = (!$isInQuotes);
157 // Skip double-quote (escaping of them is not yet supported)
158 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] isInQuotes=%d ...', __METHOD__, __LINE__, intval($isInQuotes)));
162 // Add char to column
163 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] Adding char=%s ...', __METHOD__, __LINE__, $idx, $char));
167 // Is there something outstanding?
168 if (!empty($column)) {
169 // Then don't forget this. :-)
170 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] Adding column=%s ...', __METHOD__, __LINE__, $column));
171 array_push($lineArray, $column);
174 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] line[]=%d - After add!', __METHOD__, __LINE__, count($lineArray)));
178 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('[%s:%d:] line[]=%d - EXIT!', __METHOD__, __LINE__, count($lineArray)));