Selección de texto en bloques o columnas

Hay varios programas que nos permiten alternar entre el tradicional modo de selección por líneas y el modo de selección por bloques o columnas.

Intentaré ir completando este post con los programas que me parezcan relevante destacar, y la forma que utilizarlo.

Eclipse

Desde la versión 3.5 de Eclipse (Galileo) y en la 3.6 (Helios), 3.7 (Indigo) y posteriores contamos con la posibilidad de cambiar el modo de selección, de líneas a bloques.

Para activarlo, podemos utilizar el botón de modo de selección, o utilizar la combinación de teclas Alt + Shift + A Eclipse.

Eclipse - selección por bloques

Eclipse – selección por bloques

Me resultó curioso que el tipo de letra utilizado cambia cuando habilitamos este modo de selección, pasando a Courier New a pesar que el tipo de letra que estaba utlizando era monoespaciado. En Linux no pasó.

Notepad++

Notepad++ es un editor de texto simple y poderoso. Podemos seleccionar bloques en lugar de líneas apretando la tecla Shift junto al botón izquierdo del mouse. Si estamos utilizando el teclado, apretando juntas las teclas Alt + Shift y las teclas de cursor.

Notepad++ - selección por bloques

Notepad++ – selección por bloques

Otros

Si algún lector sabe de algún editor que valga la pena agregar algún otro editor, ¡que agregue un comentario! 🙂

SSH sin contraseñas (y seguro)

Es posible establecer conexiones SSH a servidores Linux sin necesidad de ingresar una contraseña cada vez. Para esto debemos:

  • Crear nuestro par de claves pública y privada
  • Copiar nuestra clave pública en el servidor SSH
  • Utilizar la clave privada al establecer la conexión SSH

Dependiendo si estamos utilizando Windows o Linux, tendremos varias formas de llevar a cabo estos pasos.

Creación de las llaves

Para comenzar a utilizar claves pública/privadas desde nuestro cliente SSH, debemos generarlas antes.

Yo las creé utilizando el algoritmo RSA en lugar de DSA, principalmente por el tamaño de la clave, a pesar que DSA es el estándar del gobierno federal de los Estados Unidos. Pero de todas formas, ambos son ampliamente aceptados y por lo que leí,
no hay demasiadas ventajas entre uno o el otro. En este link hay un poco de información interesante y actual.

En Linux

En Linux las generamos utilizando el comando ssh-keygen. Utilizaremos la mayoría de las opciones por defecto, pero se puede consultar las páginas man del comando por más información.

Para generar el par de claves RSA utilizamos el comando:

<br />
ssh-keygen -t rsa<br />

Nos pide ingresar algunas opciones, que podemos dejar todo por defecto (notar que para conectarse sin ingresar claves, no se debe ingresar passphrase).
La ejecución va a tener una salida similar a la siguiente:

<br />
[~]# ssh-keygen -t rsa<br />
Generating public/private rsa key pair.<br />
Enter file in which to save the key (/root/.ssh/id_rsa):<br />
Created directory '/root/.ssh'.<br />
Enter passphrase (empty<br />
for no passphrase):<br />
Enter same passphrase again:<br />
Your identification has been saved in /root/.ssh/id_rsa.<br />
Your public key has been saved in /root/.ssh/id_rsa.pub.<br />
The key fingerprint is:<br />
XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX:XX root@CentosVM<br />
The key's randomart image is:<br />
+--[ RSA 2048]----+<br />
|  o....  .       |<br />
|   o..+=Eo.      |<br />
|     .*B=o.      |<br />
|     ****        |<br />
|      ...        |<br />
|       o o       |<br />
|        .        |<br />
|                 |<br />
|                 |<br />
+-----------------+<br />
[~]#<br />

Esto genera 2 archivos en nuestro directorio home: ~/.ssh/id_rsa y ~/.ssh/id_rsa.pub. El primero contiene la clave privada, que debemos mantener segura y no compartir. El segundo archivo contiene la clave pública, que compartiremos con los servidores SSH a los que querramos conectarnos.

En Windows

