Mostrar Mensajes

Esta sección te permite ver todos los posts escritos por este usuario. Ten en cuenta que sólo puedes ver los posts escritos en zonas a las que tienes acceso en este momento.


Mensajes - Kabuto

Páginas: 1 ... 16 17 18 19 20 [21] 22 23 24 25 26 ... 50
401
Hola.

En Java, no se puede hacer la opción de "pulse CUALQUIER tecla para continuar" ( y si alguien sabe hacerlo soy todo oídos)

La entrada de la consola Java es una "entrada almacenada en búfer", es decir, se va capturando lo que se teclea, pero la entrada no se da por finalizada hasta que no se pulsa la tecla ENTER.

Por tanto, Sí podemos dar la opción de detener el programa e indicar "pulse ENTER para continuar".
Pero no CUALQUIER tecla.

He visto alguna solución compleja a esto, como hacer una petición a Windows para que active la consola de comandos y sea él quien espere una pulsación de tecla, con lo cuál ya estaríamos escribiendo un código que no funcionaría bajo otros sistemas como Linux y que podría tener un comportamiento inesperado si lo estamos ejecutando desde un IDE como NetBeans, Eclipse o similar.

No vale la pena complicarse tanto para algo así.


Así que lo que sí podemos hacer es detener la ejecución del programa hasta que el usuario pulse ENTER.
Y esto lo hacemos simplemente usando el objeto Scanner para que el usuario haga una entrada de datos.
Esto implica que el usuario podrá teclear letras y números y se verá en pantalla lo que teclea. Esto es inevitable.
Pero bueno, nosotros lo que haremos será indicar en pantalla que lo que el usuario debe hacer es pulsar ENTER para continuar.
Si es rebelde y le da por teclear, pues ya es problema suyo.

Como en tu programa estás leyendo datos numéricos, con nextInt() y nextDouble(), previamente habrá que "limpiar" el buffer de entrada porque estos métodos dejan como residuo el carácter especial que representa a la tecla ENTER.
Y esto hace que cuando queramos hacer la pausa del programa, esta no se haga porque esos residuos hacen creer a Java que el usuario ya ha pulsado ENTER para continuar, cuando en realidad no ha tenido ocasión de hacerlo.

Esta pausa la haríamos después de los if que analizan la opción introducida por el usuario.
Por cierto, te he corregido los IF , pues la opción 4 que se supone es para terminar el programa la habías asignado a calcular el área del cuadrado.

Además he ampliado los IF, uno para informar al usuario de que se va a poner fin al programa cuando se pulsa 4, que siempre queda más informativo.
Y otro para cuando el usuario por error o lo que sea introduce una opción que no existe en el menú. En este caso también es inconveniente dar un mensaje en pantalla.


Para la pausa, usaremos el método nextLine() de la clase Scanner. Tanto para limpiar el buffer (capturar los residuos que hayan dejado nextInt() y nextDouble()) como para esperar a que se pulse ENTER.
Este método es el único que acepta una entrada de "datos vacía"
Podríamos limpiar con nextLine() y luego pausar con nextInt() por ejemplo. Pero nextInt() no aceptará que pulsemos ENTER sin más. Nos exigirá teclear un número antes de pulsar ENTER.

Por eso mejor usar nextLine().

Aquí te dejo el código. Pruébalo y pregunta lo que no entiendas.
Un saludo.

Código: [Seleccionar]
    public static void main(String[] args) {

Scanner neme = new Scanner(System.in);
int opcion;

do {
    System.out.println("Seleccione una de las opciones:");
    System.out.println("1.-Calcular el area de un triangulo\n2.-Calcular el area de un rectangulo\n3.-Calcular el area de un cuadrado\n4.-Salir");
    opcion = neme.nextInt();
    double base=0.0,altura=0.0,area=0.0,baserec=0.0,alturarec=0.0,arearec=0.0,basecua=0.0,alturacua=0.0,areacua=0.0;

    if(opcion==1) {
        System.out.println("Ingrese la base del triangulo");
        base = neme.nextDouble();
        System.out.println("Introduce la altura del triangulo");
        altura = neme.nextDouble();
        area=(base*altura)/2;
        System.out.println("El area del triangulo de base = " + base + " y altura = " + altura + ", es de " + area + " m2 ");
    }
    else if(opcion==2) {
        System.out.println("Introduce la base del rectangulo");
        baserec=neme.nextDouble();
        System.out.println("Introduce la altura del rectangulo");
        alturarec=neme.nextDouble();
        arearec=baserec*alturarec;
        System.out.println("El area del rectangulo de base = " + baserec + " y altura = " + alturarec + ", es de " + arearec + " m2 ");
    }
    else if(opcion==3) {
        System.out.println("Introdusca la bace del cuadrado");
        basecua=neme.nextDouble();
        System.out.println("Introdusca la altura del cuadrado");
        alturacua=neme.nextDouble();
        areacua=basecua*alturacua;
        System.out.println("El area del cuadrado de base = " + basecua + "y altura = " + alturacua + ", es de " + areacua + " m2 ");
    } else if (opcion == 4) {
        System.out.println("\nFIN DE PROGRAMA");
    } else { //Por si se introduce un valor que no es una opción de menú
        System.out.println("Opcion invalida");
    }
    //Para pausar la ejecución del programa
    neme.nextLine(); //Limpiamos buffer del System.in
    System.out.println("\n\t\tPRESIONE ENTER PARA CONTINUAR..."); //Mensaje en pantalla
    neme.nextLine(); //Ahora el programa se detiene hasta que se pulse ENTER
       }while(opcion != 4);
    }

402
No hay de qué. Encantado de ayudar.

Si no logras completar el programa o te queda alguna pregunta pendiente, no dudes en publicarla por aquí.

Un saludo.

403
Sí, pero será más laborioso.
Tendrás que decidir una forma de escribir los datos de manera que luego, al leerlo, te sea fácil desde el código interpretar lo que estás leyendo para reconstruir la matriz/tablero.

Por ejemplo, escribir los valores separados por comas, en tres líneas de texto:
Citar
0,0,1
2,0,2
0,0,1

Cada línea representa una fila de la matriz, y las comas las separa en columnas.
Luego se puede leer línea por línea.
Cada línea la dividimos en tres partes usando el método split(",")
Así sabemos que la primera parte, de la primera línea, es el valor de la posición 0,0 en la matriz
La segunda parte, de la primera línea es 0,1
La tercera parte es 0,2

Luego en la segunda línea, la primera parte corresponde a 1,0
Etc, etc...

Los valores de la matriz guardados como texto, los leeremos como String. Por tanto, habrá que convertirlos a int para poder devolverlos a la matriz..

Este podría ser el método para guardar como texto:
Código: [Seleccionar]
public void guardarPartidaTxt() {
try {
BufferedWriter escritor = new BufferedWriter(new FileWriter("d:/partida3Raya.txt"));
//Escribimos las tres filas de la matriz, con sus valores separados por comas
for (int fila = 0; fila < tablero.length; fila++) {
escritor.write(tablero[fila][0] + "," + tablero[fila][1] + "," + tablero[fila][2]);
escritor.newLine();
}
escritor.close();

} catch (IOException e) {
System.out.println("Problema de acceso a fichero: partida3Raya.txt");
}
}

Y este para leer el txt:
Código: [Seleccionar]
public void cargarPartidaTxt() {
try {
BufferedReader lector = new BufferedReader(new FileReader("d:/partida3Raya.txt"));
//Leemos primera linea, equivale a primera fila
String linea = lector.readLine();
int fila = 0;
while (linea != null) {
//Separamos valores
String[] valores = linea.split(",");
//Asignamos a donde corresponde en la matriz
tablero[fila][0] = Integer.parseInt(valores[0]);
tablero[fila][1] = Integer.parseInt(valores[1]);
tablero[fila][2] = Integer.parseInt(valores[2]);
//Pasamos a la siguiente linea y siguiente fila
linea = lector.readLine();
fila++;
}
lector.close();
} catch (FileNotFoundException e) {
System.out.println("No se encuentra fichero: partida3Raya.txt");
} catch (IOException e) {
System.out.println("Problema de acceso a fichero: partida3Raya.txt");
}
}

404
mmmh, tu método para guardar partida, el principal fallo que tiene es que estás usando clases Reader(BufferedReader, InputStreamReader,... )

Es decir, son clases para "leer", y tu lo que quieres es "escribir".

Si quieres guardar el contenido de la matriz tablero como texto plano, puedes usar BufferedWriter.

Sin embargo, puedes guardarlo directamente como un archivo de bytes. La ventaja es que es más cómodo y rápido de programar, son muy poquitas líneas.
La "desventaja", es que el archivo donde se guarda no será legible a ojos humanos. Pero en un caso como este, supongo que esto no importa, pues el propósito es que el programa pueda cargar y guardar partidas, no obtener un fichero que se pueda leer.


Para ello puedes usar la clase ObjectOutputStream.
Lo que hace es coger un objeto (en Java, una matriz es un objeto) y tal cuál está en ese momento en memoria RAM, lo traspasa a un archivo de bytes.

No es necesario recorrer la matriz  e ir guardando el valor de cada uno de sus elementos, no...
Se coge la matriz entera y se guarda como código binario.

El método podría ser tan sencillo como este:
Código: [Seleccionar]
public void guardarPartida(){
try {
ObjectOutputStream escritor = new ObjectOutputStream(new FileOutputStream("d:/partida3Raya.bin"));
//Escribimos la matriz tal cuál está en memoria RAM, como archivo binario
escritor.writeObject(tablero);
escritor.close();
} catch (FileNotFoundException e) {
System.out.println("No se encuentra fichero: partida3Raya.bin");
} catch (IOException e) {
System.out.println("Problema de acceso a fichero: partida3Raya.bin");
}
}

Luego, para recuperar los datos del archivo y pasarlos a la matriz tablero, usaríamos la clase opuesta, la ObjectInputStream.
Al leer el archivo nos retorna un Object (un objeto), el cuál para poder asignarlo como matriz tablero, habrá que hacer un casting a int[][]

