El mucho morro

Published 75 weeks, 1 day ago
Thu Jan 25 2007
Es imparable, no reconoce fronteras, ni religiones, ni razas, ni absolutamente nada. Cuando se tiene mucho morro uno, por supuesto, no se para en tonterías tales como el copyright, la propiedad intelectual o las marcas registradas. Aunque el que ostenta la marca registrada esté en un terreno un tanto gris al respecto, el logo en cuestión es marca registrada y por lo tanto debería haberse respetado.

O no, dependiendo del morro que se tenga. Mirad si no el logo de esta consultora especializada en la contratación de personal de dirección.

I Love ReSharper

Published 75 weeks, 3 days ago
Wed Jan 24 2007
Ésta iba a ser una entrada sobre los distintos plugins de VS 2005 que uso, y otros programas útiles para mi día a día (la mayoría Open Source, por cierto); pero mientras lo estaba escribiendo se ha ido convirtiendo progresivamente en una carta de amor a Resharper. No puedo más que decir cosas buenas de este plugin de aumento de productividad.

Éstas son sólo algunas de sus características:
  • Chequeo de errores y avisos según escribes código; al estilo de la revisión automática de ortografía de Office pero bastante más inteligente.
  • Intellisense mejorado. De lejos, lo que más me gusta y más se nota después de recién instalado. Si lo deseas, ReSharper puede reemplazar el Intellisense de VS2005 con el suyo propio, que es más rápido y completo. Posibilidad de establecer en Intellisense la misma fuente que en el entorno de desarrollo, posibilidad de reemplazar los iconos de Intellisense con los de ReSharper, añade un pop-up con la firma del método completo al resaltarlo en Intellisense y se activa desde que escribes los primeros tres caracteres (por defecto) de cualquier cosa.
  • Auto-completado de código, escribes las tres primeras letras de cualquier cosa y ReSharper ya te está mostrando alternativas. Si además lo que estás escribiendo es un método, pulsando TAB te inserta el nombre completo del método más los paréntesis y el punto y coma necesario.
  • Templates.
  • Refactorización: extracción de propiedades desde variables, extracción de métodos desde código: seleccionas un bloque de código, eliges extraerlo como método, le das un nombre y opciones, entre las que se incluyen los parámetros de entrada del método que ReSharper detecta automáticamente y método extraído.
  • Búsqueda avanzada de usos de métodos.
  • Vista de jerarquías de tipos.
  • Lanzamiento de testeos unitarios con NUnit o csUnit desde el propio IDE.
  • Acciones contextuales. En ciertas secciones del código puede aparecer una pequeña ventana flotante con un icono de una bombilla. ReSharper te quiere ayudar: extendiendo esa ventana con el ratón (o mejor, ALT+Enter) ReSharper te mostrará una lista de las posibles acciones a realizar según el contexto del código: chequear valores nulos, reorganizar sentencias if, reorganizar cadenas con un StringBuilder, eliminar referencias using que no se están usando.
  • Detección automática de variables, métodos o referencias no usadas. Declaras una variable al principio del código y luego no la usas para nada: ReSharper la resalta en color gris, para que sepas que no se está usando en ningún sitio, ningún método hace referencia a ella.
  • Salto directo a la definición de un método o variable: con CTRL+Click encima del nombre de un método saltamos directamente al fichero de código que contiene ese método, si está disponible.
Y estos son sólo las partes de la herramienta que utilizo todos los días. Definitivamente, es la mejor compra que he hecho jamás para un complemento de Visual Studio; sobre todo teniendo en cuenta que me acogí a la oferta de $99 de estas navidades (de la que ya avisé por aquí), porque ha vuelto a su precio normal de $249. Los programadores de JetBrains hacen con su producto que Visual Studio 2005 sea un IDE aún mejor de lo que ya es. La lástima es que la perenne noticia de que están desarrollando su propio IDE para .NET es, al parecer, antigua (mirad en los comentarios), ya que al parecer el proyecto se ha abandonado. Una pena.