Para crear el par de claves en Windows se puede utilizar puttygen (se puede descargar aquí: http://www.chiark.greenend.org.uk/~sgtatham/putty/download.html).
Basta con iniciar el programa, seleccionar «SSH-2 RSA«, 2048 bits de largo de clave y clickear Generate. Nos va a pedir mover el puntero del mouse para generar suficiente aleatoriedad en la generación de las claves.



Luego de eso podemos guardar las claves, teniendo en cuenta las mismas precauciones que si la generamos en un Linux: la clave privada no se comparte con nadie, la clave pública si.


Copiar llaves al servidor SSH

Luego de generar las claves debemos compartir nuestra clave pública con el servidor SSH al que queremos conectarnos.

En Linux

<br />
[~]# ssh-copy-id -i ~/.ssh/id_rsa.pub usuario@servidor<br />

Nos preguntará la contraseña de usuario en servidor, para luego agregar nuestra clave pública al final del archivo .ssh/authorized_keys del directorio home de usuario en servidor.

En Windows

Parece que no hay un equivalente a ssh-copy-id en Windows (link a ServerFault), por lo que hacemos este proceso a mano.

Con PuTTY o cualquier programa similar debemos agregar el
contenido de nuestra llave pública en el archivo .ssh/authorized_keys del servidor SSH. Debemos copiar nuestra llave pública (sin incluir los comentarios de "---- BEGIN SSH2 PUBLIC KEY ----" y "---- END SSH2 PUBLIC KEY ----") a una nueva línea con el siguiente formato:

<br />
ssh-rsa llave_pública [comentario]<br />

El [comentario] es opcional, y sirve simplemente para identificar nuestra clave en el archivo, dado que se muy posible que existan otras claves públicas listadas.

Si no existe el directorio o el archivo se pueden crear, asegurándonos que queden con permisos de lectura y escritura sólo para el dueño (chmod 600 .ssh/authorized_keys).

Conexión utilizando clave pública/privada

En Linux

Luego de la instalación de la clave, si utilizamos los nombres de las claves por defecto la conexión desde Linux al servidor SSH es tan simple como ejecutar:

<br />
[~]# ssh -i ~/.ssh/id_rsa usuario@servidor<br />

En Windows

La forma de utilizar nuestro nuevo par de claves generado dependerá del programa que vayamos a utilizar. En PuTTY lo configuramos de la siguiente forma:

Navegamos hasta la sección «Auth» del árbol de opciones e ingresamos la ruta del archivo de nuestra clave privada en «Private key file for authentication«

Más información

Error de Oracle ORA-12705 (Cannot access NLS data files or invalid environment specified)

Hay veces que al conectarnos a una base Oracle, nos retorna un error ORA-12705 con el mensaje «ORA-12705:Cannot access NLS data files or invalid environment specified», o en un ambiente en español «ORA-12705: No se puede acceder a los archivos de datos NLS o se ha especificado un entorno no válido».

Un stack trace típico de este error en una aplicación Java es algo parecido a esto:

<br />
Caused by: java.sql.SQLException: ORA-00604: error occurred at recursive SQL level 1<br />
ORA-12705: Cannot access NLS data files or invalid environment specified<br />
	at oracle.jdbc.driver.DatabaseError.throwSqlException(DatabaseError.java:112)<br />
	at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:331)<br />
	at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:283)<br />
	at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:278)<br />
	at oracle.jdbc.driver.T4CTTIoauthenticate.receiveOauth(T4CTTIoauthenticate.java:785)<br />
	at oracle.jdbc.driver.<br />
T4CConnection.logon(T4CConnection.java:362)<br />
	at oracle.jdbc.driver.PhysicalConnection.&amp;lt;init&amp;gt;(PhysicalConnection.java:414)<br />
	at oracle.jdbc.driver.T4CConnection.&amp;lt;init&amp;gt;(T4CConnection.java:165)<br />
	at oracle.jdbc.driver.T4CDriverExtension.getConnection(T4CDriverExtension.java:35)<br />
	at oracle.jdbc.driver.OracleDriver.connect(OracleDriver.java:801)<br />
	at java.sql.DriverManager.getConnection(DriverManager.java:582)<br />
	at java.sql.DriverManager.getConnection(DriverManager.java:185)<br />
	at org.apache.commons.dbcp.DriverManagerConnectionFactory.createConnection(DriverManagerConnectionFactory.java:65)<br />
	at org.apache.commons.dbcp.PoolableConnectionFactory.makeObject(PoolableConnectionFactory.java:294)<br />
	at org.apache.commons.pool.impl.GenericObjectPool.borrowObject(GenericObjectPool.java:840)<br />
	at org.apache.commons.dbcp.PoolingDataSource.getConnection(PoolingDataSource.java:96)<br />
	... 7 more<br />

