CSV.php 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  1. <?php
  2. class PHPExcel_Reader_CSV implements Iterator, Countable {
  3. private $_inputEncoding = 'UTF-8';
  4. private $_delimiter = ',';
  5. private $_enclosure = '"';
  6. private $_filter = 0;
  7. /**
  8. * @param string $filePath
  9. * @param int $filter filter empty row
  10. * @throws Exception
  11. */
  12. public function __construct($filePath, $filter = 0) {
  13. if ( ! file_exists($filePath)) {
  14. throw new Exception("Could not open " . $filePath . " for reading! File does not exist.");
  15. }
  16. $this->filePath = $filePath;
  17. $this->_filter = $filter;
  18. ini_set('auto_detect_line_endings', true);
  19. $this->_fileHandle = fopen($filePath, 'r');
  20. $this->_detectEncoding();
  21. }
  22. /**
  23. * Move filepointer past any BOM marker
  24. *
  25. */
  26. private function _detectEncoding()
  27. {
  28. $step = 0;
  29. while ($step < 3) {
  30. $BOM = bin2hex(fread($this->_fileHandle, 2 + $step++));
  31. rewind($this->_fileHandle);
  32. if ($BOM == 'fffe' || $BOM == 'feff') {
  33. $BOMLength = 2;
  34. $this->_delimiter = "\t";
  35. $this->_inputEncoding = 'UTF-16';
  36. break;
  37. }
  38. else if ($BOM == 'efbbbf') {
  39. $BOMLength = 3;
  40. break;
  41. }
  42. else if ($BOM == '0000feff' || $BOM == 'fffe0000') {
  43. $BOMLength = 4;
  44. $this->_delimiter = "\t";
  45. $this->_inputEncoding = 'UTF-32';
  46. break;
  47. }
  48. }
  49. if ( ! $BOMLength) {
  50. $encoding = mb_detect_encoding(fgets($this->_fileHandle, 1024), 'ASCII, UTF-8, GB2312, GBK');
  51. rewind($this->_fileHandle);
  52. if ($encoding) {
  53. if ($encoding == 'EUC-CN') {
  54. $this->_inputEncoding = 'GB2312';
  55. }
  56. else if ($encoding == 'CP936') {
  57. $this->_inputEncoding = 'GBK';
  58. }
  59. else {
  60. $this->_inputEncoding = $encoding;
  61. }
  62. }
  63. }
  64. if ($this->_inputEncoding != 'UTF-8') {
  65. stream_filter_register("convert_iconv.*", "convert_iconv_filter");
  66. stream_filter_append($this->_fileHandle, 'convert_iconv.' . $this->_inputEncoding . '/UTF-8');
  67. }
  68. }
  69. /**
  70. * Returns information about sheets in the file.
  71. * @return array
  72. */
  73. public function Sheets() {
  74. return array(0 => basename($this->filePath));
  75. }
  76. /**
  77. * Changes sheet to another.
  78. * @param bool
  79. */
  80. public function ChangeSheet($index) {
  81. if ($index == 0) {
  82. $this->rewind();
  83. return true;
  84. }
  85. return false;
  86. }
  87. /**
  88. * Rewind the Iterator to the first element.
  89. */
  90. public function rewind() {
  91. rewind($this->_fileHandle);
  92. $this->currentRow = null;
  93. $this->index = 0;
  94. }
  95. /**
  96. * Return the current element.
  97. * @return mixed
  98. */
  99. public function current() {
  100. if ($this->index == 0 && ! isset($this->currentRow)) {
  101. $this->rewind();
  102. $this->next();
  103. $this->index = 0;
  104. }
  105. return $this->currentRow;
  106. }
  107. /**
  108. * Move forward to next element.
  109. */
  110. public function next() {
  111. $this->currentRow = array();
  112. $this->index++;
  113. while (($row = fgetcsv($this->_fileHandle, 0, $this->_delimiter, $this->_enclosure)) !== false) {
  114. if ( ! $this->_filter || array_filter($row, array($this, 'filter'))) {
  115. $this->currentRow = $row;
  116. break;
  117. }
  118. }
  119. return $this->currentRow;
  120. }
  121. /**
  122. * Return the identifying key of the current element.
  123. * @return mixed
  124. */
  125. public function key() {
  126. return $this->index;
  127. }
  128. /**
  129. * Check if there is a current element after calls to rewind() or next().
  130. * @return bool
  131. */
  132. public function valid() {
  133. if ($this->currentRow || ! feof($this->_fileHandle)) {
  134. return true;
  135. }
  136. else {
  137. fclose($this->_fileHandle);
  138. return false;
  139. }
  140. }
  141. /**
  142. * return the count of the contained items
  143. * @return int
  144. */
  145. public function count() {
  146. if ( ! isset($this->rowCount)) {
  147. $total = 0;
  148. rewind($this->_fileHandle);
  149. while (($row = fgetcsv($this->_fileHandle, 0, $this->_delimiter, $this->_enclosure)) !== false) {
  150. if ( ! $this->_filter || array_filter($row, array($this, 'filter'))) {
  151. $total++;
  152. }
  153. }
  154. $this->rowCount = $total;
  155. }
  156. return $this->rowCount;
  157. }
  158. /**
  159. * filter empty string
  160. * @param mixed $value
  161. * @return boolean
  162. */
  163. private function filter($value) {
  164. return trim($value) !== '';
  165. }
  166. }
  167. class convert_iconv_filter extends php_user_filter {
  168. private $modes;
  169. function filter($in, $out, &$consumed, $closing) {
  170. while ($bucket = stream_bucket_make_writeable($in)) {
  171. $bucket->data = mb_convert_encoding($bucket->data, $this->modes[1], $this->modes[0]);
  172. $consumed += $bucket->datalen;
  173. stream_bucket_append($out, $bucket);
  174. }
  175. return PSFS_PASS_ON;
  176. }
  177. function onCreate() {
  178. $format = explode('/', substr($this->filtername, 14));
  179. if (count($format) == 2) {
  180. $this->modes = $format;
  181. return true;
  182. }
  183. else {
  184. return false;
  185. }
  186. }
  187. }