10 oct 2016

Manejo de fechas en Java

java.util.Date


Durante mucho tiempo la clase utilizada para manejar fechas fue java.util.Date, pero hoy día muchos de sus métodos se consideran obsoletos.

java.util.Calendar


Posteriormente se utilizó la clase java.util.Calendar y java.util.GregorianCalendar. Su principal diferencia con Date, es que Calendar es mutable, permite cambiar el valor de un objeto sin tener que generar un nuevo objeto.
Calendar es una clase abstracta por lo que sus objetos deben instanciarse a través de alguna de sus clases heredadas, por ejemplo con el método getInstance() que nos devolvería la fecha actual del sistema o bien con GregorianCalendar, que sí es instanciable. Ejemplos:

Calendar hoy= Calendar.getInstance();
Calendar hoy= new GregorianCalendar();

Calendar dispone de métodos “get” y “set” que permiten recuperar o almacenar valores de las fechas, los correspondientes al argumento pasado, pero también tiene métodos “add” y “roll” que permiten a partir de unos valores incrementar o decrementar las fechas, recalculando las fechas correctas si es necesario.

Ejemplo:


Si quisiéramos darle valores, por ejemplo de una fecha de nacimiento, sería:



La clase Calendar permite trabajar de dos modos, en un modo indulgente o permisivo (Lenient), que es el que está activo por defecto, y que nos permitiría almacenar fechas sin realizar validaciones, por ejemplo darle al atributo día el valor 34, o no tener en cuenta sin un año es bisiesto, y un modo más restrictivo, que produciría una excepción de tipo IllegalArgumentException, si los parámetros de la fecha no son correctos.
Para activar el modo restrictivo, o no indulgente, pondríamos:
nacim.setLenient(false);

El ejemplo anterior con una fecha no válida, daría el siguiente resultado:



Paquete java.time


A partir de la versión 8 de Java se recomienda el uso del paquete java.time. Este paquete contiene clases para tratar fechas, tiempos, instantes y duraciones. Por ejemplo LocalTime, permite almacenar horas sin fecha, como: 14:37:26;  LocalDate, sirve para almacenar fechas sin hora, como 2015-05-22; LocalDateTime, permite almacenar fechas con hora.

LocalDate

Los objetos se crean con el método estático of() de la clase LocalDate.
LocalDate fecha = LocalDate.of(2016, 3, 27);

Si lo que queremos es obtener la fecha actual del sistema, utilizaremos el método now():
LocalDate ahora = LocalDate.now();

Dispone de métodos get para obtener los componentes día, mes y año de una fecha:
int dia, mes, anno;
dia= fecha.getDayOfMonth();
mes= fecha.getMonthValue(); //devuelve valores 1..12
anno= fecha.getYear();

Otros métodos get:
DayOfWeek diaSemana= fecha.getDayOfWeek();
Month nombreMes= fecha.getMonth();

Tiene métodos para incrementar, plus(), o para hacer retroceder una fecha, minus(). Estos métodos admiten dos argumentos el primero es el número de unidades y el segundo que expresa el tipo de unidades, mediante el tipo enumerado ChronoUnit, que tiene valores como DAYS, WEEKS, MONTHS o YEARS. Ejemplos:
LocalDate fecha2= fecha1.plus(5, ChronoUnit.MONTHS); //Aumenta fecha1, 5 meses
LocalDate fecha2= fecha1.minus(3, ChronoUnit.DAYS); //Disminuye fecha1, 3 días

Otros métodos interesantes:
·         isLeapYear()      verifica si un año es bisiesto
·         until()                   permite calcular el tiempo entre dos fechas
·         parse()                 permite analizar una cadena para generar una fecha

Para establecer el formato de las fechas, se utiliza la clase DateTimeFormatter.

3 dic 2015

Formatos en ficheros de texto

Se trata de dotar de una organización interna a los ficheros de texto.