Código: [Seleccionar]
public void cargarPartida(){
try {
ObjectInputStream lector = new ObjectInputStream(new FileInputStream("d:/partida3Raya.bin"));
//Leemos los bytes del archivo y nos retornará un Object, al que haremos casting a matriz int[]
tablero = (int[][]) lector.readObject();
lector.close();
} catch (FileNotFoundException e) {
System.out.println("No se encuentra fichero: partida3Raya.bin");
} catch (IOException e) {
System.out.println("Problema de acceso a fichero: partida3Raya.bin");
} catch (ClassNotFoundException e) {
System.out.println("Los datos obtenidos no permiten recuperar la partida");
}

}


Bien, ambos métodos deberían funcionar.
Pero ahora se te presenta otro problema.  :o
¿Cómo puede el usuario, en mitad de una partida, salirse de ella para regresar al menú y ordenar que se guarde la partida?

A ver si se te ocurre como solucionarlo.

Un saludo.

405
EL género es una letra: M o F.
No debes guardarlo como int:
Código: [Seleccionar]
System.out.println("Proporcione Genero:");
int genero = entrada.nextInt();

Si no como String, o como char

Código: [Seleccionar]
System.out.println("Proporcione Genero:");
char genero = entrada.nextLine().charAt(0);

Luego, para optimizar el código:
Vas a tener que hacer varios IF e IF ELSE, no hagas un System.out con la cadena completa en cada uno de ellos.
Además, la cadena está mal construida, la coma que hay detrás de (año) tendría que ir entre comillas, justo delante de la palabra usted

Código: [Seleccionar]
      if (año <= 1945) {
         System.out.println("Usted nacio en el"+(año), "usted es un"+(genero)"que pertenece a la generacion Del Silencio");
      }

Repetir ese System.out para todos los IF que aún te faltan, es redundante.
En lugar de eso, declara una variable String llamada generacion y lo que haces en los IF, es determinar su valor. Luego tras terminar los IF, usas esa variable para construir la cadena final.

De hecho, puedes hacer lo mismo con el género, ya que no puedes mostrar directamente la M o la F que te ha de dar el usuario, porque queda feo decirle a alguien:
"Usted es una M.."
 :o (¿Que soy una M...? ¿Este programa me está llamando MIERDA..??)  ;D

Mira, algo parecido a este código que pongo.
Por cierto, fíjate en como he escrito la línea donde pido el año.
Cambio dos cosas respecto a tu código:
1 - No uso la ñ. Se puede usar y funcionará en la mayoría de los casos, pero al ser un carácter que no pertenece al alfabeto inglés, a la larga puedes encontrarte situaciones en la que te de problemas.
Así que conviene evitar la ñ y no usarla ni para nombres de variables, de clases o de métodos.
En una cadena de texto, sí se puede usar.

2 - Mucho más importante. No uso el método nextInt() para pedir el año.
Lo pido usando nextLine(), que retorna un String y automáticamente lo transformo (parseo) en un valor int.
Código: [Seleccionar]
anio = Integer.parseInt(entrada.nextLine());¿Por qué?
Porque el Scanner, el objeto que usamos para pedir datos por teclado, dará problemas si tras pedir un valor con nextInt(), queremos pedir un valor con nextLine(), que es lo que vamos a necesitar para pedir el char del género.

Es un poco largo de explicar el motivo técnico...
Simplemente recuerda que cuando hagas un programa Java, si SOLO vas a pedir valores numéricos, puedes usar nextInt() sin problemas.
Pero si en el mismo programa vas a pedir valores numéricos(int, double, float...) y también de texto(String o char), entonces mejor pedir todos los valores con el método nextLine(), y luego transformarlo a int, a double, o a lo que se necesite.

De lo contrario dará problemas. Tu mismo puedes comprobarlo si quieres, cuando ya tengas el programa terminado y funcionando. Prueba a pedir el año con nextInt() y verás como luego no te será posible pedir el género para obtener un char.

Bueno, te dejo aquí el código de como podría hacerse para que quede más optimizado.
Solo falta completar los ELSE IF para determinar la generación según el año introducido.

Código: [Seleccionar]
    public static void main(String[] args) {
        Scanner entrada = new Scanner(System.in);   
        //Principales variables del programa
        int anio = 0;
        String genero = "", generacion = "";
     
        System.out.println("Proporcione año de nacimiento:");
        anio = Integer.parseInt(entrada.nextLine());
     
        System.out.println("Proporcione Genero:");
        char gen = entrada.nextLine().charAt(0);
       
        //Determinamos el Genero
        if (gen == 'M' || gen == 'm')
            genero = "Masculino";
        else if (gen == 'F' || gen == 'f')
            genero = "Femenino";
        else
            genero = "Otro";
     
        //A continuacion determinamos la generacion
        if (anio <= 1945)
            generacion = "Del Silencio";
        else if (anio >= 1946 && anio <= 1964)
            generacion = "Baby Boomer";
        //faltan el resto de if else...
       
       
        //Por último, construimos cadena de mensaje final
        System.out.println("Usted nacio en el " + anio + ", usted es un " + genero + " que pertenece a la generacion " + generacion);
    }


406
Mmhh, esta vez el código va bien encaminado, aunque no del todo.

La comprobación inicial para ver si existe o no el fichero está bien.
Pero luego estas líneas:

Código: [Seleccionar]
if (System.IO.File.Exists(nombre))
                   {
                    Console.WriteLine("el nombre existe");
                   }
                else
                {
                    Console.WriteLine("eel nombre no existe");
                }
Aquí no estás comprobando si el nombre existe dentro del archivo, que es lo que se pide.

Estás comprobando si existe un archivo cuya ruta sea igual al nombre, y no es eso lo que hay que lograr.

Una vez has comprobado que el archivo sí existe, entonces hay que leer las líneas de ese archivo y comprobar si alguna línea contiene el nombre.

Es decir, imagina que el archivo de texto seleccionado, contiene una lista de nombres con sus teléfonos:
Citar
Nombre: sara pelaez -- Telefono: 656789034
Nombre: laura juan -- Telefono: 690872311
Nombre: alfredo timoneda -- Telefono: 650812093
Nombre: eva llanes -- Telefono: 633908070
Nombre: lidia guarque -- Telefono: 654120987
Nombre: sandra cevallos -- Telefono: 621109082
Nombre: jorge morales -- Telefono: 656231050

Y como nombre a buscar nos indican: jorge morales

Pues hay que leer esas líneas y comprobar si alguna contiene la cadena "jorge morales".
Si la tiene, podemos mostrar la línea completa.

Si no la tuviera, habrá que informar de que no existe ese nombre.


Mira, te explico la lógica que yo he seguido y luego pongo el código completo al final.

* Primero pido el nombre a buscar
* Luego pido el nombre de fichero. Esta petición la hago dentro de un bucle de forma que se va a repetir hasta que:
- me indique una ruta de un fichero que si existe
- o bien teclee la cadena "fin" para terminar el programa, sin hacer ninguna búsqueda

*A continuación, si NO ha introducido "fin" y el bucle anterior ha terminado, es que entonces SÍ tengo un ruta de fichero válida.
Pues entonces inicializo un StreamReader y comienzo a leer líneas dentro de otro bucle.
Este bucle se repite hasta que:
- No queden líneas por leer en el fichero
- Encuentre el nombre que se busque. Esto lo controlo con una variable bool, si encuentro el nombre, le daré valor true a esta variable y entonces el bucle terminará, sin seguir leyendo más líneas.

* Por cada línea leída, compruebo si contiene el nombre indicado. ¿Cómo?
Pues cuando leo una línea, esta se guarda en un String. Este String, tiene un método llamado Contains() al cuál le puedo pasar una cadena de texto y me dice si ese String contiene esa cadena o no (true o false)
Así que le paso el nombre a buscar y si retorna true, indico en pantalla que se ha encontrado el nombre y muestro la línea completa.
A la variable bool le doy valor true, y así el bucle sabe que ya puede terminar.

* Por último, compruebo de nuevo el valor de esta variable bool.
Si su valor al final del programa es false, significa que el bucle ha terminado sin haber encontrado el nombre que se busca, así que lanzo un mensaje final avisando de que no existe ese nombre.

Este es el código. Fíjate también que solo necesito dos "using ...." al principio.
En tus códigos pones varios using innecesarios como estos:
Código: [Seleccionar]
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

Aquí mi código. Insisto en que lo repases y si algo no lo entiendes, pregúntalo.
No te conformes con tener una solución, asegúrate de que la comprendes.

Código: [Seleccionar]
using System;
using System.IO;

namespace BuscaNombreFichero
{
    class Program
    {
        static void Main(string[] args)
        {
            String linea, nombre, fichero;
            bool encontrado = false;

            Console.WriteLine("Nombre a buscar: ");
            nombre = Console.ReadLine();

            //Con el bucle repito la solicitud de archivo hasta que exista o hasta que el usuario elija "fin"
            do
            {
                Console.WriteLine("Fichero donde hacer busqueda(escriba 'fin' para terminar): ");
                fichero = Console.ReadLine();
                if (!fichero.Equals("fin"))
                {
                    if (!File.Exists(fichero))
                        Console.WriteLine("No se encuentra ese fichero...\n");
                }
            } while (!fichero.Equals("fin") && !File.Exists(fichero));


            if (!fichero.Equals("fin")) //El siguiente código solo se ejecuta si NO se ha introducido "fin"
            {
                StreamReader lector = new StreamReader(fichero);

                //Leemos mientras hayan lineas, o hasta que se encuentre el nombre buscado
                while ((linea = lector.ReadLine()) != null && !encontrado)
                {
                    //Comprobamos si esta linea contiene el nombre
                    if (linea.Contains(nombre))
                    {
                        Console.Write("El nombre ha sido encontrado en la linea: ");
                        Console.ForegroundColor = ConsoleColor.Yellow;
                        Console.WriteLine(linea);
                        Console.ResetColor();
                        encontrado = true; //Esto pone fin al bucle
                    }
                }

                lector.Close();
                if (!encontrado)
                    Console.WriteLine("No se ha encontrado el nombre en el fichero.");
            }
        }
    }
}

407
Hola.

La clase Comparator está mal escrita.

Fíjate que las dos condiciones de los if, son las mismas
Citar
        if (o1.getNota()>o2.getNota()) {
            return -1;
        } else if (o1.getNota()>o2.getNota()) {
            return 0;
        } else{
            return 1;
        }


