Acerca de:

Este blog contiene los códigos, ejemplos y bases de datos que he usado cuando aprendía acerca de algún tema específico. En lugar de borrarlos (una vez dominado ya el tema), he decidido publicarlos :)

miércoles, 10 de diciembre de 2014

Cómo instalar el servicio de Apache Tomcat 8.0.15

Tengo el JDK 1.8 y el JRE 1.8 instalados en mi Windows 7 64 bits SP1, cuya instalación me creó la siguiente variable de entorno:


Dado que no me creó las variables JAVA_HOME y JRE_HOME, es recomendable añadirlas manualmente.
JAVA_HOME apunta a la carpeta donde está instalado el JDK. JRE_HOME apunta a la instalación del JRE.



Un día decidí trastear, literalmente, una instalación de Tomcat 8.0.15 en mi pc.

Luego de la última instalación, el instalador ya no fue capaz de crear el servicio del Tomcat
Esto lo supe al ejecutar el programa Tomcat8w.exe, pues arrojaba un mensaje de error avisando que no existía el servicio, y éste tampoco aparecía en la lista de servicios en services.msc.

Para instalar el servicio de Tomcat, primero hay que reiniciar el sistema (esto es necesario luego de crear las variables de entorno JAVA_HOME y JRE_HOME).
Después, en una ventana de consola ejecutada como administrador, se debe ir a la carpeta donde está el archivo service.bat: el cual está en la carpeta "bin", dentro de la ruta de instalación del servidor Tomcat (en mi caso dentro de C:\Program Files\Apache Software Foundation\Apache Tomcat 8.0.15\bin) y ejecutar el comando "services.bat install"


Ya con esto, se ha instalado el servicio de Tomcat y es posible entrar a las opciones de configuración ejecutando tomcat8w.exe:


jueves, 27 de noviembre de 2014

Encontrando errores en los archivos xlsx (Ms Excel 2007 - 2010) con Open XML SDK Validation

Entrada original en el blog de Brian Jones (autor Zeyad Rajabi).

Lo único que hice fue acomodar y corregir un poco el código para crear un método que llame al Open XML SDK Validation. Este método escribirá en la ventana de Resultados los errores del archivo xlsx cuya ruta está en la variable "path", y puede ponerse dentro de cualquier clase:

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Reflection;
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Spreadsheet;
using DocumentFormat.OpenXml;
using DocumentFormat.OpenXml.Validation;

static void validarExcel(string path)
{
   OpenXmlValidator validator = new OpenXmlValidator();
   using (SpreadsheetDocument excelFile = SpreadsheetDocument.Open(path, true))
  {
      try
      {
            int count = 0;
            foreach (ValidationErrorInfo error in validator.Validate(excelFile))
            {
                     count++;
                     Trace.WriteLine("Error " + count);
                     Trace.WriteLine("Description: " + error.Description);
                     Trace.WriteLine("Path: " + error.Path.XPath);
                     Trace.WriteLine("Part: " + error.Part.Uri);
                     Trace.WriteLine("-------------------------------------");
             }
      }
      catch (Exception ex)
      {
               Trace.WriteLine(ex.Message);
      }
        excelFile.Close();
    }
}

Por ejemplo, un error que hallé en un archivo xlsx generado desde C# fue que, al ponerles nombres a cada hoja de cada archivo de excel, éstos no pueden tener más de 32 caracteres. Al abrir el archivo xlsx en MS Excel 2010 me tiraba este error (no ocurre en OpenOffice):


 "Registros Reparados: Propiedades de la hoja de /xl/workbook.xml"

Este error se repara en la línea donde se declara el objeto Sheet y se corrige truncando la cadena con el nombre de la hoja a menos de 32 caracteres:

String myName = "nombremuymuymuymuymuymuymuymuymuylargo";

Sheet sheet = new Sheet() { Id = spreadsheet.WorkbookPart.GetIdOfPart(newWorksheetPart), SheetId = worksheetNumber, Name = new string(myName.Take(31).ToArray()) };


Más información en este enlace.

Mike Gledhill tiene una excelente sección acerca de cómo escribir archivos xlsx con Open XML.

martes, 14 de octubre de 2014

Test Fizz Buzz en Python recursivo