O algo parecido a esto, en español:

<br />
ORA-12705: No se puede acceder a los archivos de datos NLS o se ha especificado un entorno no válido); nested exception is org.apache.commons.dbcp.SQLNestedException: Cannot create PoolableConnectionFactory (ORA-00604: se ha producido un error a nivel 1 de SQL recursivo ORA-12705: No se puede acceder a los archivos de datos NLS o se ha especificado un entorno no válido)<br />

En mi caso, el problema se da cuando tengo el sistema operativo -y el conjunto de caracteres- configurado para Uruguay. Para solucionarlo en las aplicaciones Java, podemos agregar opciones para configurar el locale que queremos que la JVM utilice, de forma que no tome el del sistema operativo. Esto se hace agregando las siguientes opciones al ejecutar el comando java:

<br />
-Duser.region=us -Duser.language=en<br />

Con user.region configurada para ‘us’ y user.language para ‘en’ no falla, eso es seguro. La configuración para México (-Duser.region=mx -Duser.language=es) o España (-Duser.region=es -Duser.
language=es) también funciona bien.

Otra opción es setear la variable de entorno NLS_LANG con un valor de tres partes: _.. Ejemplos de NLS_LANG pueden ser «AMERICAN_AMERICA.WE8ISO8859P1» o «AMERICAN_AMERICA.UTF8». Podemos ver todos los valores válidos para el lenguaje, territorio y conjunto de caracteres en la vista llamada V$NLS_VALID_VALUES, mediante la consulta

<br />
SELECT parameter, value FROM V$NLS_VALID_VALUES<br />

Una tercer opción, válida en Windows es cambiar la configuración regional y de idioma para algún país que Oracle soporte (Estados Unidos, España, México, etc.). Hay que tener en cuenta sin embargo que esta configuración nos puede afectar otros programas, especialmente en la configuración de moneda, formato de fecha o de separación de miles.

Los siguientes links tienen buena información al respecto, aunque no muy orientadas a resolver el problema para lenguajes o sets de caractedes «raros» para Oracle:
http://ora-12705.ora-code.com/
http://www.dba-oracle.com/t_nls_lang.htm
http://www.dba-oracle.com/t_ora_12705_error.htm
http://oraclespin.wordpress.com/2008/05/01/setting-nls_lang-for-oracle/

Editar archivos de Office 2007 en versiones anteriores de Office

Según Microsoft,

Al instalar el paquete de compatibilidad junto con Office 2000, Office XP u Office 2003, podrá abrir, editar y guardar archivos con los nuevos formatos de Word 2007, Excel 2007 y PowerPoint 2007. El paquete de compatibilidad puede utilizarse junto con Microsoft Office Word Viewer 2003, Excel Viewer 2003 y PowerPoint Viewer 2003 para ver los archivos guardados con estos formatos.

El paquete de compatibilidad pesa 37.2 MB y se puede descargar en ésta página. Es un instalador llamado FileFormatConverters.exe, y el proceso de instalación es bien simple.

Luego de instalado tendremos la posibilidad de abrir archivos de Office 2007 (.docx, .pptx, .xlsx, etc.), editarlos y guardarlos como si fueran
versiones antiguas. Para crear archivos de Office 2007 tendremos la opción de guardarlos con el formato deseado en la lista de formatos de «Guardar como tipo«:

Referencias:

Array de bytes y la clase java.nio.ByteBuffer