Para que quede bien, y ordene de mayor a menor, lo escribiríamos así:

Código: [Seleccionar]
public class OrdenandoCalificacion implements Comparator<Perro> {
   
    @Override
    public int compare(Perro o1, Perro o2) {
       
        if (o1.getNota() < o2.getNota()) { //Si primer perro tiene nota menor
            return 1; //Retornamos positivo porque ha de quedar por DETRÁS de los MAYORES
        } else if (o1.getNota() > o2.getNota()) { //Si primer perro tiene nota mayor
            return -1; //Retornamos negativo porque ha de quedar por DELANTE de los MENORES
        } else{ //Si son iguales
            return 0; //Retornamos 0
        }
    }
}

Luego, hay otro error, en la clase Competencia, en el método donde ordenamos y mostramos el ranking.
El bucle for inicializa su índice con el valor del tamaño del array registro, es decir, que si tenemos 10 participantes registrados, la i comienza con valor 10.
Y luego le pides que el bucle se repita, mientras se menor que 3. ¿Por qué 3?
Es decir, no se va a ejecutar si tenemos 3 o más participantes registrados, porque la i ya comienza con un valor que no es "menor que 3"

Citar
    public void ranking(){
       
        Collections.sort(registro, new OrdenandoCalificacion());
       
        String dato = "";
       
        for (int i = registro.size(); i < 3; i++) {
            dato += "       RANKING DE PREMIADOS"+ "\n";
            dato += "Participante Nro."+registro.get(i).getPlaca() +"\n";
            dato += "Calificacion: "+registro.get(i).getNota();
        }
        JOptionPane.showMessageDialog(null, dato);
       
    }

Parece más lógico que la i comience con valor 0 y se repita mientras no alcance el tamaño del arraylist, para así mostrar todos los participantes.

Código: [Seleccionar]
    public void ranking(){
       
        Collections.sort(registro, new OrdenandoCalificacion());
       
        String dato = "";
       
        for (int i = 0; i < registro.size(); i++) {
            dato += "       RANKING DE PREMIADOS"+ "\n";
            dato += "Participante Nro."+registro.get(i).getPlaca() +"\n";
            dato += "Calificacion: "+registro.get(i).getNota();
        }
        JOptionPane.showMessageDialog(null, dato);
       
    }

Ahora sí, el ranking se ordena y se muestra por completo.

Por cierto, los ArrayList ya incorporan el método sort(), así que también le puedes pedir a él que ordene sin tener que llamar a la clase Collections

Y la cadena "RANKING DE PREMIADOS" se añade al texto para cada participante.
Para que solo salga una vez, a modo de título, tenemos que añadirlo al String dato al principio, antes de que comience el bucle.
Así queda mejor:

Código: [Seleccionar]
    public void ranking(){
       
        //Collections.sort(registro, new OrdenandoCalificacion());
    registro.sort(new OrdenandoCalificacion());
       
        String dato = "       RANKING DE PREMIADOS\n";
       
        for (int i = 0; i < registro.size(); i++) {
            dato += "\n";
            dato += "Participante Nro."+registro.get(i).getPlaca() +"\n";
            dato += "Calificacion: "+registro.get(i).getNota();
        }
        JOptionPane.showMessageDialog(null, dato);
       
    }

408
Para poder guardar la factura, podria hacer el mismo codigo que para guardar clientes y productos?

Sí, aunque si lo quieres guardar también como texto plano.... va a resultar bastante más complicado.
¿Por qué?
Porque la clase Factura tiene algunos atributos que son objetos "complejos".

Cuando se trata de un String, un int, double.... no hay problema para escribirlos como texto.
Lo único a tener en cuenta, es que al leerlos habrá que convertir a int, a double o lo que sea..

Pero que pasa cuando nos encontramos con atributos más complicados.

Hay un atributo que es el Cliente
Código: [Seleccionar]
    /**
     * El client de la factura
     */
    private Client client = null;
   

Para guardar esto como texto, se puede desglosar sus atributos para luego reconstruirlo.
Aunque se podría optimizar guardando como texto solo el id de cliente, y luego al leer el archivo de texto para reconstruir las Facturas, buscar el cliente a través del id

También hay un atributo que es LocalDateTime

Código: [Seleccionar]
    /**
     * Data de creació de la factura
     */
    private LocalDateTime dataFactura = null;

¿Cómo escribes eso en una línea de texto?
Se puede hacer, extraer dia, mes y año(que son valores int) y escribirlos como texto plano.
Luego al leer, con ese dia, mes y año se puede reconstruir el objeto LocalDateTime.

Pero esto ya exige escribir más código y desarrollar una línea de texto más compleja.


Y espera, ahora viene lo mejor, hay un atributo que es un array de objetos ProducteFactura
Código: [Seleccionar]
    /**
     * Array amb els productes de la factura
     */
    private ProducteFactura[] producteFactures = null;

¿Cómo guardas ese array en texto plano?  :P

Pufff.... se puede guardar desglosando los atributos correspondientes a cada objeto ProducteFactures.
Y luego al leer, reconstruir esos objetos y con ellos, reconstruir el array.
De nuevo esto exige un código más complejo.

Pero es que hay un problema añadido.
Las líneas de texto que usamos para guardar Productos o Clientes.., tienen un número de atributos fijos, así que todas las líneas van a tener la misma cantidad de valores.
Así cuando hacemos split(), ya sabemos de antemano cuántos elementos nos va a devolver y en el código ya sabemos a que atributo corresponde cada elemento obtenido mediante split().

Pero en este caso, resulta que cada objeto Factura, su array de ProducteFactura puede tener distintas longitudes.
Una Factura puede que solo tenga dos productos facturados, otra puede que tenga cinco, otra puede tener diez....

Es decir, que cada línea de texto plano que representa una Factura, el método split() nos va a retornar distintas cantidades de elementos.
Porque cada Factura tendrá un número distinto de elementos facturados.

Aquí ya no solo se va a requerir más código, si no más ingenio para encontrar una forma de crear una línea con toda esa cantidad de datos y que luego sepamos leerla bien para reconstruir los datos.

Tal vez se podría cambiar la estrategia de "una línea por objeto".
Es decir, hasta ahora hacíamos una línea de texto por cada Producto, otra por cada Cliente...

En el caso de las Facturas, se podría usar varias líneas por cada Factura.
La primera línea, todos los atributos menos el array.
Y al final de la línea un número que nos diga cuantas líneas de las que vienen a continuación, corresponde a los elementos del array.

Algo parecido a esto:
Citar
0001 -- 22/03/2021 -- 21.0 -- Efectivo -- 0045 -- 3
-- Datos de un objeto ProducteFacture
-- Datos de un objeto ProducteFacture
-- Datos de un objeto ProducteFacture
0002 -- 12/01/2021 -- 16.0 -- Visa -- 0023 -- 2
-- Datos de un objeto ProducteFacture
-- Datos de un objeto ProducteFacture

Como ves, resulta bastante más complejo y trabajoso guardar las Facturas como texto plano.
Pero se puede.

En cambio si serializamos para guardar en un archivo de bytes, no hay problema, no importa lo complejos que sean los objetos que queremos guardar.

Por cierto, ahora que me fijo, la clase ProducteFacture tiene atributos redundantes.
Podría bastar con que solo guardase referencia del producto y cantidad.
Lo que es el nombre, precio y descripción... ya lo tiene la clase Producte y esos datos se podrían obtener consultando al array de Productes a través de la referencia.
 

409
¿Has probado a ejecutar tu código?
Supongo que no, ya que de hecho no va a compilar porque hay alguna instrucción mal escrita:
Citar
escribir = Convert.ToByte(Console.Redline());

Pero es que además hay varios using innecesarios, se declaran dos variables estaticas de tipo byte, que luego no se usan para nada (porque de todos modos no tienen ninguna utilidad).

Cuando se pide el nombre del fichero, se convierte a byte lo que se recoja, para guardarlo en una variable int.
Código: [Seleccionar]
            Console.WriteLine("Escribe el fixero a leer: ");
            fixero = Convert.ToByte(Console.ReadLine());
No tiene ningún sentido. Ahí lo que pasará que si le doy el nombre "miFichero.txt", esto obviamente es imposible convertirlo a números, ni a int ni aún menos a byte (un byte es un número entre 0 y 255).
Por lo tanto se producirá una excepción y el programa se detendrá.

Esta línea tampoco la aceptará el compilador:
Código: [Seleccionar]
void AdicionarInfoAlTxt()¿Qué es?
Un intento de declaración de un método? ¿Sin punto y coma al final?
 ¿Sin llaves de apertura y cierre para definir su código?
De todos modos está dentro del método Main(), no se puede declarar/definir un método, dentro de otro método.

Y bueno, aunque nada de todo esto fallase, la lógica que sigue el código no es la adecuada de todas maneras. Primero leería las líneas, las mostraría en pantalla y cerraría fichero de lectura.

Luego intentaría escribir en el fichero copia, pero las líneas leídas no están guardadas en ningún sitio, así que no hay nada disponible para escribir.


No quiero ser demasiado crítico, pero quiero que veas que copiar retazos de código de aquí y de allá y juntarlo confiando que sirva de algo... no lleva a ninguna parte.
Es que ni siquiera has comprobado tú mismo si funciona o no.


No copies/uses código que no entiendes. Tienes que conseguir escribir tu propio código, aunque luego no funcione o no cumpla con todo lo que pide el enunciado.
Y antes de escribir código, tienes que tener clara la lógica que quieres seguir.

La lógica a seguir ya te la dije en el mensaje anterior:
Citar
Tienes que pedir dos nombres de archivo al usuario: nombre de archivo a leer y nombre de archivo donde guardar una copia del primero.

Entonces hay que leer el primer archivo y escribir las mismas líneas en el otro.

Si hubieras hecho un programa donde, al menos, pidiese los dos nombres de archivo y los guardase en el tipo de dato adecuado (String, no un byte o int), ya tendría mucho más mérito que la amalgama de código anterior.

Sigamos la lógica:
- Pedir nombre de archivo a leer. Si es un nombre lo que pedimos, es decir, una cadena de texto pues se recoge en un String.
Luego, usando ese String, podemos inicalizar un objeto StreamReader que será quien luego leerá líneas de ese fichero:
Código: [Seleccionar]
        static void Main(string[] args)
        {
            Console.Write("Fichero a leer: ");
            String origen = Console.ReadLine();
            System.IO.StreamReader ficheroOrigen = new System.IO.StreamReader(origen);
        }