VirtualBox

Published 76 weeks, 4 days ago
Mon Jan 15 2007
Cuando empezaba en esto del desarrollo, trabajaba para una empresa que (entre otras cosas) vendía una solución de software para la personalización de tarjetas de plástico. Dicha solución era genérica (lo que los anglosajones llaman off-the-shelf software o shrinkwrap. Vamos, un programa genérico que los clientes compraban tal cual, como podrían comprarse un Office, por ejemplo; en lugar de una solución específica para cada cliente.

Como podréis imaginar, dicho software debía instalarse de forma fácil y sencilla y no requerir componentes adicionales. Para asegurarnos de ello, cada vez que se compilaba una nueva versión se hacían pruebas de compatibilidad: en mi mesa tenía cuatro ordenadores, uno para desarrollo y otras tres máquinas un poco más viejas para pruebas. Una de las máquinas tenía un Windows 95, otra un Windows 98 y otra un Windows NT; y cada una de ellas no tenía nada más que el sistema operativo y los drivers correspondientes instalados. Nuestro software debía funcionar en máquinas desnudas, y cada nueva versión que realizábamos teníamos que formatear las máquinas de prueba y reinstalar el sistema operativo, para asegurarnos de un funcionamiento perfecto. El proceso se repetía tanto que llegué a saberme de memoria los números de serie de cada uno de los SOs.

Conseguimos agilizar bastante el proceso gracias a Norton Ghost: se hacía una imagen del SO "desnudo" por cada máquina y después de las pruebas de instalación de nuestro software, se reinstalaba desde la imagen de Ghost.

Pero seguía siendo un coñazo.

Lo que no hubiera dado en aquellos días por software de máquinas virtuales: por aquél entonces o no existía o yo no lo conocía. Para los que no sepáis de lo que hablo, las máquinas virtuales son programas que crean un entorno virtual entre tu hardware físico y el sistema operativo instalado en dicho hardware; y en ese espacio virtual puedes instalar un sistema operativo completamente distinto al instalado físicamente, en un entorno completamente cerrado. Puedes tener un Windows Server 2003 dentro de un Windows XP, por ejemplo.

Sencillamente, se asigna dentro del disco duro de tu equipo un fichero que va a funcionar como disco duro de la máquina virtual; y se le asigna a la máquina virtual una cantidad de memoria RAM (que saldrá del total de la memoria RAM física del equipo, por lo que cuanta más RAM tengamos, mejor). Pueden también montarse unidades de CD y floppy virtuales, permitir que la máquina virtual tenga o no acceso a la red, etc. Para instalar un sistema operativo basta con montar un fichero ISO de auto-arraque en el CD virtual y arrancar la máquina virtual. A partir de ahí, podremos seguir las instrucciones de instalación como si tuviéramos, en efecto, un ordenador dentro de nuestro ordenador.

He usado principalmente dos entornos de virtualización: Microsoft Virtual PC, que en su versión 2004 es completamente gratuito (la 2007 es de pago) y que es un muy buen entorno de virtualización, de hecho es el que ahora utilizo en el trabajo.

Y el que acabo de probar en casa es InnoTek VirtualBox, que es Open Source y gratuito, para un entorno personal. Al menos en mi caso, lo importante es lo segundo: francamente no me veo con nivel como para entender, y mucho menos modificar, el código fuente de una de estas cosas.

De hecho, estoy escribiendo esta entrada desde una máquina virtual montada con VirtualBox y que está corriendo un Ubuntu Dapper Drake. No, no vuelvo a las andadas: es lo que tenía a mano para probar VirtualBox. En realidad, el objetivo en los próximos días es instalar Vista. =)

Así que si no conocíais el software de virtualización, mi consejo es que probéis cualquiera de los dos reseñados: ambos son sencillos e intuitivos de manejar, y mediante la virtualización disponemos de un entorno en el cual probar y cacharrear sin peligro.

Apellidos aleatorios (y falsos)