Un buffer es una secuencia finita de elementos -en este caso, tipos primitivos-. La clase abstracta java.nio.Buffer y sus respectivas subclases concretas (java.nio.ByteBuffer, java.nio.IntBuffer, java.nio.FloatBuffer, etc.) sirven como contenedores para datos de tipos primitivos. Estos buffers forman parte de la API NIO junto a las classes Channel, Selector, entre otras.

Estas clases, en particular java.nio.
ByteBuffer
, son de especial utilidad en aplicaciones en las cuales tenemos que generar array de bytes y que no justifica usar frameworks demasiado refinados para dicha tarea.

Un java.nio.Buffer tiene algunas propiedades escenciales:

  • Capacidad: la cantidad de elementos máxima del buffer. Siempre tiene un valor positivo, y no cambia durante la vida del objeto. Debe ser conocido al momento de inicializar el buffer.
  • Límite: según la documentación de java.nio.Buffer, el límite es «el índice del primer elemento que no debería ser leído ni escrito». Es un valor entre la posición y la capacidad del buffer. La idea es utilizarlo como marcador para que, luego de terminada la escritura hacia el buffer, sepamos hasta donde lo podemos leer para obtener el array generado.
  • Posición: la posición actual dentro del bufffer. Es el índice del próximo elemento a leer o escribir.

Funcionalidades básicas

Todas estas funcionalidades están muy detalladas en los javadocs de las clases, pero es interesante repasar las más útiles:

  • Operaciones de escritura: la API provee las distintas variantes de operaciones put, permitiendo agregar bytes y tipos más grandes como ser int, long, float, arrays de bytes o incluso el contenido de otro ByteBuffer. Para los tipos de datos que ocupen más de un byte, se considerará la codificación (endianness) seteado utilizando el método order(). Tenemos métodos put para agregar bytes en posiciones relativas (en la posición actual) o absolutas (en una posición determinada)
  • Operaciones de lectura: al igual que con las operaciones put, las operaciones get permiten recuperar datos del buffer. Tenemos operaciones para recuperar diferentes tipos
    de datos (siempre teniendo en cuenta la codificación –endianness– cuando los tipos de datos son multi-byte), para recuperarlos desde la posición actual o desde una posición determinada. También podemos cargar un array de bytes con una sección del buffer.
  • clear(): prepara el buffer para una nueva secuencia de operaciones de escritura, seteando el límite con igual valor que la capacidad, y la posición actual en cero.
  • flip(): prepara el buffer para una secuencia de lecturas, seteando el límite igual a la posición actual, y la posición a cero. Más allá de la capacidad máxima del buffer, permite hacer operaciones sobre la porción que utilizamos anteriormente.
  • rewind(): prepara el buffer para una relectura de la información que contiene, seteando la posición en cero sin cambiar el valor del límite.
  • mark() /
    reset()
    : mark() setea la marca del buffer en la posición actual. al invocar reset(), cambiamos de posición en el buffer a la última invocación de mark().
  • slice(): crea un nuevo ByteBuffer a partir de la posición actual y hasta el fin del array. Los buffers comparten el array subyacente, por lo que un cambio en los datos se verá reflejado en ambos.
  • array(): retorna el buffer en forma de array.
  • Chaining (encadenamiento) de invocaciones: los métodos que no deben retornar valores al invocarlos, retornan una referencia a sí mismo, de manera de poder encadenar invocaciones al buffer. El encadenamiento no aporta nuevas funcionalidades, simplemente mejora la usabilidad.

Ejemplo de uso

Imaginemos que necesitamos generar un array de bytes para enviar a un sistema externo, que contiene la siguiente información:

CampoTipo de datoLargoFormato
Largo del mensajeshort2 bytesLittle Endian
Idlong8 bytesBig Endian
Largo del nombreshort2 bytesLittle Endian
NombreStringMáximo 30 caracteres
Edadbyte1 byte
Salarioint4 bytesBig Endian

Debemos codificar el largo total del mensaje y el largo del nombre como little-endian (es decir, el byte menos significativo va en la primer posición, y el byte más significativo va en la última) en lugar de big-endian -a veces también llamado network order, por ser la codificación definida en
el protocolo IP-.