Continuamos:
-Pedir nombre de archivo donde escribir. Es decir, el fichero donde haremos una copia del anterior. El proceso es el mismo, solo que esta vez inicializaremos un objeto StreamWriter, que será quién escriba.

Código: [Seleccionar]
        static void Main(string[] args)
        {
            Console.Write("Fichero a leer: ");
            String origen = Console.ReadLine();
            System.IO.StreamReader ficheroOrigen = new System.IO.StreamReader(origen);
           
            Console.Write("Fichero donde hacer copia: ");
            String copia = Console.ReadLine();
            System.IO.StreamWriter ficheroCopia = new System.IO.StreamWriter(copia);
        }

Siguiente paso:
Leer el primer archivo y escribir las mismas líneas en el otro.
Lo que haremos será leer línea a línea, tal y como hicimos en el primer ejercicio.
Por cada línea leída, la mostramos en pantalla. Pero además, tenemos que pedirle al StreamWriter que la escriba en el fichero "copia".
Porque esas líneas no se guardan en ningún sitio, cada vez que leemos una, la línea anterior se pierde.
Así que al mismo tiempo que leemos y mostramos en pantalla, también se la entregamos al StreamWriter.

Cuando el bucle que lee las líneas termina, hay que cerrar ambos Streamers, tanto el de lectura como el de escritura.
Especialmente el de escritura, si no lo cerramos, las líneas "escritas" no se transferirán al fichero de texto, así que no habremos conseguido nada.

Dejo el código final al completo. Para mostrar las líneas en pantalla de una forma más destacada, cambia el color del texto a amarillo. Pero esto ya son adornos estéticos, no es lo importante.

Código: [Seleccionar]
using System;

namespace CopiarFichero
{
    class Program
    {
        static void Main(string[] args)
        {
            Console.Write("Fichero a leer: ");
            String origen = Console.ReadLine();
            System.IO.StreamReader ficheroOrigen = new System.IO.StreamReader(origen);
           
            Console.Write("Fichero donde hacer copia: ");
            String copia = Console.ReadLine();
            System.IO.StreamWriter ficheroCopia = new System.IO.StreamWriter(copia);

            Console.WriteLine("\n\n\t\tIniciando la copia linea a linea...");
            String linea;

            while ((linea = ficheroOrigen.ReadLine()) != null)
            {
                //Mostramos la linea actual en pantalla
                Console.WriteLine("Copiando linea:");
                Console.ForegroundColor = ConsoleColor.Yellow;
                Console.WriteLine(linea);
                Console.ResetColor();

                //Escribimos la linea en el archivo copia
                ficheroCopia.WriteLine(linea);
            }
            ficheroOrigen.Close();
            ficheroCopia.Close();

            Console.WriteLine("\n\n\t\tCopia finalizada");

        }
    }
}

A continuación pongo el resultado obtenido en pantalla.
Fíjate que cuando se piden los nombres de archivos, hay que dar una ruta completa.
Se podría dar una ruta relativa..., pero puede ser más difícil acertar con la ruta correcta, ya que dependerá de donde se ejecuta el archivo .exe del programa.
Así que mejor dar una ruta completa, de un archivo de texto que tengamos en la raíz del disco, para no tener que indicar demasiadas carpetas en la ruta.
El archivo a leer tiene que existir.
El archivo a copiar, no hace falta que exista, el programa lo creará automáticamente.



Y obviamente no solo se ven las líneas en pantalla.
El fichero copiado aparecerá en la ruta que hayamos indicado.


Repasa el código que he puesto y comprueba que entiendes cada línea, que significa cada instrucción, por qué las estamos usando, cuál es la lógica que se ha seguido...

Lo que no entiendas, no dudes en preguntar.

Luego veremos el tercer ejercicio.

Saludos.

410
Ese error parece como que alguna línea leída, al hacer split(),no ha proporcionado un array de cuatro elementos, si no de tres. (el error dice que length es 3)
Entonces, al intentar obtener el valor que estaría en la posición [3] del array que retorna el split() se produce ese error de "índice fuera de limites" (out of bounds), porque no existe dicha posición.

Comprueba que las líneas que se escriben en el archivo de texto de clientes, tienen 4 valores, ya que los Clientes tienen cuatro atributos.

Si a caso has decidido guardar solo tres atributos, porque por ejemplo no estás guardando el idCliente, entonces cambia el código para luego solo recoger los elementos de las posiciones [ 0 ], [1] y [2].


411
Sobre el segundo ejercicio, el código que has puesto no se corresponde con nada de lo que pide el enunciado.

Haz un intento escribiendo tú el código, no copiando algún ejemplo (que no resuelve nada de lo que se pide)

Tienes que pedir dos nombres de archivo al usuario: nombre de archivo a leer y nombre de archivo donde guardar una copia del primero.

Entonces hay que leer el primer archivo y escribir las mismas líneas en el otro.

Inténtalo, llega hasta donde puedas, y luego corregimos y completamos.

412
Respecto al primer ejercicio.

Esta línea de código:
Código: [Seleccionar]
String[] words= line.Split(' '); Eso lo que hace es dividir la línea en palabras, cada palabra queda insertada en el array llamado words.
Pero luego no haces con ese array. De todos modos, el enunciado del ejercicio no te dice que busques palabras.

Esta otra línea:
Código: [Seleccionar]
count = count + char.Length;
Directamente no compila... char.Length  no es nada. Se estaría llamando a la propiedad Length de la clase char, pero es que no tiene dicha propiedad..

Lo que necesitas son dos String.
Uno para leer línea a línea el archivo de texto.
El otro String, para guardar la línea mayor.
Cada vez que se lee una línea se comparan la longitud de la línea leída con la que tengamos guardada como línea mayor (estas si tienen la propiedad .Length)
Si la línea recién leída es más larga, pasa a ser guardada como línea mayor y se sigue comparando con las siguientes líneas.

Además usaremos dos int.
Uno para contar las líneas leídas y el otro para guardar la posición de las línea que se va guardando como mayor.

Luego, lo que no se nos pide es que una vez tengamos finalmente la mayor línea de todas, calculemos cuantas vocales tiene.

Esto, para que el código quede mejor estructurado, es mejor usar un método que reciba la línea y retorne un int con la cantidad de vocales encontradas.
Para buscar vocales en la línea, lo que haremos será transformar el String que contiene la línea, en un array de char.
Recorremos el array, carácter a carácter, y comprobamos si es una vocal. Para esto podemos usar un switch, donde si el caracter es 'a', 'e', 'i', 'o', 'u'... pues incrementamos un contador.
Finalmente se retorna este contador, que indica las vocales encontradas.

Este podría ser el código:

Código: [Seleccionar]
        static void Main(string[] args)
        {
            String linea, lineaMayor = "";
            int contadorLinea = 0, posicionLineaMayor = 0;
            System.IO.StreamReader file = new System.IO.StreamReader(@"d:\curso.txt");

            while ((linea = file.ReadLine()) != null)
            {
                contadorLinea++; //Contamos línea leída

                //Comparamos tamaño linea actual, con la linea Mayor encontrada hasta ahora
                if (linea.Length > lineaMayor.Length)
                {
                    //Es mayor, esta linea pasa a ser la linea mayor y guardamos su posicion
                    lineaMayor = linea;
                    posicionLineaMayor = contadorLinea;
                }

            }
            file.Close();
            //Ya tenemos la mayor línea y su posicion, mostramos resultados
            Console.WriteLine("Mayor linea encontrada:\n" + lineaMayor);
            Console.WriteLine("\t\n*****************************\n");
            Console.WriteLine("Posicion:" + posicionLineaMayor);
            Console.WriteLine("Cantidad de vocales: " + cuentaVocales(lineaMayor));

        }

        static int cuentaVocales(String linea)
        {
            int vocales = 0;

            //Convertimos la linea String a un array de caracteres (todos en minúscula)
            char[] caracteres = linea.ToLower().ToCharArray();

            //Repasamos cada caracter con un bucle y buscamos vocales con un switch
            for (int i = 0; i < caracteres.Length; i++)
            {
                switch(caracteres[i])
                {
                    case 'a':
                    case 'e':
                    case 'i':
                    case 'o':
                    case 'u':
                        vocales++;
                        break;
                }
            }

            return vocales;
        }

Si ejecutamos el código, parece funcionar correctamente:

Citar
Mayor linea encontrada:
¿Por qué se obliga en C a añadir archivos de cabecera incluso para funciones básicas del lenguaje? Posiblemente porque cuando se creó C se hizo teniendo muy en cuenta la eficiencia y el evitar sobrecargar el computador ocupando memoria inútilmente. Imagínate que eres el cliente de un restaurante ("el cliente del compilador") y que vas a pedir un menú. El dueño del restaurante no prepara todos los platos posibles y los pone a tu disposición, sino que espera a que tú le digas los platos que deseas y son únicamente esos los que prepara. De esta forma ahorra espacio y esfuerzo. En nuestro caso, en lugar de cargar en memoria y poner a tu disposición todas las posibles funciones del lenguaje, el compilador sólo carga y pone a disposición aquellas funciones que declaramos que vamos a usar. De este modo se gana en eficiencia.

*****************************

Posicion:135
Cantidad de vocales: 300

413
Comunidad / Re: Presentación
« en: 23 de Mayo 2021, 00:35 »
Hola y bienvenido.

Si encuentra el curso adecuado, es posible aprender a programar al mismo tiempo que aprendes el lenguaje.

Es habitual aprender a programar mediante pseudocódigo, que además hoy día con programas como PSeInt se puede ejecutar como si fuera un lenguaje real.
Yo en los años 90 (también tengo mis añitos) estudié programación, empezando con pseudocódigo, y el primer año apenas tocamos los ordenadores.. :o
Todo era con lápiz y papel....

La ventaja del pseudocódigo es que es simple y no necesitas que lo que escribas te lo valide ningún compilador. Pero en realidad no es muy distinto de tener que aprender y usar un lenguaje real.

Y precisamente Python, tiene una sintaxis muy sencilla. Por lo que no es problema aprender a programar al mismo tiempo que se aprende Python.