Published 77 weeks, 21 hours ago
Fri Jan 12 2007
Respondiendo al desafío planteado por Jon Galloway en su excelente blog, aquí está mi solución para generar apellidos falsos y aleatorios. El código está repleto de comentarios, eso sí en inglés de momento, los traduciré en cuanto pueda. Estás invitado, por supuesto, a hacer preguntas, críticas, o incluso mejor un buen refactorizado! =)

class Program
{
    
//Un arraylist que contiene todos los posibles digramas (ver más abajo)
    //(Nota: digram en inglés es la combinación de dos caracteres, no sé si en español será sílaba)
    private static ArrayList AllDigrams = new ArrayList();
    
//Un arraylist que contiene todas las letras posibles (ver más abajo)
    
private static ArrayList AllLetters = new ArrayList();
    
//Una semilla aleatoria para el generador aleatorio principal
    
private static Random rSeed = new Random()
    
//Un array multidimensional que especifica la estructura principal de un apellido. 
    // "A" representa una vocal, "B" una consonante
    //"AB representa un digrama de vocal + consonante, etc...
    //Estas combinaciones las he hecho a mano, puedes añadir, modificar o quitar 
    //combinaciones para ajustar el tipo de apellidos que se obtengan
    
private static string[,] SurnameRandomTypes = new string[106]
            {
                {
"A",  "BA""B",  "A",  "BA"""},
                {
"B",  "A",  "BA""",   "",   ""},
                {
"A",  "BA""A",  "B",  "A",  "BB"},
                {
"A",  "B",  "B",  "A",  "BA"""},
                {
"A",  "BB""AB""A",  "",   ""},
                {
"BA""B""B",  "A",  "B",  "A"},
                {
"B",  "AB""A",  "BB""A",  "AB"},
                {
"B",  "AB""B",  "A",  "BB""AB"},
                {
"A",  "BB""A",  "B",  "AB"""},
                {
"AB""AA""B",  "A",  "B",  "A"},
            }
;

