support lecture et écriture CSV
This commit is contained in:
		
							parent
							
								
									6a837fbcb3
								
							
						
					
					
						commit
						e3e3fc91f4
					
				
							
								
								
									
										46
									
								
								src/os/csv/csv_flavours.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										46
									
								
								src/os/csv/csv_flavours.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,46 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace nur\sery\os\csv;
 | 
			
		||||
 | 
			
		||||
use nur\A;
 | 
			
		||||
use nur\ref\ref_csv;
 | 
			
		||||
 | 
			
		||||
class csv_flavours {
 | 
			
		||||
  const MAP = [
 | 
			
		||||
    "oo" => ref_csv::OO_FLAVOUR,
 | 
			
		||||
    "ooffice" => ref_csv::OO_FLAVOUR,
 | 
			
		||||
    ref_csv::OO_NAME => ref_csv::OO_FLAVOUR,
 | 
			
		||||
    "xl" => ref_csv::XL_FLAVOUR,
 | 
			
		||||
    "excel" => ref_csv::XL_FLAVOUR,
 | 
			
		||||
    ref_csv::XL_NAME => ref_csv::XL_FLAVOUR,
 | 
			
		||||
  ];
 | 
			
		||||
  
 | 
			
		||||
  const ENCODINGS = [
 | 
			
		||||
    ref_csv::OO_FLAVOUR => ref_csv::OO_ENCODING,
 | 
			
		||||
    ref_csv::XL_FLAVOUR => ref_csv::XL_ENCODING,
 | 
			
		||||
  ];
 | 
			
		||||
  
 | 
			