Eso sí, lo importante es encontrar un curso que trate sobre "aprender a programar con Python", y no solo "aprender Python".

Es decir, no es lo mismo que te enseñen "cómo se hace un bucle con Python" (se da por hecho que ya sabes qué es un bucle), a que te enseñen "qué es un bucle, que utilidad tiene, cómo sacarle partido...y ya de paso cómo hacerlo en Python". Mucho mejor esto último.


Un saludo.

414
Al guardar el archivo en disco, no se puede leer un carajo... porque NO es un archivo de texto.
Es un archivo de bytes, lo que guardamos es una copia del "lenguaje máquina" tal cuál está en la memoria RAM.

Por eso en el programa lo puse para que se guardase con extensión .bin (binary code).
Se le puede poner cualquier extensión, sus datos internos son los mismos. Pero si le ponemos .txt, se puede dar a entender que es un archivo de texto. Y no lo es.


La ventaja de guardar los datos como bytes, es que el código para hacer esto es muy sencillo.
Básicamente necesitamos una instrucción para escribir, y otra para leer. Punto pelota.

La desventaja, que si luego queremos poder leer su contenido abriéndolo como un archivo de texto, nos vamos a encontrar un galimatías.


Se puede guardar también como archivo de texto plano, es decir, un verdadero y genuino .txt

El proceso es muy parecido, pero implica hacer más (muchas más) operaciones extra, además de decidir cómo nos conviene que se escriban los datos.
Porque según como los escribamos, luego será más fácil o más difícil leerlos.
Me explico:

Un Producto tiene cuatro atributos: referencia(int),  nombre(String), precio(double) y descripcion(String)

Supongamos que tenemos 5 Productos registrados en un ArrayList y queremos guardarlos en un archivo de texto plano.
Tendremos que recorrer el ArrayList con un bucle para obtener los tres atributos de cada Producto y escribirlos en el archivo de texto (esto como decía, ya supone realizar varias instrucciones de más comparado con antes).

Si simplemente vamos escribiendo en el archivo de texto, según vamos obteniendo valores de atributos, tendremos un archivo de texto como este:

Citar
1Coca-Cola1.25Bebida refrescante azucarada2Coca-Cola Light1.30Bebida refrescante ligera en azucares3Coca-Cola Zero1.30Bebida refrescante sin azucar4Fanta Limon1.25Bebida refrescante sabor limon5Fanta Naranja1.25Bebida refrescante sabor naranja

Vale, ahora si tenemos texto que entiende un humano, pero esta todo apelotonado en una sola línea.
No solo es feo a nuestros ojos, esto es lo de menos.
El auténtico problema es que luego para recuperar los datos en nuestro programa, tenemos que saber que palabras y números corresponden a cada atributo, para podre reconstruir los objetos Producto que habíamos guardado de la sesión anterior.

¿Entiendes por donde voy?
Antes, serializando todo en un archivo de bytes, era muy sencillo porque los objetos Producto se guardan tal cuál estaban en memoria RAM. Luego al leerlos no hay que reconstruir nada, porque permanecen "construidos" en el archivo de bytes.

Esto no es posible hacerlo en el archivo de texto plano, aquí solo podemos guardar los valores de los atributos de los productos, pero no los objetos Producto tal cual.
Y luego, hay que recuperar esos valores y separarlos, y utilizarlos para volver a construir otra vez los Productos (habrá que hacer otra vez new Producto() para cada uno y agregarlos otra vez al array de Producte[] )

Así que para facilitar esta tarea, primero hay que decidir una forma más adecuada para escribir los valores en el txt.
Lo habitual es hacer una línea para cada Producto.
Citar
1Coca-Cola1.25Bebida refrescante azucarada
2Coca-Cola Light1.30Bebida refrescante ligera en azucares
3Coca-Cola Zero1.30Bebida refrescante sin azucar
4Fanta Limon1.25Bebida refrescante sabor limon
5Fanta Naranja1.25Bebida refrescante sabor naranja

Bien, ahora ya sabemos donde comienza y termina cada Producto. Cada línea es un Producto.
Pero no sabemos bien donde comienzan y terminan cada valor de los atributos.
Tenemos que decidir alguna forma óptima de separarlos.
Así después, leeremos las líneas una por una. Y cada línea la partiremos en trozos allá donde detectemos lo que separa un valor de otro.
¿Y con qué separamos un valor de otro?

No podemos usar espacios en blanco, porque por ejemplo los nombres de producto o el texto de la descripción contienen espacios en blanco. Así que esto no nos va a servir para saber con certeza donde comienza un valor y termina otro

Podemos usar una coma para separar cada valor
Citar
1,Coca-Cola,1.25,Bebida refrescante azucarada
2,Coca-Cola Light,1.30,Bebida refrescante ligera en azucares
3,Coca-Cola Zero,1.30,Bebida refrescante sin azucar
4,Fanta Limon,1.25,Bebida refrescante sabor limon
5,Fanta Naranja,1.25,Bebida refrescante sabor naranja
Esta solución puede servir, si estamos seguros de que ni el nombre ni la descripción van a tener comas.
Sin embargo es arriesgado no solo por esos atributos, si no también por el precio. Yo lo he puesto con punto decimal, pero en realidad, cuando se guarde en archivo es posible que se guarden con coma decimal, esto puede variar según el sistema operativo y la configuración regional del usuario.
De hecho, luego al leer los datos, el que corresponde al precio habrá que parsearlo a double porque al leer del txt lo vamos a obtener como String, pero el atributo es un double.
Y habrá que controlar que no nos llegue con coma decimal, porque entonces el parseo a double fallará...solo es posible parsear a double si el texto leído tiene un punto decimal, no coma decimal.

Así que por todo esto, mejor usar otro símbolo para separar los productos. Para curarnos en salud, lo mejor es usar un conjunto de símbolos, por ejemplo dos guiones seguidos con espacio en blanco a cada lado.

Citar
1 -- Coca-Cola -- 1.25 -- Bebida refrescante azucarada
2 -- Coca-Cola Light -- 1.30 -- Bebida refrescante ligera en azucares
3 -- Coca-Cola Zero -- 1.30 -- Bebida refrescante sin azucar
4 -- Fanta Limon -- 1.25 -- Bebida refrescante sabor limon
5 -- Fanta Naranja -- 1.25 -- Bebida refrescante sabor naranja
Así se lee mejor a simple vista, y lo mejor de todo es que luego raro sería que ocurriesen errores al trocear las líneas correctamente en cuatro valores, uno para cada atributo.

Disculpa si me extiendo mucho con la explicación, pero es para que quede claro que guardar los valores como texto plano, supone ciertas complicaciones.

Ahora veremos como resolver todo esto con código Java.
De nuevo trabajaremos con los Productos y usaremos métodos específicos para guardar como texto plano, sin quitar lo que habíamos hecho antes. Así tendremos las dos versiones para practicar.

Lo primero que haremos, para facilitar las cosas, es crear un método en la clase Producto que nos construya una línea de texto con los atributos, tal y como queremos que se guarden en el archivo txt.
Algo parecido a lo que hacemos con el método toString(), pero este lo usaremos para escribir el Producto en una línea de texto.

Dicho método, puede ser como este:
Código: [Seleccionar]
    public String toTxt() {
    return String.format("%d -- %s -- %.2f -- %s", refProducte, nom, preu, descripcio);
    }
Eso ya nos devuelve una línea de texto, con los atributos separados por los dobles guiones.
También conviene reiniciar a 0 el contador interno de Productos creados que tiene la clase Producte.
Este contador se usa para establecer el atributo de la referencia cuando creamos un Producto nuevo. Como al leer líneas de texto, vamos a crear Productos nuevos (con valores guardados de antes) para evitar inconsistencias con las referencias, es mejor que comience desde 0 otra vez.
Así que le añadimos también este método a la clase Producte (ha de ser estatico):
Código: [Seleccionar]
    public static void resetTotalProductes() {
    totalDeProductes = 0;
    }
Esto no lo tuvimos en cuenta la vez anterior, y creo que también habría sido importante hacerlo.


Bien, pues ahora en la clase principal, añadimos nuevo método para guardar productos, esta vez en un archivo de texto plano.
Para esto usaremos la clase BufferedWriter, que a su vez necesita un FileWriter, que a su vez necesita un File.
El proceso es parecido a la forma anterior, pero fíjate que antes escribíamos tal cual el array de Productos.
Ahora, hay que recorrer el array y a cada Producto, pedirle que nos retorne la línea de texto, que será lo que guardaremos en el archivo.

Código: [Seleccionar]
private static void guardarProductesTXT() {

File fitxerProductes = new File("productes.txt");

if (!fitxerProductes.exists()) {
try {
fitxerProductes.createNewFile();
} catch (IOException e) {
System.out.println("\n-- ERROR. No s'ha pogut crear: " + fitxerProductes.getAbsolutePath());
return; //Posem fi a l'operació. No es pot guardar en aquesta ubicació.
}
}

try {
BufferedWriter bw = new BufferedWriter(new FileWriter(fitxerProductes));
//Per cada Producte de l'array, escriurem una linia
for (int i = 0; i < comptadorDeProductes; i++) {
bw.write(productes[i].toTxt());
bw.newLine(); //Fen una nova linia per al següent producte
}
bw.close();
} catch (IOException e) {
System.out.println("\n-- ERROR. No es pot accedir a fitxer: " + fitxerProductes.getAbsolutePath());
}

}

Y ahora otro método para el proceso opuesto, leer las líneas de texto, separar los valores, parsear a int la referencia, parsear a double el precio, y crear nuevos Productos mediante estos valores.
Para leer usaremos la clase BufferedReader, que a su vez necesita un FileReader, que a su vez necesita un File...

Las líneas que vamos a leer las recibimos como String, y String tiene el método split() con el que podemos ordenar que "trocee" la línea allá donde encuentre los dobles guiones que hemos usado para separar los valores.
Al trocear la linea, vamos a recibir un array que contiene cada "trozo" obtenido. Estos "trozos" son los valores de los atributos, que usaremos para reconstruir los Productos.
El primer "trozo" (posicion 0 del array que nos da split()) es la referencia, pero como este valor se autoasigna de forma interna con el contador que tiene la clase Producte, no lo usaremos para nada.
Por eso antes hicimos el método para resetear este contador.