    static void Main()
    {
        
bool exit = false;
        
//Cargamos TODOS los posibles digrams en memoria, 
        //en el arraylist AllDigrams
        
LoadDigrams();
        
//Lo mismo para las letras posibles, 
        //en el arraylist AllLetters
        
LoadLetters();
        do
        
{
            
for (int 0i <20i++)
            {
                Console.WriteLine(
"Apellido Aleatorio:    {0}"
                    GenerateRandomSurname())
;
            
}
            Console.WriteLine(
"Pulsa ENTER para generar otro lote, X para salir");
            string 
sInput Console.ReadLine();
            if 
(sInput.ToString() == "x" || sInput.ToString() == "X")
                exit 
= true;
        
while (exit == false);
        
Console.ReadLine();
    
}

    
/// <summary>
    /// Éste método genera un apellido aleatorio y lo devuelve.
    /// </summary>
    /// <returns></returns>
    
static string GenerateRandomSurname()
    {
        
string sRet "";
        
Random r;
        
//Nueva semilla aleatoria para el generador de números
        
= new Random(rSeed.Next(11000));
        
//y obtenemos un índice al azar
        //para el array multidimensional SurnameRandomTypes
        
int rndIndex r.Next(09);
        
//Entonces leemos las distintas "columnas" de  
        //esa "fila" y decidimos si necesitamos una vocal, 
        //una consonante, un digrama vocal-consonante, 
        //o lo que sea
        
for (int 0i < 6i++)
        {
            
switch(SurnameRandomTypes[rndIndex, i])
            {
                
case "AA":
                    sRet +
getDigram(DigramType.TwoVowels);
                    break;
                case 
"AB":
                    sRet +
getDigram(DigramType.VowelAndConsonant);
                    break;
                case 
"BA":
                    sRet +
getDigram(DigramType.ConsonantAndVowel);
                    break;
                case 
"BB":
                    sRet +
getDigram(DigramType.TwoConsonants);
                    break;
                case 
"A":
                    sRet +
getLetter(LetterType.Vowel);
                    break;
                case 
"B":
                    sRet +
getLetter(LetterType.Consonant);
                    break;
                default
:
                    
break;
            
}
        }
        
//Y devolvemos el apellido generado 
        //convirtiendo a mayúsculas la primera letra
        
return sRet.Substring(0,1).ToUpper() + 
            sRet.Substring(
1);
    
}

    
#region Letters and Digrams
    
    
#region Structs and Enums
    
/// <summary>
    /// Con esta enumeración sabremos de qué 
    /// está compuesto el digrama
    /// </summary>
    
private enum DigramType
    { TwoVowels, VowelAndConsonant, ConsonantAndVowel, TwoConsonants }

    
/// <summary>
    /// Con esta enumeración sabremos si una letra es una
    /// vocal o una consonante
    /// </summary>
    
private enum LetterType { Vowel, Consonant }

    
/// <summary>
    /// Un objeto digrama. Además del propio digrama ("th", por ejemplo)
    /// guardamos el rango de frecuencia en el que el digrama aparece 
    /// en el lenguaje inglés y su tipo
    /// </summary>
    
private struct Digram 
    {
        
public int iniRange;        //El principio de su rango.
        
public int endRange;        //El final de su rango.
        
public string Value;        //Su valor
        
public DigramType Type;     //Su tipo, según la enumeración DigramType
    
}

    
/// <summary>
    /// Una letra. Además de la propia letra ("a", por ejemplo)
    /// guardamos el rango de frecuencia en la que aparece  
    /// en el lenguaje inglés y su tipo
    /// </summary>
    
private struct Letter
    {
        
public int iniRange;        //El principio de su rango.
        
public int endRange;        //El final de su rango.
        
public string Value;        //Su valor.
        
public LetterType Type;     //Su tipo, según la enumeración LetterType
    
}
    
#endregion

    
/// <summary>
    /// Este método devuelve el valor  
    /// de un digrama aleatorio del tipo especificado
    /// </summary>
    /// <param name="type">El tipo de digrama que queremos obtener</param>
    /// <returns>El VALOR del digrama ("th", por ejemplo)</returns>
    
static string getDigram(DigramType type)
    {
        Random r 
= new Random(rSeed.Next(01000));
        
Digram nuDigram = new Digram();
        do
        
{
            
//Obtenemos un número aleatorio dentro del rango
            //TOTAL de TODOS los digramas
            
int freq r.Next(05548)//Ver LoadDigrams()
            
foreach (Digram digram in AllDigrams)
            {
                
//Y si el número aleatorio está dentro del  
                //rango de frecuencia del digrama actual, 
                //tenemos un ganador!
                
if (freq >digram.iniRange && freq <digram.endRange) 
                        nuDigram 
digram;
            
}
            
//Pero sólo si es del tipo que necesitamos
        
while (nuDigram.Type !type);
        return 
nuDigram.Value;
    
}

    
/// <summary>
    /// Obtenemos el VALOR de una sola letra del tipo especificado
    /// </summary>
    /// <param name="type">El tipo de letra que queremos obtener</param>
    /// <returns>El VALOR de la letra</returns>
    
static string getLetter(LetterType type) 
    {
        Random r 
= new Random(rSeed.Next(01000));
        
Letter nuLetter = new Letter();
        do
        
{
            
//Obtenemos un número aleatorio dentro del rango 
            //TOTAL de TODAS las letras
            
int freq r.Next(010025)//Ver LoadLetters()
            
foreach (Letter letter in AllLetters)
            {
                
//Y si el número aleatorio
                //está dentro del rango de frecuencias de
                //la letra actual, tenemos un ganador!
                
if (freq >letter.iniRange && freq <letter.endRange)
                        nuLetter 
letter;
            
}
            
//Pero sólo si es del tipo que necesitamos  
        
while (nuLetter.Type !type);
        return 
nuLetter.Value;
    
}

    
/// <summary>
    /// Simple comprobación booleana para ver si una letra determinada es
    /// una vocal o una consonante
    /// </summary>
    /// <param name="checkLetter">La letra a comprobar</param>
    /// <returns>True si es una vocal, false en cualquier otro caso</returns>
    
static bool IsAVowel(string checkLetter)
    {
        
switch(checkLetter.ToLower())
        {
            
case "a":
            
case "e":
            
case "i":
            
case "o":
            
case "u":
                
return true;
            default
:
                
return false;
        
}
    }

    
/// <summary>
    /// Gracias del departamento de Informática de la Universidad de Bristol
    /// (y a Google) he encontrado una lista de todas las letras del
    /// alfabeto inglés, con su frecuencia de uso.
    /// Como Random maneja enteros, he multiplicado todos estos valores * 100
    /// La suma de todos ellos debería ser 10.000, lo que hace que cuando calculamos
    /// el rango de números debería ser 10.000 + la cantidad de letras posibles;  
    /// porque el rango de la primera letra es de 0 a 1.231, y el siguiente  
    /// rango de letra empezará en 1.232 (el fin del anterior rango +1)
    /// En el caso de letras solas esto es correcto, y los rangos van desde
    /// 0 a 10.025. 
    /// 
    /// Pero, no sé porqué, en el caso de los digramas el rango va  
    /// de 0 a 5.548. El porcentaje de frecuencia de todos los digramas disponibles,
    /// todos ellos multiplicados por 100 y sumados NO da 10.000 como debiera.
    /// ¿Alguien sabe porqué? A no ser que la lista de digramas que tengo NO esté completa.
    /// </summary>
    
static void LoadLetters()
    {
        
// http://www.cs.bris.ac.uk/Teaching/Resources/COMS30124/Labs/freq.html
        // Porcentaje de frecuencia de letras
        //
        //  E 12.31      L 4.03     B 1.62
        //  T  9.59      D 3.65     G 1.61
        //  A  8.05      C 3.20     V 0.93
        //  O  7.94      U 3.10     K 0.52
        //  N  7.19      P 2.29     Q 0.20
        //  I  7.18      F 2.28     X 0.20
        //  S  6.59      M 2.25     J 0.10
        //  R  6.03      W 2.03     Z 0.09
        //  H  5.14      Y 1.88      

        
string[] _letterValue 
        
{
            
"e""l""b""t""d""g""a""c""v",
            
"o""u""k""n""p""q""i""f""x",
            
"s""m""j""r""w""z""h""y"
        
};
        int
[] _letterFreq 
        
{
            
123140316295936516180532093
            
794310527192292071822820,
            
659225106032039514188
        
};
        int 
lastEndRange 0;
        
//Recorriendo los arrays rellenados con esos datos
        //guardamos una colección de objetos Letra llamada AllLetters, 
        //asignando a cada Letra su rango, valor y tipo
        
for (int 0i < _letterFreq.Lengthi++)
        {
            Letter nuLetter 
= new Letter();
            
nuLetter.iniRange lastEndRange;
            
nuLetter.endRange lastEndRange + _letterFreq[i];
            
lastEndRange nuLetter.endRange + 1;
            
nuLetter.Value _letterValue[i];
            
nuLetter.Type IsAVowel(_letterValue[i]) ? 
                LetterType.Vowel : LetterType.Consonant
;
            
AllLetters.Add(nuLetter);
        
}
    }
     
    
/// <summary>
    /// Ver el comentario de LoadLetters
    /// </summary>
    
static void LoadDigrams()
    {
        
// http://www.cs.bris.ac.uk/Teaching/Resources/COMS30124/Labs/freq.html
        // Digramas ingleses y su porcentaje de frecuencia de uso
        //  TH  3.15   TO  1.11   SA  0.75   MA  0.56 
        //  HE  2.51   NT  1.10   HI  0.72   TA  0.56
        //  AN  1.72   ED  1.07   LE  0.72   CE  0.55
        //  IN  1.69   IS  1.06   SO  0.71   IC  0.55
        //  ER  1.54   AR  1.01   AS  0.67   LL  0.55
        //  RE  1.48   OU  0.96   NO  0.65   NA  0.54
        //  ES  1.45   TE  0.94   NE  0.64   RO  0.54
        //  ON  1.45   OF  0.94   EC  0.64   OT  0.53
        //  EA  1.31   IT  0.88   IO  0.63   TT  0.53
        //  TI  1.28   HA  0.84   RT  0.63   VE  0.53
        //  AT  1.24   SE  0.84   CO  0.59   NS  0.51
        //  ST  1.21   ET  0.80   BE  0.58   UR  0.49
        //  EN  1.20   AL  0.77   DI  0.57   ME  0.48
        //  ND  1.18   RI  0.77   LI  0.57   WH  0.48
        //  OR  1.13   NG  0.75   RA  0.57   LY  0.47 

        
string[] _digramValue 
        
{   
            
"th""to""sa""ma""he""nt""hi""ta",
            
"an""ed""le""ce""in""is""so""ic",
            
"er""ar""as""ll""re""ou""no""na",
            
"es""te""ne""ro""on""of""ec""ot",
            
"ea""it""io""tt""ti""ha""rt""ve",
            
"at""se""co""ns""st""et""be""ur",
            
"en""al""di""me""nd""ri""li""wh",
            
"or""ng""ra""ly"
        
};
        int
[] _digramFreq 
        
{
            
31511175562511107256,
            
17210772551691067155
            
1541016755148966554
            
145946454145946453
            
131886353128846353
            
124845951121805849
            
120775748118775748
            
113755747
        
};
        int 
lastEndRange 0;
        
//Recorremos los arrays de datos para rellenar
        //la colección de objetos Digram AllDigrams
        //asignándole a cada uno su rango,
        //valor y tipo de digrama
        
for (int 0i < _digramFreq.Lengthi++)
        {
            Digram nuDigram 
= new Digram();
            
nuDigram.iniRange lastEndRange;
            
nuDigram.endRange lastEndRange + 
                _digramFreq[i]
;
            
lastEndRange nuDigram.endRange + 1;
            
nuDigram.Value _digramValue[i];
            if 
(IsAVowel(_digramValue[i].Substring(01)) && 
                IsAVowel(_digramValue[i].Substring(
11)))
                    nuDigram.Type 
DigramType.TwoVowels;
            else if 
(!IsAVowel(_digramValue[i].Substring(01)) && 
                IsAVowel(_digramValue[i].Substring(
11)))
                    nuDigram.Type 
DigramType.ConsonantAndVowel;
            else if 
(IsAVowel(_digramValue[i].Substring(01)) && 
                !IsAVowel(_digramValue[i].Substring(
11)))
                    nuDigram.Type 
DigramType.VowelAndConsonant;
            else 
nuDigram.Type DigramType.TwoConsonants;
            
AllDigrams.Add(nuDigram);
        
}
    }
#endregion
}

Colorized by: CarlosAg.CodeColorizer

Actualización 13/01/2007.- Traducidos los comentarios del código fuente al español. Éste es un ejemplo de los apellidos que se generan:
  • Andaso
  • Estecon
  • Ovloso
  • Eleasawh
  • Athene
  • Tertore
  • Setolyiis
  • Face
  • Lirsuco
  • Rofsender

Comprimiendo y descomprimiendo ficheros ZIP

Published 78 weeks, 1 day ago
Thu Jan 04 2007
Para comprimir y descomprimir ficheros en formato ZIP tenemos una herramienta muy valiosa a nuestra disposición: la excelente librería SharpZipLib, creada por IC#Code, el mismo grupo de desarrolladores que nos han proporcionado el excelente IDE Open Source para C# SharpDevelop, del que ya os he hablado otras veces.

Lo primero que tenemos que hacer, por lo tanto, es descargar dicha librería a nuestro sistema. Una vez hecho esto, debemos incluirla como referencia en el proyecto de Visual Studio en el que vayamos a usarla. Si creemos que vamos a usarla con relativa frecuencia, lo más cómodo es instalarla en el GAC. Para ello, abrimos la Ventana de comandos de Visual Studio 2005, que encontraremos bajo Herramientas del grupo del menú de Inicio Visual Studio 2005. Cuando lo ejecutamos, se abre una consola que deberemos dirigir a la ruta donde hayamos descargado la librería ICSharpCode.SharpZipLib.dll (por comodidad, yo la tengo en una carpeta propia en C:\Archivos de Programa) y una vez situados ahí escribimos el siguiente comando:

gacutil /i ICSharpCode.SharpZipLib.dll

De esta manera añadimos la librería a la caché global de ensamblados para Visual Studio.

Tanto si añadimos la librería al GAC como si no, debemos referenciarla en nuestro proyecto. Para ello, ya sabéis, una vez creado nuestro proyecto (de consola, Windows o Web) elegimos el menú Proyecto, Añadir Referencia y elegimos la librería en el cuadro de diálogo que nos aparece: en la solapa llamada .NET si hemos incluido la librería en el GAC, o podemos buscarla mediante la solapa Examinar. Una vez incluida la referencia, incluimos la línea

using ICSharpCode.SharpZipLib.Zip;

en nuestro código y ya estamos listos.
Una última aclaración antes de meternos en faena: para hacer más simple este artículo he decidido que los ejemplos de código incluidos funcionen en la consola y he dado por supuestas varias cosas, como rutas y nombres de ficheros. Si quieres que el código fuente te funcione no te limites a cortarlo y pegarlo, debes adaptarlo a las rutas y nombres de ficheros existentes en tu sistema.

Listar Contenido de Ficheros

Vamos a ver primero cómo listar el contenido de un fichero ZIP. Para ello, nos limitamos a declarar una variable llamada zip de tipo ZipFile. Nótese que SharpZipLib hace que un ZipFile esté compuesto de objetos ZipEntry, no de ficheros ni de líneas ni de ninguna otra cosa por el estilo. El objeto ZipEntry va a representar cualquiera de las entidades que pueden estar contenidas en un fichero ZIP, sean un fichero o un directorio. Una vez sabido esto, nos recorremos las entradas del fichero ZIP mediante un bucle foreach y sencillamente mostramos el nombre de la entrada en la consola:

private static void ListarContenidoZip(string sFile) 
{
    ZipFile zip 
= new ZipFile(File.OpenRead(sFile));
    foreach
(ZipEntry entry in zip) 
    {
        Console.WriteLine(entry.Name)
;
    
}
}

Colorized by: CarlosAg.CodeColorizer

Descomprimir Ficheros

La trama se complica. Para descomprimir un fichero ZIP lo abriremos como un objeto ZipInputStream, recorremos sus entradas mediante el método GetNextEntry() y guardaremos el contenido binario de cada entrada en un array de bytes. Dicho array alimentará a un objeto StreamWriter, con el cual crearemos el nuevo fichero, que será una copia exacta de los datos binarios de la entrada del fichero ZIP.

private static void DescomprimirZip(string sFile)
{
    ZipInputStream zipIn 
= new ZipInputStream(File.OpenRead(sFile));
    
ZipEntry entry;
    while 
((entry zipIn.GetNextEntry()) != null)
    {
        FileStream streamWriter 
File.Create(@"C:\Temp\" + entry.Name);
        long 
size entry.Size;
        byte
[] data = new byte[size];
        while 
(true)
        {
            size 
zipIn.Read(data, 0, data.Length);
            if 
(size > 0) streamWriter.Write(data, 0, (int) size);
            else break;
        
}
        streamWriter.Close()
;
    
}
    Console.WriteLine(
"Hecho!!");
}

Colorized by: CarlosAg.CodeColorizer

Comprimir ficheros

Para comprimir ficheros, usaremos un objeto similar al anterior pero de propósito opuesto, el ZipOutputStream. Para mantener la sencillez del ejemplo, he supuesto que queremos comprimir todos los ficheros existentes en la ruta pasada en el parámetro sRuta. Por lo tanto, nos recorremos todos los ficheros existentes en dicha ruta, creamos un nuevo objeto ZipEntry, obtenemos cierta información mediante FileInfo (podríamos haber incluido chequeo de CRC, pero quería mantener el ejemplo sencillo) para asignar dicha información a la nueva entrada, y añadimos la entrada al objeto ZipOutputStream mediante el método PutNextEntry. Ojo, esto crearía una entrada vacía: para realmente introducir los datos que contiene el fichero (sea un fichero de texto o un binario cualquiera) debemos leer sus datos binarios mediante un FileStream como en el ejemplo anterior y entonces invocar el método Write del ZipOutputStream para guardar dichos datos binarios en la última entrada del stream de salida Zip. Una vez recorridos e incluidos todos los ficheros, terminamos y cerramos el objeto ZipOutputStream.

private static void ComprimirZip(string sRuta) 
{
    ZipOutputStream zipOut 
= new ZipOutputStream(File.Create(@"C:\Temp\prueba.zip"));
    foreach
(string fName in Directory.GetFiles(sRuta))
    {
        FileInfo fi  
= new FileInfo(fName);
        
ZipEntry entry = new ZipEntry(fi.Name);
        
FileStream sReader File.OpenRead(fName);
        byte
[] buff = new byte[Convert.ToInt32(sReader.Length)];
        
sReader.Read(buff, 0, (int) sReader.Length);
        
entry.DateTime fi.LastWriteTime;
        
entry.Size sReader.Length;
        
sReader.Close();
        
zipOut.PutNextEntry(entry);
        
zipOut.Write(buff, 0, buff.Length);
    
}
    zipOut.Finish()
;
    
zipOut.Close();
}

Colorized by: CarlosAg.CodeColorizer

Y esto concluye esta pequeña introducción a SharpZipLib. IC#Code mantiene un excelente foro (en inglés) en el que podréis ver más ejemplo de código y más dudas resueltas.

Actualización 5/01/2007.- Jugando un poco con la librería y el Examinador de Objetos de VS 2005, me encuentro con una clase llamada FastZip. Hmmmm. Pues sí, es lo que parece: una clase wrapper para facilitar la compresión y descompresión de archivos mediante SharpZipLib. Por ejemplo, para descomprimir un fichero es tan simple como esto:

private static void DescomprimirZipFast(string sFile)
{
    FastZip fZip 
= new FastZip();
    
fZip.ExtractZip(sFile, @"C:\Temp""");
}

Colorized by: CarlosAg.CodeColorizer
Los tres argumentos del método ExtractZip son de tipo string, y son el nombre del fichero ZIP a extraer, la ruta de destino para los ficheros extraídos y una máscara para el tipo de ficheros que debemos extraer. Ojo, no vale una máscara al estilo "*.txt", debe hacerse mediante una expresión regular. Si queremos incluir todos los archivos existentes en el directorio, debemos dejar este último parámetro como null o como cadena vacía "".

Y para comprimir:

private static void ComprimirZipFast()
{
    FastZip fZip 
= new FastZip();
    
fZip.CreateZip(@"C:\Temp\walls.zip"@"C:\Wallpapers"false".jpg$");
    
Console.WriteLine("Hecho!!");
}

Colorized by: CarlosAg.CodeColorizer
En este caso el método CreateZip admite cuatro argumentos: el nombre y ruta del fichero ZIP a crear, el directorio donde se encuentran los ficheros que queremos comprimir, un parámetro booleano que indica si la compresión es recursiva (incluimos subdirectorios) o no, y por último un parámetro de tipo string que contiene la máscara para elegir el tipo de ficheros que queremos a comprimir. En este caso es todos los ficheros con extensión JPG. Se aplican las mismas excepciones en uso de máscaras que he comentado para ExtractZip.