miércoles, 12 de octubre de 2011

Shell y AG's

Estos días estamos programando un Shell en una asignatura de la Universidad que de momento parece que está marchando bien. El lenguaje no es el habitual de las publicaciones que he ido haciendo, si no que tenemos que hacerlo en C (a base de punteros señales interrupciones POSIX...). Para cuando esté terminado colgaré alguna parte del código que vea que es bastante más interesante que el resto y suponiendo que la práctica vaya bien. Por otro lado estamos también programando algoritmos genéticos en C++, para dentro de muy poco tenemos que entregar la primera práctica, quizás también cuelgue algo por aquí que sea instructivo, porque la verdad que es un tema bastante interesante. ¡¡Próxima publicación dentro de poco!!

viernes, 16 de septiembre de 2011

Archivos en Java y revertir un número

Últimamente he tocado poco Eclipse ya que he tenido un examen y me ha tenido bastante ocupado. Aun así, el foro en el que suelo comentar lo he seguido visitando y, a pesar de que no es algo extremadamente interesante, me vale para hacer una muy breve entrada y dejar algo por aquí que sea nuevo.

Por un lado alguien preguntó sobre comprobar si una cadena de texto era sufijo de otra, pero de forma recursiva. La verdad que es algo muy simple de comprobar ya que apenas tiene dificultad. El código es el siguiente:

public boolean sufijo(String c1, String c2){
	if(c1.length() == 0)
		return true;
	int aux = c1.length();
	if(c1.charAt(c1.length()-1) == c2.charAt(c2.length()-1))
		return sufijo(c1.substring(0, c1.length()-1), c2.substring(0, c2.length()-1));
	else
		return false;
}