Aprovecharemos las funcionalidades de la clase ByteBuffer para resolver este problema. En particular, ByteBuffer nos permite setear la representación de los enteros de varios bytes en el array, utilizando el método order(ByteOrder bo). Una solución que resuelve la generación del array podría ser la siguiente:

<br />
package com.josearrarte.demo;</p>
<p>import java.nio.ByteBuffer;<br />
import java.nio.ByteOrder;</p>
<p>public class ByteBufferDemo {</p>
<p>	private static final int SIZEOF_BYTE = 1;<br />
	private static final int SIZEOF_SHORT = 2;<br />
	private static final int SIZEOF_INT = 4;<br />
	private static final int SIZEOF_LONG = 8;</p>
<p>	public static void main(String[] args) {</p>
<p>		byte[] byteArray = buildByteArray(12345678L, &amp;quot;Pedro Picapiedra&amp;quot;, (byte)46, 25000);</p>
<p>		String hexString = getHexString(byteArray);<br />
		System.out.println(hexString);<br />
	}</p>
<p>	private static byte[]<br />
buildByteArray(long id, String name, byte age, int salary) {</p>
<p>		int capacity = 2 * SIZEOF_SHORT +	// largos<br />
			SIZEOF_LONG + 					// id<br />
			name.length() + 				// nombre<br />
			SIZEOF_BYTE + 					// edad<br />
			SIZEOF_INT;						// salario</p>
<p>		ByteBuffer buffer = ByteBuffer.allocate(capacity);</p>
<p>		// largo de datos<br />
		buffer.order(ByteOrder.LITTLE_ENDIAN);<br />
		buffer.putShort((short)capacity);</p>
<p>		// id<br />
		buffer.order(ByteOrder.BIG_ENDIAN);<br />
		buffer.putLong(id);</p>
<p>		// largo del campo nombre<br />
		buffer.order(ByteOrder.LITTLE_ENDIAN).putShort((short)name.length());</p>
<p>		// nombre<br />
		buffer.order(ByteOrder.BIG_ENDIAN).put(name.getBytes());</p>
<p>		// edad<br />
		buffer.put(age).putInt(salary);</p>
<p>		return buffer.array();</p>
<p>	}</p>
<p>	private static String getHexString(byte[] array) {<br />
		final char[] chars = { '0', '1', '2', '3', '4',<br />
				'5', '6', '7', '8', '9',<br />
				'A', 'B', 'C', 'D', 'E', 'F' };</p>
<p>		StringBuilder strBuilder = new StringBuilder();</p>
<p>		for (int<br />
i = 0; i &amp;lt; array.length; i++) {<br />
			byte b = array[i];</p>
<p>			byte lowNibble = (byte) (b &amp;amp; 0x0F);<br />
			byte highNibble = (byte) ((b &amp;amp; 0xF0) &amp;gt;&amp;gt;&amp;gt; 4);</p>
<p>			strBuilder.append(chars[highNibble]);<br />
			strBuilder.append(chars[lowNibble]);<br />
			strBuilder.append(' ');<br />
		}</p>
<p>		return strBuilder.toString();<br />
	}<br />
}<br />

La salida del programa es:

<br />
21 00 00 00 00 00 00 BC 61 4E 10 00 50 65 64 72 6F 20 50 69 63 61 70 69 65 64 72 61 2E 00 00 61 A8<br />

Podemos desglosar la salida para cada dato:

  • Largo de datos: 21 00 (33)
  • Id: 00 00 00 00 00 BC 61 4E (12345678)
  • Largo del campo de nombre: 10 00 (16)
  • Nombre: 50 65 64 72 6F 20 50 69 63 61 70 69 65 64 72 61 («Pedro Picapiedra»)
  • Edad: 2E (46)
  • Salario: 00 00 61 A8 (25000)

Notemos la facilidad ByteBuffer que nos da a la hora de tener que intercambiar los formatos de
enteros y de mezclar distintos tipos primitivos en un mismo array. No tuvimos necesidad de ir calculando las posiciones dentro del array, ni calcular la representación de enteros en formato little-endian o big-endian.

Referencias

API de java.nio.ByteBuffer
API de java.nio.Buffer
http://en.wikipedia.org/wiki/Endianness
http://en.wikipedia.org/wiki/New_I/O