El caso extremo sería, sin ningún formato, en este caso sería una secuencia de caracteres de principio a fin. Sería el caso, por ejemplo, de muchos libros disponibles en la red. Su utilidad en muchos casos es el análisis de determinados aspectos lingüísticos.

Los principales formatos serían:
Con longitud de campos fija.
Con separador entre campos.
Con un campo por línea.



Con longitud de campos fija o encolumnados.

Cada campo se almacena con una longitud fija para todos los elementos.
Es necesario conocer estos valores de longitudes para poder leer y escribir.
Supongamos que queremos guardar datos de empleados (dni (9 caracteres), edad (2 dígitos), nombre (20 caracteres) y salario mensual (5 dígitos y 2 decimales). Podríamos escribir con un formato  %9s%2d%20s%7.2f\n

Ejemplo:
14567432F32    Ana Martín Pérez 1654.43
 4322224M28Manuel Rodríguez Mar 998.32
  657897H45  Jesús López Castro10234.23   



Con separador entre campos.

Cada campo está separado del siguiente por un separador, que puede ser cualquier carácter, pero que debe ser elegido de forma que no forme parte del contenido de los campos.  Caracteres habituales son el tabulador, la coma (,), el punto y coma (;), los dos puntos (:), los dobles dos puntos (::),  la almohadilla (#),  asterisco (*), etc.
El separador (,) es muy habitual dando nombre a un tipo de ficheros llamado .csv (del inglés comma-separated values).
La mayoría de las hojas de cálculo, los procesadores de textos y los sistemas gestores de bases de datos permiten importar y exportar a este formato.

Ejemplo:

14567432F#32#Ana Martín Pérez#1654.43
4322224M#28#Manuel Rodríguez Mar#998.32
657897H#45#Jesús López Castro#10234.23


Por ejemplo, en Word, nos preguntaría por el tipo de codificación del fichero:



En Excel, nos permitiría separarlo en columnas, indicándole primero la codificación y posteriormente la separación:




En otras hojas de cálculo de forma muy similar, como en Calc de Libre Office:



Con un campo por línea.

Aparece cada campo en una línea.

Ejemplo:
14567432F
32
Ana Martín Pérez
1654.43
4322224M
28
Manuel Rodríguez Mar
998.32
657897H
45
Jesús López Castro
10234.23


18 nov 2015

Acceso aleatorio a ficheros en Java

Java dispone de la clase RandomAccessFile que permite acceder al contenido de un fichero binario de forma aleatoria.

Para crear objetos de esta clase, al constructor se le puede pasar, o la ruta mediante un String o mediante un File. En cualquiera de los dos casos hay que pasar un segundo parámetro con el modo de acceso. El modo de acceso puede ser “r”, “rw”

Ejemplos:
Fich= new RandomAccessFile(String nombre, String modoAcceso)
Fich = new RandomAccessFile(File fic, String modoAcceso)


Los métodos más importantes son:
long getFilePointer()
Devuelve la posición del puntero en el fichero.
void seek(long pos)
Pone el puntero en la posición dada por pos, tomada desde el inicio.
long length()
Devuelve el tamaño del fichero en bytes.
Int skipBytes (int desplaz)
Mueve el puntero desde su posición el número de bytes indicado por desplaz.

Ejemplo:
Vamos a guardar los datos de una serie de empleados. Interesa que los datos de cada persona tengan la misma longitud para poder saltar e ir a una persona, para ello limitamos (fijamos) el tamaño de los String antes de escribirlos:

buffer = new StringBuffer(apellidos[i]);
buffer.setLength(10);

El resto de campos tienen una longitud fija, porque son enteros y double.
En total: int (4 bytes), string (20 bytes), int (4 bytes) y double (8 bytes) = 36 bytes.
Tamaño del fichero= 8 registros x 36 bytes = 288 bytes

public class EscribirFichAleatorio {
public static void main(String[] args) throws IOException {
        // TODO code application logic here
        File fich = new File("Empl_aleat.dat");
        RandomAccessFile file = new RandomAccessFile(fich, "rw");
        String[] apellidos = {"MARTÍN", "SORIA", "FERNÁNDEZ", "LUNA", "GARCÍA", "PÉREZ", "RODRÍGUEZ", "MARTÍNEZ"};
        int[] dept = {10, 20, 30, 20, 10, 40, 30, 40};
        Double[] salario = {850.65, 12035.36, 2156.36, 1500.32, 989.23, 1566.32, 1866.88, 2356.78};
        StringBuffer buffer = null;
        for (int i = 0; i < apellidos.length; i++) {
            file.writeInt(i + 1); //identificador de empleado
            buffer = new StringBuffer(apellidos[i]);
            buffer.setLength(10);
            file.writeChars(buffer.toString());
            file.writeInt(dept[i]);
            file.writeDouble(salario[i]);
        }
        file.close();
    }
}


13 nov 2015

Lectura / Escritura de objetos en Java

Con algunas limitaciones, los objetos se pueden leer y escribir en flujos de forma similar a los tipos primitivos. Concretamente, las clases ObjectInputStream y ObjectOutputStream poseen las mismas funcionalidades que DataInputStream y DataOutputStream (y otras específicas de objetos), lo cual da lugar a que un flujo de objetos pueda contener tanto tipos primitivos como objetos.

Los métodos que más interés tienen son
Object readObject()
void writeObject(Object obj)
que leen/escriben el próximo objeto presente en el flujo.


Ejemplo de uso:

package flujosdeobjetos;

import java.io.*;
import java.math.BigDecimal;
import java.util.Calendar;
import java.util.Locale;
import java.util.TimeZone;

public class FlujosDeObjetos {

    static final String archivoDeDatos = "datosDeFacturacion.bin";
    static final BigDecimal[] precio_unitario = {
        new BigDecimal("19.99"),
        new BigDecimal("9.99"),
        new BigDecimal("15.99"),
        new BigDecimal("3.99"),
        new BigDecimal("4.99")};
    static final int[] num_unidades = {12, 8, 13, 29, 50};
    static final String[] descripciones = {"memoria usb",
        "tarjeta sd",
        "teclado",
        "disco usb",
        "disco duro"};

    public static void main(String[] args)
            throws IOException, ClassNotFoundException {

        TimeZone tz = TimeZone.getTimeZone("Europe/Madrid");
        Locale loc = new Locale("ES");
        Calendar cal = Calendar.getInstance(tz, loc);

        ObjectOutputStream f_sal = null;
        try {
            f_sal = new ObjectOutputStream(new BufferedOutputStream(new FileOutputStream(archivoDeDatos)));

            f_sal.writeObject(cal);
            for (int i = 0; i < precio_unitario.length; i++) {
                f_sal.writeObject(precio_unitario[i]);
                f_sal.writeInt(num_unidades[i]);
                f_sal.writeUTF(descripciones[i]);
            }
        } finally {
            f_sal.close();
        }

        ObjectInputStream f_ent = null;
        try {
            f_ent = new ObjectInputStream(new BufferedInputStream(new FileInputStream(archivoDeDatos)));

            Calendar fecha = null;
            BigDecimal precio;
            int unidades;
            String descripcion;
            BigDecimal total = new BigDecimal(0);

            fecha = (Calendar) f_ent.readObject();
            System.out.format("Hoy es %tA, %<te de %<tB de %<tY:%n%n", fecha);

            try {
                while (true) {
                    precio = (BigDecimal) f_ent.readObject();
                    unidades = f_ent.readInt();
                    descripcion = f_ent.readUTF();
                    total = total.add(precio.multiply(new BigDecimal(unidades)));
                }
            } catch (EOFException e) {
            }
            System.out.format("%nImporte total: %.2f€%n", total);
        } finally {
            f_ent.close();
        }
    }

}