fLIPIS

Usando PHP y ficheros

fLIPIS, 2009
No reproducir sin permiso expreso del autor
   

Abrir y leer de fichero


      
En este tutorial vamos a ver las diferentes formas de trabajar con ficheros que tenemos en PHP. Existe una miríada de funciones, cada una adecuada para una tarea específica, y vamos a ver unas cuantas en este tutorial. Lo primero, es ver como se abre un fichero, se lee contenido del mismo, se escribe en el, y se cierra (esto último es muy importante). Si ya has trabajado con ficheros en PHP anteriormente, lo mas seguro es que no te interese demasiado esta primera parte del tutorial. Comenzaremos con las funciónes fopen(), fclose() y file_exists(), que sirve para abrir un fichero y devolver un descriptor (puntero) al mismo. Este descriptor o puntero nos servirá para todas las operaciones que queramos realizar sobre ese fichero. Como vamos a crear una clase para encapsular toda la funcionalidad del tutorial, veamos el método de apertura de ficheros, así como el contructor de la clase:
   
file_operations_wrapper.class.php
   
<?php
define ("MODE_READ", "r");
define ("MODE_WRITE", "w");
define ("MODE_APPEND", "a");
define ("MODE_READ_PLUS", "r+");
define ("MODE_WRITE_PLUS", "w+");
define ("MODE_APPEND_PLUS", "a+");

public function __construct($filename)
{
   $this->_fileHandler = null;
   $this->_aLines = array();
   $this->_nLines = 0;

   if(!file_exists($filename))
   {
      throw new Exception("THE FILE ".$filename." DOES NOT EXIST");   
   }
   else
   {
      if(!$this->_fileHandler = fopen($filename, $mode))
      {
         throw new Exception("ERROR OPENING FILE ".$filename." WITH MODE ".$mode);
      }
   }

}
?>
      
En el constructor, inicializamos el descriptor de fichero $_fileHandler a null (ya veremos para qué), y definimos que $_aLines es un array, cuyo contador será $_nLines. Los defines del principio en realidad no son necesarios, pero de esta forma veremos mas claramente cada modo de apertura de fichero. Los modos de apertura son, como se ve, seis. Los iremos viendo uno por uno. Nuestro constructor acepta como argumentos el nombre de fichero y el modo de apertura. Lo primero llama a file_exists(), función de PHP que devuelve true o false en función de si un fichero existe o no. Si no existe, lanzamos una excepción. Tras esto, intentamos abrir el descriptor de fichero con el modo suminstrado, en caso de no poder, lanzamos una excepción. Primero tenemos el modo de lectura, que hemos llamado WRITE_MODE. Este modo nos permite abrir el fichero para leer de él. Vamos a crear un método que lea una línea, y que al volver a ser llamado, recuerde la posición en la que se quedó, además de guardar en un array miembro de clase las líneas que hemos ido leyendo. El código es como sigue:
   
file_operations_wrapper.class.php
   
<?php
define ("ASCII_RETURN", 10);
define ("ASCII_ENDLINE", 13);

public function readLine()
{
   if(is_null($this->_fileHandler))
   {
      throw new Exception("THE HANDLER FOR THE FILE IS NOT OPEN");
   }
   else
   {
      if($this->_openMode == MODE_WRITE)
      {
         throw new Exception("THE MODE IS SET TO WRITE. READING IN THIS MODE IS NOT ALLOWED");
      }
      
      $temp = "";
      $this->_aLines[$this->_nLines] = "";
      while($temp = fread($this->_fileHandler, 1))
      {            
         if(ord($temp) != ASCII_RETURN)
         {
            if(ord($temp) != ASCII_ENDLINE)
            {
               $this->_aLines[$this->_nLines] .= $temp;
            }
            else
            {
               continue;
            }
         }
         else
         {
            break;               
         }
      }
      $this->_nLines++;         
      return $this->_aLines[($this->_nLines - 1)];
   }
}
?>
      
Como vemos, lo primero que hemos creado son un par de defines para dos caracteres ASCII muy importantes: \n y \r. El primero es el símbolo estándar para indicar salto de línea, mientras que el segundo es propio de sistemas Windows, en los cuales un salto de línea se define por los caracteres \r\n, mientras que en un sistema UNIX, se define por \n. Esta pequeña idiosincracia es importante a tener en cuenta cuando se están leyendo líneas de texto desde un fichero escrito con, por ejemplo, el bloc de notas. El método primero comprueba que el fichero haya sido abierto, y que el modo no sea el de escritura, puesto que es el único modo en el que el fichero, en el momento de llegar a esa línea, ya está vacío y todo su contenido ha sido borrado. En caso de haberlo abierto con un modo correcto, vamos leyendo byte a byte con la instrucción fread($this->_fileHandler, 1), comprobamos que el byte leído no sea un salto de línea, antes de anexarlo al elemento del array $this->_aLines[$this->_nLines]. De esta forma, mantenemos un contador de líneas de fichero, almacenado en $this->_nLines. Eso nos es últil para, mas adelante, poder hacer este método:
   
file_operations_wrapper.class.php
   
<?php
public function getLine($lineNumber)
{
   if($this->_nLines >= $lineNumber)
   {
      return $this->_aLines[$lineNumber];
   }
   else
   {
      throw new Exception("THE LINE ".$lineNumber." DOES NOT EXIST ON THE ARRAY");
   }
}
?>
      
El cual, como se ve, no es mas que un sencillo getter que nos permite sacar una línea ya leída a través de su índice numérico en el array de líneas de la clase. Por tanto ya tenemos una forma de leer las líneas de un fichero de texto, guardarlas en memoria en nuestra clase, para posteriormente, quizás manipularlas o compararlas. Antes de terminar esta parte, vamos a ver otra importantísima función (la MÁS importante, diría), de todo este tema de los ficheros: fclose(). Esta función cierra un descriptor de fichero abierto con fopen(). Como estamos usando una clase, lo que haremos será cerrar el descriptor de fichero en el destructor de la clase. Su código es como sigue:
   
file_operations_wrapper.class.php
   
<?php
public function __destruct()
{
   if(!is_null($this->_fileHandler))
   {
      fclose($this->_fileHandler);
   }
}
?>
      
Lo que hacemos es comprobar que el descriptor no sea null (de ahí que lo pongamos a null en el constructor), y en caso de no serlo, lo cerramos. Tambien podríamos hacer @fclose($this->_fileHandler);, con la @ indicando que no se desean mensajes de error para esa función, aunque falle. Personalmente, prefiero evitar el uso de la arroba siempre que me es posible, porque creo que no facilita mucho el entender el código, pero si alguien prefiere usarlo, adelante.
      
Y pasamos a la segunda parte: escribir en ficheros