Con este método, que devuelve true o false, conseguimos lo que queríamos realizar. La función lo que hace es comprobar que la cadena c1 es sufijo de c2. El caso base es bastante claro, sí la longitud de c1 es 0 es un sufijo (ya que sería una cadena vacía y teóricamente una cadena vacía es sufijo de cualquier cadena. Sí el tamaño no es 0 comprobamos que el último carácter de cada canea sea igual, sí es así, entonces hacemos return con lo que nos devuelva la función eliminando el último carácter puesto que ya hemos comprobado que son iguales. En caso de que no sean iguales es porque no es un sufijo y por tanto devolvemos false. Las llamadas recursivas terminarán en algún momento, puesto que en cada nueva llamada al método eliminamos un carácter a cada cadena.

Otra persona del foro colgó un trozo de código para hacer uso de archivos, más concretamente crear un archivo vacío con formato .txt e ingresar datos en su interior. Tras leer el código que propuso él le comenté que quizás sería mejor el código con el que respondí:

Crear un archivo vacío
File archivo2 = new File("rutaYNombreArchivo" + ".txt");
	    try {
			if(archivo2.createNewFile()){
                                 System.out.println("Archivo creado!");
                        }
		} catch (IOException e) {
			System.out.println("Imposible crear archivo");
		}


De esta forma tendríamos un archivo vacío creado en la ruta especificada en el constructor del objeto File. Creo que es mejor dejar la ruta de creación del objeto parametrizada, es decir, pasarle un parámetro con la ruta completa hasta el archivo a crear o leer, ya que si por ejemplo estableciesemos a la fuerza que la ruta inicial comenzase en D:\\ podríamos tener errores sí una persona que ejecutase nuestro código no tuviese dicho directorio.

El código no podría ser más sencillo, simplemente declaramos un objeto de tipo File pasandole como parámetro la ruta completa, posteriormente llamamos al método de la clase File createNewFile() el cual nos crea un archivo nuevo con el nombre especificado en la ruta. Es decir, si nuestra ruta fuese C:\\Users\\Usuario\\Documents\\Carpeta\\archivoNuevo.txt crearía en la carpeta "Carpeta" un archivo con el nombre "archivoNuevo.txt".

He de decir que probé intentar crear un archivo vacío en el directorio raíz C:\\ y me lanzó una excepción, supongo que al usar Windows 7 necesita privilegios de Administrador.

Por otro lado, a la hora de ingresar datos en el archivo creado sería tan sencillo como esto:
String linea;
FileReader lector = new FileReader(archivo);
BufferedReader br = new BufferedReader(lector);
FileWriter escritor = new FileWriter(archivo);
BufferedWriter ayudaEscritor= new BufferedWriter(escritor);

while((linea = br.readLine()) != null){
       ayudaEscritor.write(linea);
       ayudaEscritor.newLine();
}

for(int a=0;a<=datos.length-1;a++){
       ayudaEscritor.write(datos[a]); //datos es una variable de tipo String[].
       ayudaEscritor.newLine();
}
ayudaEscritor.close();


Este es un código ejemplo, ya que sería mucho más sencillo escribir los datos si por ejemplo fuese un StringBuffer o un String directamente y no necesitaríamos un bucle for.

Por último un método que sirve para eliminar un fichero (suponiendo que tengamos permisos del sistema suficientes para hacerlo), crearlo de nuevo e introducir en el datos.
public boolean guardar(String nombreTabla, String rutaArchivo){
       File archivo= new File (rutaArchivo);
	       try{
	       if(archivo.exists()){
	              if(archivo.delete()){
	                     if(archivo.createNewFile()){
	                            FileWriter escritor = new FileWriter(archivo);
	                            BufferedWriter auxescritor = new BufferedWriter(escritor);
	                            auxescritor.write("Esto es una prueba!!");
	                            auxescritor.close();
                                    return true;
	                     }
	                     else
	                            System.out.println("Imposible crear archivo vacío");
	              }
	              else
	                     System.out.println("Imposible borrar archivo");
	       }
	       else
	              System.out.println("El archivo no existe");
               return false;
	}catch(IOException e){
               System.out.println("Ha ocurrido una excepción");
               return false;
	}
}


Con este código primero crearíamos un objeto de tipo File con la ruta completa del archivo, antes de realizar la llamada a este método deberíamos comprobar que el parámetro que le pasamos sea algo con extensión .txt. En primer lugar comprobamos que el archivo exista (podríamos cambiar un poco la función para que, en caso de que no existiese, crease directamente el archivo, este cambio es muy simple) en caso de existir, lo eliminamos, si la eliminación ha sido correcta entonces creamos un archivo nuevo vacío, y procedemos a insertar los datos.

Por último, un miembro del foro propuso un "reto" por así decirlo en C++, era bastante sencillo. Os dejo el enunciado y la resolución.

Dado un número 0 <= n <= 10100 por la entrada estándar (stdin), se debe mostrar por la salida estándar (stdout) su número girado, tal que si los dígitos de n son a1, ..., an, los de su número girado serán an, ..., a1 (es la misma definición, formalizada un poco). Tened en cuenta por ejemplo que el girado de 100 es 001 y no 1.

Restricciones: No se pueden utilizar arrays, structs, strings, clases ni nada similar, tan sólo los tipos de variables básicos de C/C++, es decir int, char, long long, etc.


La solución es la siguiente:
public static void procedimiento() throws IOException{
	int caracter = System.in.read();
	if(Character.isDigit((char)caracter)){
		procedimiento();
		System.out.print((char)caracter);
	}
}
	 
public static void main(String[] args) throws IOException {
	procedimiento();
	System.out.println();
}


De esta forma podríamos realizar el inverso de un número de forma muy sencilla. La única pega que veo al método es si el número fuese extremadamente grande (y con extremadamente me refiero a un número enorme enorme enorme) pues podría hacer un overflow en la pila con tanta llamada recursiva.

Bueno, hasta aquí la publicación de hoy, espero que a alguien le sea interesante o de utilidad.

Saludos.

viernes, 5 de agosto de 2011

Eliminación de espacios de sobra en cadenas de texto

Este tema es algo simple, pero dado que existen algunas alternativas, y es algo interesante a la hora de realizar trabajos con cadenas de texto, expongo aquí métodos en Java para que, en una cadena de caracteres, solo exista un espacio entre cada palabra y la cadena empiece y termine por una letra.

Una de las primeras formas que se nos podría ocurrir, sería la siguiente:

String cadena = new String("   Hola   esto es   una prueba      ");
cadena = cadena.trim();
String[] nueva = cadena.split(" +");

De esta forma, tendríamos un array de cadenas que corresponderían a la frase original, y, a la hora de reconstruir la frase podríamos realizarlo de la siguiente manera:

StringBuffer frase = new StringBuffer();
frase.append(nueva[0]);
for(int i = 1;i < nueva.length;i++)
    frase.append(" " + nueva[i]);

Lo que estamos realizando en este código es: en primer lugar llamamos al método trim() de la clase String, que elimina espacios por delante y por detrás de la cadena en caso de que los haya. Posteriormente llamamos al método split() y le pasamos como parámetro una expresión regular, en este caso " +" que es una expresión regular que reconoce las cadenas que poseen uno o más espacios. A continuación recorremos el array devuleto por split y vamos concatenando en un StringBuffer (se podría realizar concatenando en una variable String con el operador +, pero por temas de eficiencia es preferible y muy recomendable realizarlo con StringBuffer).

De esta forma, en la variable frase, que es de tipo StringBuffer, tendríamos la cadena inicial, pero sin espacios al principio ni al final y además con un único espacio entre fases.

Este método es correcto y realiza su función, pero hemos tenido que escribir 6 líneas de código para realizarlo además de declarar un stringbuffer y parece que hemos tenído que darle demasiadas vueltas.


Una segunda forma de realizar este cometido podría ser la siguiente:

String texto = new String("    Hola    esto   es una prueba         de eliminar espacios  ");
texto = texto.replaceAll(" +", " ");
texto = texto.trim();

Lo que hacemos es reemplazar con el método replaceAll() todas las cadenas especificadas por el primer parámetro, que es una expresión regular, y las sustituimos por el segundo parámetro, es decir, reemplazamos todos los espacios por un único espacio. Por ejemplo, sí tenemos: "El         jamón     ", tras aplicar este método nos quedaría lo siguiente: "El jamón ". Por tanto, a continuación tan solo deberíamos aplicar el método trim() y conseguiríamos lo que andábamos buscando.

En este caso, tenemos tan solo dos líneas de código (no cuento la declaración del objeto) y de nuevo realiza correctamente su cometido. Incluso de esta manera sería más eficiente que la propuesta anteriormente.

Una última forma podría ser empleando la clase StringTokenizer, y sería de la siguiente manera:

String texto = new String("    Hola esto   es  una prueba      de     eliminar      espacios                    ");
StringTokenizer tokenizer = new StringTokenizer(texto, " ");
StringBuffer frase = new StringBuffer();
if(tokenizer.hasMoreTokens()){
    frase.append(tokenizer.nextToken());
    while(tokenizer.hasMoreTokens())
        frase.append(" " + tokenizer.nextToken());
}

De nuevo en la variable frase, tendríamos el contenido.

Aquí estamos declarando una variable de tipo StringTokenizer y le pasamos como parámetro al constructor el texto y el delimitador de cada token. Por último, si queremos reconstruir la frase hacemos lo especificado arriba y la conseguiríamos.

Este método no me agrada tanto como el anterior, ya que hay que realizar muchas más comprobaciones (la del if se ejecutará siempre y las del bucle while tantas como tokens hayan a partir del primero) además de que se realizan muchas más llamadas a métodos que en el anterior caso.

No puedo decir que una sea más eficiente que la otra ya que debería saber el coste de cada método, pero por intuición (cosa que puede estar equivocada) diría que el 2º método sería el más eficiente.

Espero que sea de utilidad a todo aquel que venga a parar aquí.

Saludos y suerte.

viernes, 1 de julio de 2011

Cambiar icono JFrame y JDialog.

Hola y buenos días para las personas que vengan a parar aquí.

El motivo de esta entrada es explicar un poco como cambiar el icono de las ventanas JFrame y JDialog, ya que sinceramente, me parece algo lioso para quién no lo sepa, y que muchas de las cosas que leí por internet solamente me funcionó una de ellas.

Para las clases que programemos que hereden de JFrame, es bastante sencillo realizar el cambio de icono.

En primer lugar vamos a redefinir el método getIconImage(), heredado de la clase JFrame. El código que insertaremos en este método será el siguiente:


public Image getIconImage() {
    Image retValue = Toolkit.getDefaultToolkit().
    getImage(ClassLoader.getSystemResource("resources/Cadenas.png"));

    return retValue;
}

Donde "resources/Cadenas.png" es la ruta donde se encuentra el icono que queremos establecer para nuestra ventana. El formato debe ser PNG, ya que en otros formatos da ciertos problemas.

También es recomendable que nuestros iconos se encuentren en un paquete de nuestro proyecto. En mi caso cree una carpeta llamada resources y dentro de ella metí la imagen del icono.

Una vez realizado esto, en el constructor de nuestra clase pondremos la siguiente instrucción:

setIconImage(getIconImage());

De esta forma cambiaremos el icono por el que indicamos en el método redefinido.

Ya tenemos nuestro JFrame con nuestro icono personalizado.

En los JDialog la cosa cambia, ya que por si solos ellos no poseen un icono. Para poder establecer un icono para los JDialog, debemos crear un JFrame, asignarle a este JFrame un icono (como hemos visto anteriormente) y, a la hora de crear nuestro JDialog, pasarle como padre el JFrame modificado. De esta forma tendremos nuestro JDialog con icono.

lunes, 20 de junio de 2011

La primera entrada.

Aquí da comienzo un blog donde iré colgando algunas mini-aplicaciones creadas por mí. La mayoría estarán hechas en Java, así que para quién le guste algún programilla que cuelgue, y además necesite o le interese la portabilidad, le vendrá genial.

Sin más rodeos un pequeño programa que he terminado hoy mismo. Es bastante simple aunque al menos tiene una pequeña interfaz gráfica. El programa realiza lo siguiente: Coge dos ficheros, uno de origen y otro de destino. El fichero de origen contendrá nombres, o palabras cada nombre o cada frase en una linea. Se irá al segundo fichero y buscará ese nombre o frase o número o lo que sea, y lo sustituirá por lo que nosotros queramos. Además tiene la opción de sustituirlos todas las coincidencias de una vez, o ir de una en una.

Por ejemplo si tenemos estos dos ficheros:

FICHERO 1                                                            FICHERO 2
-------------                                                            -------------

Pascual Antonio López                                             Perico de los palotes
29-10-1990                                                             Santiago Segura
Hola esto es una prueba                                            Pascual Antonio López
Fuckencio Segura                                                     29*10+5 = 295
29*10+5 = 295

recorrería todo el fichero 1 leyendo cada linea, encontraría en el fichero 2 "Pascual Antonio López" y la expresión aritmética y las sustituiría por lo que quisiésemos.

El programa no estará exento de fallos seguramente. Sí alguien lo descarga, prueba y desea hacer cualquier comentario acerca de alguna mejora o fallo, encantado.

Sustitución de cadenas:

Sustitucion de cadenas en Java


VERSIÓN NUEVA CON ALGUNAS MEJORAS:


Sustitución de cadenas en Java Versión actualizada

Esta versión incluye la opción de poder seleccionar varios archivos destino al mismo tiempo (manteniendo pulsado control y pinchando en los distintos ficheros), además de poder seleccionar una carpeta contenedora donde se encuentren los distintos ficheros a analizar.

Además, a la hora de hacer reemplazos de uno en uno existe la posibilidad de dejar la cadena tal cual está.

También se ha cambiado el icono de la aplicación y ahora se muestra en la barra de herramientas.

VERSIÓN ACTUALIZADA Y FINAL:


Sustitución de cadenas en Java versión final

Esta versión incluye una ventana de ayuda con especificaciones generales del programa. Además se han resuelto algunos problemas de las versiones anteriores y añadido nuevas funcionalidades.

Dos capturas de pantalla del programa en ejecución:

Ventana principal

Ejemplo ejecución