Ejecutado y compilado con este compilador online.

def FZ(i):
  if i==0:
    return 0
  else:
    if i%3 == 0 or i%5 == 0:
      if i%3 == 0 and i%5 == 0:
        print "Fizz Buzz"
      else:
        if i%3 == 0:
          print "Fizz"
        if i%5 == 0:
          print "Buzz"
    else:
      print i
  
    return FZ(i-1) 
 
FZ(100)

miércoles, 1 de octubre de 2014

Modificar los datos de un DataTable con Linq en C#

Este post existe gracias a la ayuda de StackOverflow :D

Tengo un DataTable, el cual tiene una columna llamada "Campo1" con datos tipo texto. Quiero que todos los datos que coincidan con un valor sean cambiados por la cadena "Datos por Defecto".
En lugar de usar un bucle, se puede hacer con Linq:

DataTable d = MetodoQueDevuelveUnDataTable();
d.AsEnumerable().ToList<DataRow>().ForEach
   ( r =>
             {
                     if(String.Compare(r["Campo1"].ToString(), "valor que quiero cambiar") == 0)
                          r["Campo1"] = "Datos Por Defecto";
             }
   );

Lo que hace el ForEach es recorrer todas las filas y buscar aquellas donde se cumple la condición y realizar las acciones que deseemos:

DataTable d = MetodoQueDevuelveUnDataTable();
d.AsEnumerable().ToList<DataRow>().ForEach
  ( r =>
             {
                      if(String.Compare(r["Campo1"].ToString(), "valor que quiero cambiar") == 0)
                      {
                           r["Campo1"] = "Datos Por Defecto";
                           r["Campo2"] = Otro Valor;
                       }
             }
  );

Si se desea modificar un valor de uno o más campos determinados en todas las filas, sólo se quita la condición:
 
DataTable d = MetodoQueDevuelveUnDataTable();
d.AsEnumerable().ToList<DataRow>().ForEach
  ( r =>
             {
                 r["Campo1"] = "Datos Por Defecto";
                 r["Campo2"] = Otro Valor;
             }
  );

viernes, 12 de septiembre de 2014

Cómo ejecutar Visual Studio 6 en Windows 7 y evitar los errores con referencias y dependencias

Tengo un Visual Basic 6 Enterprise corriendo en Windows 7 de 32 bits. Todo parece funcionar bien: se ha instalado sin problemas, y se ejecuta sin inconveientes.
Pero al abrir un proyecto nuevo del tipo Exe estándar, e intentar cargar librerías y ocx de terceros (no son de Microsoft, o no están firmadas por Microsoft) me salta este error "Error al intentar tener acceso al registro del sistema":


(No lo parece, pero esto es un Windows 7, sólo que con el tema de Windows Clásico. Los colorcitos sólo sirven para consumir memoria, otra razón es que así puedo aluscinar que tengo un Windows Server)


Este fue un error muy feo que me tuvo ocupada durante horas (además de hacerme instalar y desinstalar las ocx varias veces), y al final la solución era muy simple, ejecutar el Visual Studio 6 como Administrador:


Ya con todos los permisos disponibles, el Windows nos deja importar componentes, referencias y dependencias. Y como se muestra en la imagen, el Visual Studio 6 puede convivir perfectamente con el Visual Studio 2013 (yo tengo la versión Express).
Un último consejo: si se tiene un proyecto de Visual Basic 6 con dependencias de terceros, es mejor ejecutar primero el IDE como administrador, y luego abrir nuestro proyecto. Se evitarán así muchos errores.

domingo, 24 de agosto de 2014

Algoritmos para calcular la factorización QR de una matriz

La factorización QR se utiliza para descomponer una matriz (que puede representar un sistema de ecuaciones a resolver) en dos matrices, con las cuales es más fácil de operar y hallar soluciones al sistema de ecuaciones.
Más información en la WIkipedia.

Los siguientes códigos están basados en los algoritmos mostrados en el libro "Numerical Linear Algebra" de Trefethen y Bau.

Algoritmo de Gram-Schmidth Modificado (devuelve la factorización QR reducida):



El código en Matlab es:

function [Q, R] = gs(A)
[m, n] = size(A);
R = zeros(n, n);
V = A;
Q=zeros(m, n);

for i =1:n
    R(i,i)= norm(V(:,i));
    Q(:,i)= V(:,i)/R(i,i);
   
    for j=i+1:n
       R(i,j)= (Q(:,i)')*V(:,j);
       V(:,j)=V(:,j) - R(i,j)*Q(:,i);
    end
end

"A" es una matriz de rango completo, la función devuelve dos matrices: Q (la matriz ortonormal) y R (la matriz triangular).

Problemas con este algoritmo: Sólo da la factorización QR reducida, y sólo funciona para matrices de rango completo (es decir: ninguna columna es múltiplo de, y/o resultado de, sumar otras columnas de la mariz entre sí).

Modifiqué este algoritmo para que funcionara también con matrices de "rango deficiente" (lo opuesto a "rango completo"), y que devolviera la factorización completa; esto último se hace completando las columnas de la matriz Q, las cuales son ortogonales entre sí. En palabras del curso de Álgebra Lineal: "completando la base ortonormal del espacio vectorial generado por las columnas de Q". Dicho de otra forma: aplicando el Teorema de la Base Ortonormal Incompleta.

El código en Matlab es:

function [Q, R] = gsC(A)
clc;
[m, n] = size(A);
R = zeros(m, n);
V = A;
Q=zeros(m, n);

k=1; % inicializo eta variable, sólo la uso para matrices de rango deficiente

for i =1:n
    R(i,i)= norm(V(:,i));  
   
    if abs(R(i,i)) > 1.0e-10
        Q(:,i)= V(:,i)/R(i,i);
    else  % salió cero! La matriz A es de rango deficiente
        r= zeros(1, m); % este va a ser el vector que reemplazará a la fila problemática de A
              
        Q(:,i)= r;
        Q(k, i)=1;
        k=k+1;
        Q2 = Q; % Q2 tendrá los valores de Q, en Q irá la nueva base ortonormal

        % ortogonalizo otra vez aplicando Gram-Schmidt a la matriz Q para
        % hallar un vector con el qué reemplazar a la fila problemática y
        % seguir teniendo una base ortonormal
        for j = 1 : i-1
            r(j) = (Q2(:,j)')*Q2(:,i);
            Q(:,i)=Q(:,i) - r(j)*Q2(:,j);
            Q(:,i)=Q(:,i)/norm(Q(:,i));
        end
    end
   
    for j=i+1:n
       R(i,j)= (Q(:,i)')*V(:,j);
       V(:,j)=V(:,j) - R(i,j)*Q(:,i);
    end
end

% hasta aquí Q y R son la descomposición reducida
% acá hallo la completa, Gram Schmidt otra vez
if m > n   
    I = eye(m, m - n);
    Q=[Q I];
    V=Q;
    r= zeros(1, m);
   
    k=0;
    for i=n+1:m
        for j = 1 : n + k
            r(j) = (V(:,j)')*V(:,i);
            Q(:,i)=Q(:,i) - r(j)*V(:,j);
            Q(:,i)=Q(:,i)/norm(Q(:,i));
        end
        k=k+1;
    end
end

"A" es una matriz cualquiera, la función devuelve dos matrices: Q (la matriz ortonormal) y R (la matriz triangular). 

Otra forma de calcular la factorización QR (y la que utiliza internamente Matlab) es usando la factorización de Householder:


El código en Matlab es:

function [Q, R] = houseHolder(A)
clc;
[m n] = size(A);

R= A;
Q = eye(m,m);

n1=min(m,n);

for i= 1:n1
    x = R(i:m, i);
    s=0;
   
    if x(1)==0
        s = 1;
    else
        s= sign(x(1));
    end
   
    e = zeros(m - i + 1, 1);   
    e(1)=1;   
    u = s * norm(x) * e + x;
    u = u / norm(u);
    R(i:m, i:n) =  R(i:m, i:n) - 2*(u*u')*R(i:m, i:n);
    Q(:,i:m) = Q(:,i:m) - Q(:,i:m)*(2*u*u');   
end


Igual que en los casos anteriores, "A" es una matriz cualquiera, la función devuelve dos matrices: Q (la matriz ortonormal) y R (la matriz triangular).

Los tres algoritmos han sido probados en Matlab 2008a, así que deben correr en cualquier versión posterior, y no tan posterior :)

miércoles, 9 de julio de 2014

Crystal Reports Runtime: No se ha podido cargar el informe

El error completo es:
"CrystalDecisions.Shared.CrystalReportsException: No se ha podido cargar el informe. Un documento procesado por el motor JRC no se puede abrir en la pila C++".
 En concreto, salta cuando se utilizan las librerías de CrystalReports para Net.


Es un error muy feo para el motivo que lo produce: el archivo rpt que debe cargarse simplemente no está en la ruta que le indicamos. Exactamente en el método Load del objeto ReportDocument:

Private WithEvents oRpt As ReportDocument
oRpt = New ReportDocument
oRpt.Load("../informes/CrystalReportAutores.rpt")


Código sacado de la Güeb de Joaquín :D
Agradezco a este enlace por darme la solución :D

martes, 3 de junio de 2014

Npgsql lanza una System.OutOfMemoryException

Jugaba un día con el C#, las librerías del Npgsql y con una base de datos con dos tablas vinculadas entre sí por una llave foránea. Luego de insertar unos cuantos registros en ambas tablas, intenté borrar los registros de una de ellas y entonces saltó esta excepción:


Saltó una "System.OutOfMemoryException". Llegué a pensar que mi programita tendría fugas de memoria en algún lado, pero al intentar borrar los mismos registros desde el pgAdmin III saltaba el verdadero error: violación de las reglas de la llave foránea.

Me pareció curioso así que, luego de experimentar un poco, descubrí que el Npgsql lanza la System.OutOfMemoryException cada vez que se viola una relación de llaves foráneas (en mi caso: intentar borrar los registros cuyas llaves primarias estaban vinculadas hacia la otra tabla por medio de la llave foránea).
En general, esta excepción suele saltar si se violan las reglas establecidas para las secuencias (datos de tipo serial), llaves primarias y llaves foráneas.

Pero me quedaba una pregunta: ¿no debería saltar una InvalidOperationException?

Mi teoría es la siguiente: una llave foránea es en realidad un puntero hacia los datos de otra tabla. Al querer realizar una operación no permitida (por ejemplo intentar borrar los datos a donde apunta el puntero pero sin borrar el puntero) se tiene un puntero que apunta (valga la redundancia) hacia un área de memoria que ahora es inválida. Para colmo, el sistema operativo (si es código no administrado no hay colector de basura) no puede reasignar esa memoria para su uso posterior ni liberar el puntero. En resumen: resulta una fuga de memoria, la cual se traduce (al pasar ya al mundo del código administrado del .Net) como una OutOfMemoryException.

Para saber más del tema de los punteros y sus problemas, recomiendo este pdf.

viernes, 9 de mayo de 2014

¿Cuál es la diferencia entre “Null” y “Nothing” en VB6?

Esto lo pregunté en StackOverflow hace un tiempo. Acá pongo la traducción:

Tengo un recordset como el siguiente:


Dim rs as Recordset
Set rs as New Recordset

'... a lot of coding ...

if Err.Number <> 0 Then ' oops, algo salió mal
    If rs.State <> adStateClosed Then rs.Close
    Set rs = Nothing
end if

' quiero evaluar si rs es Nothing o Null

if rs is Nothing then 
' esto no tira errores y funciona bien :D
end if

if rs is Null then
' esto tira un error de "tipos no compatibles"
end if

if rs = Null then
' esto tira un error de "tipos no compatibles"
end if

if isNull(rs) then
' nunca entra aquí, isNull(rs) evalua a Falso
end if 

La respuesta que me dio Mark Bertenshaw es:
"Null" es un subtipo específico de Variant. No existe fuera de Variant, y se creó para permitir modelar un dato de tipo nulo en una base de datos.
"Nothing" es un valor de tipo Object. Identifica un puntero nulo, es decir: no hay objeto.
Al evaluar un recordset con el operador "is" tira un error porque sólo se puede usar con datos de tipo Object.
IsNull(rs) retorna falso porque evalúa variables del tipo Variant.
IsNull(rs) equivale a VarType(rs) = VBNull