Con el resto de trozos, crearemos un Producto, lo añadimos al array, y pasamos a la siguiente línea.
Código: [Seleccionar]
public static void recuperarProductesTXT() {

File fitxerProductes = new File("productes.txt");

if (!fitxerProductes.exists())
System.out.println("No hi ha productes guardats per recuperar.");
else {
try {
//Reiniciem l'array actual de Productes. Si tenía Productes, ¡¡s'esborrarán!!
productes = new Producte[MAXIM_PRODUCTES];
comptadorDeProductes = 0;
//També el comptador intern de la clase Producte
Producte.resetTotalProductes();

//Declarem el Reader
BufferedReader br = new BufferedReader(new FileReader(fitxerProductes));
//Obtenim primera linia
String linia = br.readLine();
do {
//Dividim linia per separar valors de atributs
String[] atributs = linia.split(" -- ");
//Reconstruim producte, alguns valors requereixen ser parsejats.
//atributs[0] es la referencia, pero aquest valor s'encarrega el comptador intern de Producte
String nom = atributs[1];
//El preu segurament tindrà coma decimal. Cal canviar-lo per un punt decimal.
atributs[2] = atributs[2].replaceAll(",", ".");
double preu = Double.parseDouble(atributs[2]);
String descrip = atributs[3];
productes[comptadorDeProductes] = new Producte(nom, preu, descrip);
comptadorDeProductes++;

//Producte reconstruir, llegim linia següent
linia = br.readLine();
}while (linia != null); //Repetim procés fins que no quedin linies
//Ja no quedan linies, procés finalitzat
br.close();

} catch (FileNotFoundException e) {
System.out.println("\n-- ERROR. No es troba fitxer: " + fitxerProductes.getAbsolutePath());
} catch (IOException e) {
System.out.println("\n-- ERROR. No es pot accedir a fitxer: " + fitxerProductes.getAbsolutePath());
}
}

}

Importante comprender que cada vez que recuperamos Productos, ya sea del archivo binario o de texto, los posibles Productos creados en ese momento en el array de Productos se borrarán.


Y bueno, ya por último, para poder escoger una forma u otra tanto para guardar como para leer, he cambiado el método menu() añadiendo una variable char para que el usuario elija como quiere hacerlo.
Marco en negrita los cambios:
Citar
   public static void menu(){
      Scanner sc = new Scanner(System.in);
      boolean sortir = false;
      char opcioArxiu = ' ';
      do{
         System.out.println("***** Gestió de factures *****");
         System.out.println("Dades emissor: " + Emissor.getString());
         System.out.println();
         System.out.println("--- Menú d'usuari ---");
         System.out.println("\t1) Crear nova factura");
         System.out.println("\t2) Visualitzar totes les factures");
         System.out.println("\t3) Cercar factura");
         System.out.println("\t4) Eliminar factura");
         System.out.println("\t5) Crear client");
         System.out.println("\t6) Visualitzar client");
         System.out.println("\t7) Crear producte");
         System.out.println("\t8) Visualitzar producte");
         System.out.println("\t9) Canviar emissor");
         System.out.println("\t10) Guardar emissor");
         System.out.println("\t11) Guardar productes");
         System.out.println("\t12) Guardar clients");
         System.out.println("\t13) Guardar factures");
         System.out.println("\t14) Recuperar productes");
         System.out.println("\t15) Sortir");
         System.out.print("La teva opció:");
         int opcio = 0;
         try{
            opcio = sc.nextInt();
         }catch(InputMismatchException ex){
            System.out.print("Entrada no vàlida. No és un número");
         }finally{
            sc.nextLine();
         }
         try{
            switch(opcio){
            case 1: crearNovaFactura();
            break;
            case 2: visualitzarFactures();
            break;
            case 3: cercarFactura();
            break;
            case 4: eliminarFactura();
            break;
            case 5: crearClient();
            break;
            case 6: visualitzarClients();
            break;
            case 7: crearProducte();
            break;
            case 8: visualitzarProductes();
            break;
            case 9: canviarEmissor();
            break;
            case 10: guardarEmissor();
            break;
            case 11:
               System.out.println("[ b ] - Desar com arxiu de bytes");
               System.out.println("[ t ] - Desar com arxiu de text");
               System.out.print("Opcio: ");
               opcioArxiu = sc.nextLine().toLowerCase().charAt(0);
               if (opcioArxiu == 'b')
                  guardarProductes();
               else
                  guardarProductesTXT();
               break;

            case 12: guardarClients();
            break;
            case 13: guardarFactures();
            break;
            case 14:
               System.out.println("[ b ] - Recuperar de l'arxiu de bytes");
               System.out.println("[ t ] - Recuperar de l'arxiu de text");
               System.out.print("Opcio: ");
               opcioArxiu = sc.nextLine().toLowerCase().charAt(0);
               if (opcioArxiu == 'b')
                  recuperarProductes();
               else
                  recuperarProductesTXT();
               break;

            case 15: sortir = true;
            break;
            default:
               System.out.println("Opció no vàlida!");
            }
         }catch(Exception ex){
            System.out.println("S'ha produit un error:");
            System.out.println(ex.getMessage());
         }
      }while(!sortir);
   }


Y ya está, ahora se pueda guardar y recuperar de un archivo de texto plano. Archivo que si lo abrimos con el bloc de notas, ahora si lo puede leer un humano.

Como has visto, este proceso requiere de más pasos, pero bueno, tampoco es muy distinto de la forma anterior.


Revísalo bien y pregunta si algo no lo entiendes. Un saludo.

415
Vamos a aclarar conceptos.

Cuando a la clase Persona, le pones este atributo:

Código: [Seleccionar]
private String karla;
¡¡Ojo cuidaoo!! Eso no implica que a esa Persona le estés asignando el nombre "karla".
Eso lo que implica es que has creado un atributo llamado "karla".

Insisto, karla es el nombre de la variable que declaras como atributo.
No es el nombre de la Persona que queremos representar.

Lo mismo con el otro atributo llamado "juan".

Te devuelve null porque los nombres que "seteas", los introduces en la variable/atributo llamada "nombre", que es la correcta, la única que deberías estar usando y no se por qué has necesitado declararla como Object.

Como decía, "seteas" los nombres, en "nombre". Eso es correcto.
Pero tu método get, el que retorna el valor del nombre, haces que retorne el valor del atributo llamado "juan". Este atributo en ningún momento ha recibido previamente ningún valor, así que su valor es nulo. Por eso devuelve null

Insisto que aunque a los atributos los llames, "juan", "karla" o "pepe"... eso no implica que dentro tengan esos nombres de personas.
Todos ellos comienzan con valor null. Y si no reciben nada por el constructor de la clase, o por sus correspondientes métodos SET, seguirán teniendo valor null.



En definitiva, una clase que modela una Persona, no debe tener atributos bautizados con nombres reales de personas, porque eso no implica que esos vayan a ser los nombres de ese objeto Persona

Los nombres de los atributos, han de hacer referencia a los aspectos que queremos modelar de una determinada entidad.

Por ejemplo, los atributos habituales de una Persona podrían ser nombre, edad, teléfono, peso, género, altura, color de ojos....

A esta clase Persona podemos darle dos constructores (o tres, o cuatro, o cincuenta..., tantos como creamos necesarios).

Uno es un constructor "vacío", que no recibe ningún valor al construir el objeto.
En este caso conviene dar un valor inicial a los atributos, aunque sean cadenas vacías y valor 0 para los datos numéricos, precisamente para evitar valores null como te estaba pasando.

Y otro constructor que sí reciba directamente unos valores para asignárselos a los atributos.

Te dejo un ejemplo con una clase Persona con varios atributos y en el main se crean dos objetos Persona, uno con el constructor SIN parámetros (valores) y otro con el constructor CON parámetros.

Y a continuación se muestran todos los datos de estas Personas, mediante los métodos GET de cada atributo.
Espero que con este ejemplo, entiendas mejor cuál es la función de los atributos, como usarlos y como nombrarlos:

Código: [Seleccionar]
public class Main {

public static void main(String[] args) {

//Creamos una Persona sin dar valores al constructor
Persona persona1 = new Persona();

//Otra Persona, dando valores al constructor
Persona persona2 = new Persona("Juan", 23, "Masculino", "74698762R", "646508723",
"juanito@aprenderaprogramar.com", "Azules", "Castaño", 65.8, 1.78);

//Mostramos valores
System.out.println("Datos de la Persona construida SIN valores:");
System.out.println("Nombre: " + persona1.getNombre());
System.out.println("Edad: " + persona1.getEdad());
System.out.println("Genero: " + persona1.getGenero());
System.out.println("DNI: " + persona1.getDni());
System.out.println("Telefono: " + persona1.getTelefono());
System.out.println("Email: " + persona1.getEmail());
System.out.println("Color de Ojos: " + persona1.getColorOjos());
System.out.println("Color del Pelo: " + persona1.getColorPelo());
System.out.println("Peso: " + persona1.getPeso() + "Kg");
System.out.println("Altura: " + persona1.getAltura() + "m");

System.out.println("\n"); //Salto de línea

System.out.println("Datos de la Persona construida CON valores:");
System.out.println("Nombre: " + persona2.getNombre());
System.out.println("Edad: " + persona2.getEdad());
System.out.println("Genero: " + persona2.getGenero());
System.out.println("DNI: " + persona2.getDni());
System.out.println("Telefono: " + persona2.getTelefono());
System.out.println("Email: " + persona2.getEmail());
System.out.println("Color de Ojos: " + persona2.getColorOjos());
System.out.println("Color del Pelo: " + persona2.getColorPelo());
System.out.println("Peso: " + persona2.getPeso() + "Kg");
System.out.println("Altura: " + persona2.getAltura() + "m");

}

private static class Persona {

private String nombre;
private int edad;
private String genero;
private String dni;
private String telefono;
private String email;
private String colorOjos;
private String colorPelo;
private double peso;
private double altura;

//Constructor sin parámetros, la Persona se construye sin atributos definidos
public Persona() {
//Para evitar valores null, inicializamos los atributos con valores por defecto
nombre = "";
edad = 0;
genero = "";
dni = "";
telefono = "";
email = "";
colorOjos = "";
colorPelo = "";
peso = 0.0;
altura = 0.0;
}


//Constructor que recibirá los datos necesarios para rellenar los atributos
public Persona(String nombrePer, int edadPer, String generoPer, String dniPer,
String telefonoPer, String emailPer, String colorOjosPer,
String colorPeloPer, double pesoPer, double alturaPer) {
nombre = nombrePer;
edad = edadPer;
genero = generoPer;
dni = dniPer;
telefono = telefonoPer;
email = emailPer;
colorOjos = colorOjosPer;
colorPelo = colorPeloPer;
peso = pesoPer;
altura = alturaPer;
}


public String getNombre() {
return nombre;
}


public void setNombre(String nombrePer) {
nombre = nombrePer;
}


public int getEdad() {
return edad;
}


public void setEdad(int edadPer) {
edad = edadPer;
}


public String getGenero() {
return genero;
}


public void setGenero(String generoPer) {
genero = generoPer;
}


public String getDni() {
return dni;
}


public void setDni(String dniPer) {
dni = dniPer;
}


public String getTelefono() {
return telefono;
}


public void setTelefono(String telefonoPer) {
telefono = telefonoPer;
}


public String getEmail() {
return email;
}


public void setEmail(String emailPer) {
email = emailPer;
}


public String getColorOjos() {
return colorOjos;
}


public void setColorOjos(String colorOjosPer) {
colorOjos = colorOjosPer;
}


public String getColorPelo() {
return colorPelo;
}


public void setColorPelo(String colorPeloPer) {
colorPelo = colorPeloPer;
}


public double getPeso() {
return peso;
}


public void setPeso(double pesoPer) {
peso = pesoPer;
}


public double getAltura() {
return altura;
}


public void setAltura(double alturaPer) {
altura = alturaPer;
}

}

}