		||||
  static final function verifix(string $flavour): string {
 | 
			
		||||
    $lflavour = strtolower($flavour);
 | 
			
		||||
    if (array_key_exists($lflavour, self::MAP)) {
 | 
			
		||||
      $flavour = self::MAP[$lflavour];
 | 
			
		||||
    }
 | 
			
		||||
    if (strlen($flavour) < 1) $flavour .= ",";
 | 
			
		||||
    if (strlen($flavour) < 2) $flavour .= "\"";
 | 
			
		||||
    if (strlen($flavour) < 3) $flavour .= "\\";
 | 
			
		||||
    return $flavour;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static final function get_name(string $flavour): string {
 | 
			
		||||
    if ($flavour == ref_csv::OO_FLAVOUR) return ref_csv::OO_NAME;
 | 
			
		||||
    elseif ($flavour == ref_csv::XL_FLAVOUR) return ref_csv::XL_NAME;
 | 
			
		||||
    else return $flavour;
 | 
			
		||||
  }
 | 
			
		||||
  
 | 
			
		||||
  static final function get_params(string $flavour): array {
 | 
			
		||||
    return [$flavour[0], $flavour[1], $flavour[2]];
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  static final function get_encoding(string $flavour): ?string {
 | 
			
		||||
    return A::get(self::ENCODINGS, $flavour);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -2,9 +2,11 @@
 | 
			
		||||
namespace nur\sery\os\file;
 | 
			
		||||
 | 
			
		||||
use nur\sery\NoMoreDataException;
 | 
			
		||||
use nur\sery\os\csv\csv_flavours;
 | 
			
		||||
use nur\sery\os\EOFException;
 | 
			
		||||
use nur\sery\os\IOException;
 | 
			
		||||
use nur\sery\php\iter\AbstractIterator;
 | 
			
		||||
use nur\sery\ref\os\csv\ref_csv;
 | 
			
		||||
use nur\sery\str;
 | 
			
		||||
use nur\sery\ValueException;
 | 
			
		||||
 | 
			
		||||
@ -139,6 +141,40 @@ class Stream extends AbstractIterator implements IReader, IWriter {
 | 
			
		||||
    if ($closeReader) $this->close();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  const DEFAULT_CSV_FLAVOUR = ref_csv::OO_FLAVOUR;
 | 
			
		||||
 | 
			
		||||
  /** @var array paramètres pour la lecture et l'écriture de flux au format CSV */
 | 
			
		||||
  protected $csvFlavour;
 | 
			
		||||
 | 
			
		||||
  function setCsvFlavour(string $flavour): void {
 | 
			
		||||
    $this->csvFlavour = csv_flavours::verifix($flavour);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  protected function getCsvParams($fd): array {
 | 
			
		||||
    $flavour = $this->csvFlavour;
 | 
			
		||||
    if ($flavour === null) {
 | 
			
		||||
      if ($fd === null) {
 | 
			
		||||
        # utiliser la valeur par défaut
 | 
			
		||||
        $flavour = static::DEFAULT_CSV_FLAVOUR;
 | 
			
		||||
      } else {
 | 
			
		||||
        # il faut déterminer le type de fichier CSV en lisant la première ligne
 | 
			
		||||
        $pos = IOException::ensure_valid(ftell($fd));
 | 
			
		||||
        $line = IOException::ensure_valid(fgets($fd));
 | 
			
		||||
        $line = strpbrk($line, ",;\t");
 | 
			
		||||
        if ($line === false) {
 | 
			
		||||
          # aucun séparateur trouvé, prender la valeur par défaut
 | 
			
		||||
          $flavour = static::DEFAULT_CSV_FLAVOUR;
 | 
			
		||||
        } else {
 | 
			
		||||
          $flavour = substr($line, 0, 1);
 | 
			
		||||
          $flavour = csv_flavours::verifix($flavour);
 | 
			
		||||
        }
 | 
			
		||||
        IOException::ensure_valid(fseek($fd, $pos), true, -1);
 | 
			
		||||
      }
 | 
			
		||||
      $this->csvFlavour = $flavour;
 | 
			
		||||
    }
 | 
			
		||||
    return csv_flavours::get_params($flavour);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  #############################################################################
 | 
			
		||||
  # Reader
 | 
			
		||||
 | 
			
		||||
@ -168,6 +204,18 @@ class Stream extends AbstractIterator implements IReader, IWriter {
 | 
			
		||||
    return IOException::ensure_valid(fpassthru($fd), $this->throwOnError);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * retourner la prochaine ligne au format CSV ou null si le fichier est arrivé
 | 
			
		||||
   * à sa fin
 | 
			
		||||
   */
 | 
			
		||||
  function fgetcsv(): ?array {
 | 
			
		||||
    $fd = $this->getResource();
 | 
			
		||||
    $params = $this->getCsvParams($fd);
 | 
			
		||||
    $row = fgetcsv($fd, 0, $params[0], $params[1], $params[2]);
 | 
			
		||||
    if ($row === false && feof($fd)) return null;
 | 
			
		||||
    return IOException::ensure_valid($row, $this->throwOnError);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
   * lire la prochaine ligne. la ligne est retournée *sans* le caractère de fin
 | 
			
		||||
   * de ligne [\r]\n
 | 
			
		||||
@ -267,6 +315,12 @@ class Stream extends AbstractIterator implements IReader, IWriter {
 | 
			
		||||
    return IOException::ensure_valid($r, $this->throwOnError);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function fputcsv(array $row): void {
 | 
			
		||||
    $fd = $this->getResource();
 | 
			
		||||
    $params = $this->getCsvParams($fd);
 | 
			
		||||
    IOException::ensure_valid(fputcsv($fd, $row, $params[0], $params[1], $params[2]));
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /** @throws IOException */
 | 
			
		||||
  function fflush(): self {
 | 
			
		||||
    $fd = $this->getResource();
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										20
									
								
								src/ref/os/csv/ref_csv.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/ref/os/csv/ref_csv.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,20 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace nur\sery\ref\os\csv;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * Class ref_csv: références des valeurs normalisées pour les fichiers CSV à
 | 
			
		||||
 * destination de Microsoft Excel et de LibreOffice Calc
 | 
			
		||||
 */
 | 
			
		||||
class ref_csv {
 | 
			
		||||
  const UTF8 = "utf-8";
 | 
			
		||||
  const CP1252 = "cp1252";
 | 
			
		||||
  const LATIN1 = "iso-8859-1";
 | 
			
		||||
 | 
			
		||||
  const OO_NAME = "oocalc";
 | 
			
		||||
  const OO_FLAVOUR = ",\"\\";
 | 
			
		||||
  const OO_ENCODING = self::UTF8;
 | 
			
		||||
  
 | 
			
		||||
  const XL_NAME = "msexcel";
 | 
			
		||||
  const XL_FLAVOUR = ";\"\\";
 | 
			
		||||
  const XL_ENCODING = self::CP1252;
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										62
									
								
								tests/os/file/FileReaderTest.php
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								tests/os/file/FileReaderTest.php
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,62 @@
 | 
			
		||||
<?php
 | 
			
		||||
namespace nur\sery\os\file;
 | 
			
		||||
 | 
			
		||||
use PHPUnit\Framework\TestCase;
 | 
			
		||||
 | 
			
		||||
class FileReaderTest extends TestCase {
 | 
			
		||||
  function testIgnoreBom() {
 | 
			
		||||
    # la lecture avec et sans BOM doit être identique
 | 
			
		||||
    ## sans BOM
 | 
			
		||||
    $reader = new FileReader(__DIR__.'/impl/sans_bom.txt');
 | 
			
		||||
    self::assertSame("0123456789", $reader->fread(10));
 | 
			
		||||
    self::assertSame(10, $reader->ftell());
 | 
			
		||||
    $reader->seek(30);
 | 
			
		||||
    self::assertSame("abcdefghij", $reader->fread(10));
 | 
			
		||||
    self::assertSame(40, $reader->ftell());
 | 
			
		||||
    $reader->seek(10);
 | 
			
		||||
    self::assertSame("ABCDEFGHIJ", $reader->fread(10));
 | 
			
		||||
    self::assertSame(20, $reader->ftell());
 | 
			
		||||
    $reader->seek(40);
 | 
			
		||||
    self::assertSame("0123456789\n", $reader->getContents());
 | 
			
		||||
    $reader->close();
 | 
			
		||||
    ## avec BOM
 | 
			
		||||
    $reader = new FileReader(__DIR__.'/impl/avec_bom.txt');
 | 
			
		||||
    self::assertSame("0123456789", $reader->fread(10));
 | 
			
		||||
    self::assertSame(10, $reader->ftell());
 | 
			
		||||
    $reader->seek(30);
 | 
			
		||||
    self::assertSame("abcdefghij", $reader->fread(10));
 | 
			
		||||
    self::assertSame(40, $reader->ftell());
 | 
			
		||||
    $reader->seek(10);
 | 
			
		||||
    self::assertSame("ABCDEFGHIJ", $reader->fread(10));
 | 
			
		||||
    self::assertSame(20, $reader->ftell());
 | 
			
		||||
    $reader->seek(40);
 | 
			
		||||
    self::assertSame("0123456789\n", $reader->getContents());
 | 
			
		||||
    $reader->close();
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  function testCsvAutoParams() {
 | 
			
		||||
    $reader = new FileReader(__DIR__.'/impl/msexcel.csv');
 | 
			
		||||
    self::assertSame(["nom", "prenom", "age"], $reader->fgetcsv());
 | 
			
		||||
    self::assertSame(["clain", "jephte", "50"], $reader->fgetcsv());
 | 
			
		||||
    self::assertNull($reader->fgetcsv());
 | 
			
		||||
    $reader->close();
 | 
			
		||||
 | 
			
		||||
    $reader = new FileReader(__DIR__.'/impl/ooffice.csv');
 | 
			
		||||
    self::assertSame(["nom", "prenom", "age"], $reader->fgetcsv());
 | 
			
		||||
    self::assertSame(["clain", "jephte", "50"], $reader->fgetcsv());
 | 
			
		||||
    self::assertNull($reader->fgetcsv());
 | 
			
		||||
    $reader->close();
 | 
			
		||||
 | 
			
		||||
    $reader = new FileReader(__DIR__.'/impl/weird.tsv');
 | 
			
		||||
    self::assertSame(["nom", "prenom", "age"], $reader->fgetcsv());
 | 
			
		||||
    self::assertSame(["clain", "jephte", "50"], $reader->fgetcsv());
 | 
			
		||||
    self::assertNull($reader->fgetcsv());
 | 
			
		||||
    $reader->close();
 | 
			
		||||
 | 
			
		||||
    $reader = new FileReader(__DIR__.'/impl/avec_bom.csv');
 | 
			
		||||
    self::assertSame(["nom", "prenom", "age"], $reader->fgetcsv());
 | 
			
		||||
    self::assertSame(["clain", "jephte", "50"], $reader->fgetcsv());
 | 
			
		||||
    self::assertNull($reader->fgetcsv());
 | 
			
		||||
    $reader->close();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										2
									
								
								tests/os/file/impl/avec_bom.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								tests/os/file/impl/avec_bom.csv
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
			
		||||
nom,prenom,age
 | 
			
		||||
clain,jephte,50
 | 
			
		||||
		
		
			
  | 
							
								
								
									
										1
									
								
								tests/os/file/impl/avec_bom.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/os/file/impl/avec_bom.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
0123456789ABCDEFGHIJ0123456789abcdefghij0123456789
 | 
			
		||||
							
								
								
									
										2
									
								
								tests/os/file/impl/msexcel.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								tests/os/file/impl/msexcel.csv
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
			
		||||
nom;prenom;age
 | 
			
		||||
clain;jephte;50
 | 
			
		||||
		
		
			
  | 
							
								
								
									
										2
									
								
								tests/os/file/impl/ooffice.csv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								tests/os/file/impl/ooffice.csv
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
			
		||||
nom,prenom,age
 | 
			
		||||
clain,jephte,50
 | 
			
		||||
		
		
			
  | 
							
								
								
									
										1
									
								
								tests/os/file/impl/sans_bom.txt
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								tests/os/file/impl/sans_bom.txt
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1 @@
 | 
			
		||||
0123456789ABCDEFGHIJ0123456789abcdefghij0123456789
 | 
			
		||||
							
								
								
									
										2
									
								
								tests/os/file/impl/weird.tsv
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										2
									
								
								tests/os/file/impl/weird.tsv
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,2 @@
 | 
			
		||||
nom	prenom	age
 | 
			
		||||
clain	jephte	50
 | 
			
		||||
		
		
			
  | 
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user