416
- Cuando el programa no reconoce una palabra (veo los cursos y veo que por defecto a ell@s sí se las reconocen), y no sé si es que tengo algo mal configurado (está en inglés, y me cuesta un poco), pero me marca error.

Sobre esto, habría que ver un caso concreto para ver de que se trata.

Algunas instrucciones Java, para poder usarlas, hay que "importar" la clase a la que pertenecen.
Pero si estás comenzando por lo básico, no creo que te estés encontrando todavía con instrucciones que requieran ser importadas.
Aunque no tardarás mucho, pues importar clases es lo más habitual.

De todos modos, cuando esto ocurre, los IDE's como IntelliJ son capaces de añadir por sí solos la línea de código necesaria para importar las clases necesarias.


Pero como digo, habría que ver caso por caso, para ver cuál es el problema real.

Un saludo

417
Lo que ocurre es que le estás pidiendo al System.out.println() que te imprima en pantalla el objeto persona (por cierto, por convención, los nombres de las clases deberían empezar con mayúscula: Persona)

Pero tu clase Persona, no tiene ningún atributo. Al construirlo le estás dando un nombre, pero ese nombre no lo estás recogiendo, así que aunque se lo des, este no se guarda ni se hace nada con él.

Escribe la clase de la siguiente forma.
- Dándole un atributo (una variable) para guardar el nombre. Como un nombre es un texto, usaremos la clase String para este atributo.

- El nombre que se recibe por el constructor (a través de otro String), lo guardamos en ese atributo.

- Para luego poder obtener ese nombre y mostrarlo en pantalla, se usa un método GET.
Un método GET lo que hace es retornar el valor de un atributo. Si el atributo es un String, hay que declarar este método también como String.
Este valor que pedimos retornar con la sentencia return, será lo que recogerá la instrucción System.out.println() y lo mostrará en pantalla.

- Para poder modificar el nombre una vez creado el objeto Persona, se un método SET.
Un método set lo que hace es recibir un nuevo valor y asignárselo al atributo correspondiente.
Este método NO RETORNA NADA, por tanto, hay que declararlo como void, que significa "nada", es decir, no hay un valor de retorno.

TE dejo un ejemplo con la clase y un código de prueba:
Código: [Seleccionar]
public class Main {

public static void main(String[] args) {

//Creamos una Persona
Persona pers1 = new Persona("Kabuto");
//Mostramos el nombre, solicitándolo a su método GET
System.out.println(pers1.getNombre());

//Cambiamos el nombre por otro, mediante el método SET
pers1.setNombre("Aprendiz89");
//Volvemos a mostrar el nombre cambiado
System.out.println("-- Nombre ha sido modificado --");
System.out.println(pers1.getNombre());


}

private static class Persona {

private String nombre; //Atributo, un String para guarda cadena texto

public Persona(String nombrePersona) {

nombre = nombrePersona; //El valor recibido en el constructor, lo pasamos al atributo
}

/*
* A continuación métodos get y set.
* Los métodos get sirven para obtener el valor de un atributo
* Los métodos set sirven para modificar el valor de un atributo
*/
public String getNombre() {
return nombre; //Retornamos el valor del atributo nombre
}

public void setNombre(String nuevoNombre) {
nombre = nuevoNombre; //Modificamos el nombre actual con otro
}
}

}

418
Comunidad / Re: Un gusto saludarlos soy Archirex
« en: 18 de Mayo 2021, 22:43 »
Bienvenido.

Espero que encuentres aquí lo que buscas. Cualquier duda que te vaya surgiendo en tu aprendizaje, tienes este foro a tu disposición para compartirla.

Un saludo.

419
Hola.

Toda aplicación Swing debería tener SOLO UN JFrame.

Solo uno. Dentro de este marco, usando los Layout adecuados, se pueden ir mostrando los paneles que requiera nuestra aplicación.

En el caso que propones, usar un CardLayout parece lo más adecuado.
Este layout lo que hace es contener varios paneles JPanel en uno, de manera que solo se muestra el que "está encima" de los otros.
Luego ya con botones o lo que sea, podemos decidir cuál es el panel que se ha de poner "encima" del resto en cada momento.

Para facilitar esto, lo ideal sería que tus clases PanelBiblioteca y PanelUsuario no hereden de JFrame, sino de JPanel.
Es decir, que realmente sean paneles y no marcos JFrame.

Hago un ejemplo muy simplificado, para que puedas probarlo y mirar de adaptarlo a tu programa.
Esto es PanelBiblioteca:

Código: [Seleccionar]
public class PanelBiblioteca extends JPanel{

public PanelBiblioteca() {

add(new JLabel("Este es el panel BIBLIOTECA"));

}

}

Y esto PanelUsuario:
Código: [Seleccionar]
public class PanelUsuario extends JPanel{

public PanelUsuario() {

add(new JLabel("Este es el panel USUARIO"));
}

}

A continuación pondré la clase principal, que es la única que va a heredar de JFrame.

Tendrá como atributos los dos paneles anteriores y un tercer JPanel que será el "principal", es decir, será quien contenga los otros dos paneles.
Le asignaremos el CardLayout, para que el panel "principal" sepa que la forma de gestionar esos dos paneles será mostrar solo uno de ellos, el otro quedará oculto hasta que se de la orden de intercambiarlos.

Estas ordenes las daremos con dos botones, anterior y siguiente, a quienes les añadiremos un ActionListener que se dedicará específicamente a decirle al CardLayout que panel se ha de mostrar, según el botón que se pulse.

En lo que respecta al JFrame, le vamos a dar un BorderLayout.
Este layout lo que hace es dividir el marco en 5 secciones. Una grande en el centro (CENTER) y 4 más pequeñas alrededor del centro (NORTH, SOUTH, EAST, WEST).
En cada sección podemos poner paneles.

Así, en el CENTER pondremos el panel "principal", el que contiene los paneles "biblioteca" y "usuario".
En el SOUTH, pondremos otro panel con los dos botones de "anterior" y "siguiente"

Código: [Seleccionar]
public class EjemploCard extends JFrame{

private PanelBiblioteca pnBiblioteca;
private PanelUsuario pnUsuario;
private JPanel pnPrincipal;
private CardLayout gestorPaneles;
private JButton anterior;
private JButton siguiente;

public EjemploCard() {

//Inicializacion paneles
pnBiblioteca = new PanelBiblioteca();
pnUsuario = new PanelUsuario();
pnPrincipal = new JPanel();
gestorPaneles = new CardLayout();
//Asignamos gestor al panel principal
pnPrincipal.setLayout(gestorPaneles);
//Le añadimos los otros paneles, dándoles un nombre identificador
pnPrincipal.add(pnBiblioteca, "biblioteca");
pnPrincipal.add(pnUsuario, "usuario");

//Inicialización botones
anterior = new JButton("Anterior");
anterior.setEnabled(false);
siguiente = new JButton("Siguiente");
//Accion para los botones
anterior.addActionListener(new AccionAlternaPanel());
siguiente.addActionListener(new AccionAlternaPanel());
//Panel para botones
JPanel pnBotones = new JPanel();
pnBotones.setLayout(new FlowLayout(FlowLayout.CENTER, 50, 10));
pnBotones.add(anterior);
pnBotones.add(siguiente);
pnBotones.setBorder(BorderFactory.createRaisedSoftBevelBorder());

//Configuracion JFrame
setLayout(new BorderLayout());
add(pnPrincipal, BorderLayout.CENTER);
add(pnBotones, BorderLayout.SOUTH);

setTitle("Ejemplo CardLayout");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400, 300);
setLocationRelativeTo(null);
setVisible(true);

}

//Clase ActionListener para los botones que alternan los paneles
private class AccionAlternaPanel implements ActionListener {
@Override
public void actionPerformed(ActionEvent e) {
JButton boton = (JButton) e.getSource();
//Según el botón, pedimos al CardLayout que muestre un panel u otro
if (boton.equals(anterior)) {
gestorPaneles.show(pnPrincipal, "biblioteca");
anterior.setEnabled(false);
siguiente.setEnabled(true);
}
else { //Boton siguiente
gestorPaneles.show(pnPrincipal, "usuario");
anterior.setEnabled(true);
siguiente.setEnabled(false);
}
}
}

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
@Override
public void run() {
new EjemploCard();
}
});
}

}

Si pruebas este código verás, que con solo un JFrame, se puede alternar entre dos paneles. De hecho, podríamos alternar con tantos paneles como necesitásemos.

420
Vale. Está muy bien estructurado el programa, así que va a ser fácil hacer lo que se pide.

Vamos a ver por ejemplo, como guardar los Productos.

Lo primero es implementar la interfaz Serializable a la clase Producte.
Una vez implementada, es probable que el IDE lance un warning pidiendo que también agreguemos un "serial" identificador e incluso nos ofrecerá hacerlo el automáticamente.
Puedes ponérselo, aunque en este caso es algo irrelevante, así que yo normalmente no lo pongo y para que el warning desaparezca uso la etiqueta @SupressWarnings() para indicarle al compilador que no me lance ninguna advertencia por ese motivo.

Código: [Seleccionar]
@SuppressWarnings("serial")
public class Producte implements Serializable{

Y ya está, eso es lo único que hay que cambiar en la clase Producte.

Ahora nos vamos al programa principal.
Ahí ya tienes declarado un método para guardar productos, ahora vamos a definirlo.
Lo que vamos a hacer consiste en transformar el array de productos, que está en memoria RAM, en un archivo de bytes (serializar) para poder guardarlo en disco.

Para esto se usa la clase ObjectOutputStream, quien a su vez requiere de un objeto FileOuputStream, quien a su vez nos va a pedir un objeto File (un fichero).

La lógica a seguir es crear primero el File, dándole el nombre con el que queremos que se guarde en disco. Podemos indicarle solo nombre, o una ruta completa.
Si solo damos nombre, se guardará en la carpeta de proyecto de este programa.

Acto seguido, preguntamos si NO existe, en cuyo caso lo crearemos.
Este acto de creación podría fallar, por ejemplo porque intentamos escribir en una ruta donde el S.O. no nos da permisos de escritura.
Con try catch capturamos esta posible excepción y si ocurre, anunciamos al usuario que no es posible guardar datos.

Si todo sale bien, pasamos a crear el ObjectOutputStream, de quién invocaremos el método writeObject() y le pasaremos el array que queremos guardar en disco, el de productos en este caso.
Todo este proceso también va a requerir de try catch por las posibles excepciones que puedan ocurrir.

Y ya está, tras esos pasos, si todo ha ido bien tendremos un archivo en disco con los datos del array en forma de bytes.

El método quedaría así:

Código: [Seleccionar]
  private static void guardarProductes() {
   
    File fitxerProductes = new File("productes.bin");
   
    if (!fitxerProductes.exists()) {
try {
fitxerProductes.createNewFile();
} catch (IOException e) {
System.out.println("\n-- ERROR. No s'ha pogut crear: " + fitxerProductes.getAbsolutePath());
return; //Posem fi a l'operació. No es pot guardar en aquesta ubicació.
}
    }
   
    try {
ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(fitxerProductes));
oos.writeObject(productes); //Guardem array productes
oos.close();
System.out.println("\nProductes guardats correctament\n");
} catch (FileNotFoundException e) {
System.out.println("\n-- ERROR. No es troba fitxer: " + fitxerProductes.getAbsolutePath());
} catch (IOException e) {
System.out.println("\n-- ERROR. No es pot accedir a fitxer: " + fitxerProductes.getAbsolutePath());
}
    }

Bien, podemos guardar productos.
Ahora vamos a querer recuperar esa copia guardada cuando queramos.

Para esto usamos la clase ObjectInputStream, junto con FileInputStream.
Hacen lo contrario de la anterior:
- Output --> salida/escritura
- Input--> entrada/lectura

De nuevo comenzamos creando un objeto File y preguntamos si existe o no.
Si no existe, pues entonces no hay nada que recuperar, así que lo anunciamos y se acabó.

Si existe, entonces creamos el ObjectInputStream e invocamos el método readObject().

Este método lo que hace es leer los bytes del objeto File al que está asociado y los retorna en forma de Object.

Este Object se lo vamos a asignar al array de productos, pero para que lo acepte será necesario hacer un casting a Producte[].

Si todo va bien, ya tendremos recuperado el array de productos. Aunque aún queda una cosa pendiente. El programa está usando un "contador de productos" para saber cuantos productos hay registrados, y en que posición del array debe ir registrando los nuevos productos, etc...

Tras recuperar el array que estaba guardado en disco, este contador necesita ser actualizado, pues su valor ya no se corresponde con el nuevo array.
Si por ejemplo nada más comenzar el programa recuperamos un array que tiene 20 productos, el contador en cambio nos va a decir que tiene 0 productos.

Esto hay que solucionarlo, y para ello he creado un segundo método que lo que hace es recibir un array de cualquier clase (así se puede usar también para los array de facturas, clientes..) y lo recorrer hasta encontrar la primera posición con valor null. Y retorna ese número de posición, que ha de ser el que le asignemos al contador para que tenga un valor correcto.

Este es el método para recuperar los datos guardados en disco:

Código: [Seleccionar]
    private static void recuperarProductes() {
   
    File fitxerProductes = new File("productes.bin");
   
    if (!fitxerProductes.exists())
    System.out.println("No hi ha productes guardats per recuperar.");
    else {
    try {
ObjectInputStream ois = new ObjectInputStream(new FileInputStream(fitxerProductes));
//Recuperem array productes
productes = (Producte[]) ois.readObject(); //Fem casting de Object a Producte[]
//Actualitzem comptador de productes
comptadorDeProductes = calculaComptador(productes);
ois.close();
System.out.println("\nProductes recuperats correctament\n");
} catch (FileNotFoundException e) {
System.out.println("\n-- ERROR. No es troba fitxer: " + fitxerProductes.getAbsolutePath());
} catch (IOException e) {
System.out.println("\n-- ERROR. No es pot accedir a fitxer: " + fitxerProductes.getAbsolutePath());
} catch (ClassNotFoundException e) {
System.out.println("\n-- ERROR. Les dades no es corresponen amb dades de Producte");
}
    }
   
    }

Y este es el método que acepta arrays de cualquier clase (por eso el argumento lo recoge como Object[] ) y retorna el valor correcto para los contadores:
Código: [Seleccionar]
    private static int calculaComptador(Object[] array) {
    int comptador = 0;
   
    for (int i = 0; i < array.length; i++)
    if (array[i] == null) { //Busquem la primera posició buida per determinar el comptador.
    comptador = i;
    break;
    }
   
    return comptador;
    }

Y listo, ya puedes probar a guardar y recuperar productos. En el menú, eso sí, tendrás que añadir nuevas opciones para recuperar y actualizar el switch

Citar
    public static void menu(){
        Scanner sc = new Scanner(System.in);
        boolean sortir = false;
        do{
            System.out.println("***** Gestió de factures *****");
            System.out.println("Dades emissor: " + Emissor.getString());
            System.out.println();
            System.out.println("--- Menú d'usuari ---");
            System.out.println("\t1) Crear nova factura");
            System.out.println("\t2) Visualitzar totes les factures");
            System.out.println("\t3) Cercar factura");
            System.out.println("\t4) Eliminar factura");
            System.out.println("\t5) Crear client");
            System.out.println("\t6) Visualitzar client");
            System.out.println("\t7) Crear producte");
            System.out.println("\t8) Visualitzar producte");
            System.out.println("\t9) Canviar emissor");
            System.out.println("\t10) Guardar emissor");
            System.out.println("\t11) Guardar productes");
            System.out.println("\t12) Guardar clients");
            System.out.println("\t13) Guardar factures");
            System.out.println("\t14) Recuperar productes");
            System.out.println("\t15) Sortir");
            System.out.print("La teva opció:");
            int opcio = 0;
            try{
                opcio = sc.nextInt();
            }catch(InputMismatchException ex){
                System.out.print("Entrada no vàlida. No és un número");
            }finally{
                sc.nextLine();
            }
            try{
                switch(opcio){
                    case 1: crearNovaFactura();
                            break;
                    case 2: visualitzarFactures();
                            break;
                    case 3: cercarFactura();
                            break;
                    case 4: eliminarFactura();
                            break;
                    case 5: crearClient();
                            break;
                    case 6: visualitzarClients();
                            break;
                    case 7: crearProducte();
                            break;
                    case 8: visualitzarProductes();
                            break;
                    case 9: canviarEmissor();
                            break;
                    case 10: guardarEmissor();
                            break;
                    case 11: guardarProductes();
                            break;
                    case 12: guardarClients();
                            break;
                    case 13: guardarFactures();
                            break;
                    case 14:
                       recuperarProductes();
                       break;
                    case 15: sortir = true;
                            break;

                    default:
                        System.out.println("Opció no vàlida!");
                }
            }catch(Exception ex){
                System.out.println("S'ha produit un error:");
                System.out.println(ex.getMessage());
            }
        }while(!sortir); 
    }



Siguiendo este ejemplo, tú mismo puedes escribir los restantes métodos para guardar clientes y facturas. Recuerda implementarles la interfaz Serializable a sus correspondientes clases.


Lo que no tengo claro es lo del Emisor, ¿también se ha de guardar? Pero no tienes ningún array con emisores.
Puedes guardarlo, ObjectOutputStream guardará cualquier objeto que le indiques (si implementa Serializable).
Pero como no usas ningún array, tu programa solo trabaja con un único Emisor (se puede cambiar sus atributos, pero siempre es uno) pues eso es lo que se guardará.

No tiene mucho sentido guardar simplemente un Emisor, revisa el enunciado de tu ejercicio a ver si realmente hay que guardarlo, o si bien lo que pasa es que está faltando un array de Emisores.

Pregunta cualquier duda que surja.
Un saludo.

Páginas: 1 ... 16 17 18 19 20 [21] 22 23 24 25 26 ... 50

Sobre la educación, sólo puedo decir que es el tema más importante en el que nosotros, como pueblo, debemos involucrarnos.

Abraham Lincoln (1808-1865) Presidente estadounidense.

aprenderaprogramar.com: Desde 2006 comprometidos con la didáctica y divulgación de la programación

Preguntas y respuestas

¿Cómo establecer o cambiar la imagen asociada (avatar) de usuario?
  1. Inicia sesión con tu nombre de usuario y contraseña.
  2. Pulsa en perfil --> perfil del foro
  3. Elige la imagen personalizada que quieras usar. Puedes escogerla de una galería de imágenes o subirla desde tu ordenador.
  4. En la parte final de la página pulsa el botón "cambiar perfil".