C#: Nombre de fichero aleatorio

Cuando escribes un post y tienes la responsabilidad de hacerlo en un lugar como puede ser geeks.ms, intentas que tenga un mínimo de calidad. En este caso sé que no es así, aunque no puedo evitar hacerlo, puesto que es un tema que me ha tocado las narices más de una vez.

En más de una ocasión me he encontrado con la necesidad de tener que tocar un fichero temporal, tanto en WinForms como en WebForms para, por ejemplo, descomprimir un fichero, tratar un documento en la web para descargarlo posteriormente… El caso es que, en un momento dado, necesitamos tener un nombre de fichero “aleatorio” para poder tratarlo de manera temporal. Nada más lejos de la realidad o, mejor dicho, nada más lejos de las librerías de .Net Framework, puesto que el propio Framework se encarga de, como casi siempre, solventarnos el problema…

El método GetRandomFileName() de la clase System.IO.Path nos permitirá tener un nombre de fichero aleatorio, el cual podremos usar sin tener que hacer uso de librerías propias extrañas para poder crear un fichero en una carpeta determinada.

Lo sé y pido disculpas por ello… No es un post con una calidad aceptable, pero creo que, de paso que puede servirme de recordatorio a mí, igual también tiene utilidad para aquellos a los que la memoria les falla tanto como a mí… ejemmm

string fileRandom = System.IO.Path.GetRandomFileName();

— Editado el día 28/2/11 para comentar lo siguiente:

En base a los comentarios, ¿qué se me ocurrió hacer?. Pues casi nada… Tan sólo dejar el ordenador almacenando nombres de ficheros generados por la función GetRandomFileName en una base de datos. Como no tenía mucha prisa, miraba, antes de la inserción a ver si existía. Resultado:

Dos millones de registros y no ha encontrado ningún duplicado. No es que la estadística sea mi fuerte, así que teniendo en cuenta que los nombres de los ficheros son de 8 caracteres más una extensión de 3,conteniendo letras y número, es fácil deducir que existen muchas más posibilidades que la cifra de dos millones, pero bueno, yo creo que ya ha llegado el momento de dejar de jugar con esto e intentar dedicarme a algo más serio… jeje

C#: Calcular Semana Santa

Siempre he pensado que todo programador, de vez en cuando necesitamos alguna ida de olla y, sencillamente, nos sentamos delante del ordenador más por entretenimiento que por necesidad. En mi caso por lo menos, cuando ando muy agobiado con el resto del entorno, es algo que me libera bastante. Claro, que suele liberarme mucho más salir a tomar unas cuantas cervezas con los amiguetes, pero como en este caso no tenía ganas de estar resacoso a la mañana siguiente, pues opté por hacer algo que me entretuviese un rato y que fuera más sano.

Uno de los últimos artículos que publiqué en mi blog anterior versaba sobre el cálculo de ciertos días relacionados con la Semana Santa. No es que el día que lo hice tuviese también una pájara, es que ya se me van acabando las ideas para los ejercicios que tengo que ir haciendo para los alumnos (y eso que el ejercicio era de Excel).

Bueno, nos dejaremos de charlas superfluas como si estuviésemos en la barra del bar y, ya que lo hice, lo dejo para todo aquel que quiera aprovecharlo o, sencillamente, quiera distraerse un rato como yo. El caso es que me entretuve en crear un calendario en el que se pueden ver resaltados los días de Semana Santa de los años comprendidos en el intervalo (1900,2100). He dejado una versión del calendario en la página http://velasco.biz/html/SemanaSanta/Default.aspx, no para hacer publicidad, puesto que es una página que actualizo casi cada año (quizás dos) sino para poder ver el ejemplo funcionando.

Para la creación del calendario, una página con un combo para poder seleccionar el año (Vale, sé que es un DropDownList, pero no sé dónde habré oído lo del Combo) y una tabla de servidor sin ninguna celda es suficiente (ya la rellenamos dinámicamente)

 

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Default.aspx.cs" 
    Inherits="JnSoftware.Samples.WebCalendar.Default" %>

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title>Semana Santa - Sample</title>
</head>
<body>
    <form 
            id="form1" 
            runat="server">
    <h1>Semana Santa</h1>
    <br />
    Selecciona el año:
    <asp:DropDownList 
            ID="lstAnios" 
            runat="server" 
            Width="100px" 
            AutoPostBack="True"
            OnSelectedIndexChanged="lstAnios_SelectedIndexChanged">
    </asp:DropDownList>
    <div>
        <asp:Table 
            runat="server" 
            ID="tbContenido">
        </asp:Table>
    </div>
    </form>
</body>
</html>

 

El code-behind de la página tampoco es que sea excesivamente complicado. Lo que hago es que, una vez cargado los años que sé calcular en el combo (método CargaComboAnios), tan sólo es cuestión de crear un objeto Calendar para cada uno de los meses del año (método CargaCalendarios).

Quizás, el método que más me costó, imagino que por no estar lo suficientemente despierto es el encargado de marcar los días que quería (CargaDiasSemanaSanta), puesto que me empeciné en hacer cosas raras al ver que los días de Semana Santa podían llegar a dividirse en dos meses diferentes (no pongo nada de lo que intenté porque realmente me avergüenza). Al final opté por la creación de un Array de fechas y un recorrido por el mismo, de esta manera, incluso, puede aprovecharse para añadir otras fechas.

 

using System;
using System.Web.UI.WebControls;
using JnSoftware.Calculos;

namespace JnSoftware.Samples.WebCalendar
{
    public partial class Default : System.Web.UI.Page
    {
        // Personalización del número de filas y columnas de la tabla.
        int columnas = 3;
        int filas = 4;

        #region Eventos del WebForm
        protected void Page_Load(object sender, EventArgs e)
        {
            if (!IsPostBack)
            {
                cargaComboAnios();
                cargaCalendarios();
            }
        }

        protected void lstAnios_SelectedIndexChanged(object sender, EventArgs e)
        {
            DropDownList combo = (DropDownList)sender;
            if (combo.SelectedValue != null)
                cargaCalendarios();
        }
        #endregion

        /// <summary>
        /// Realiza la carga de los años en el combo.
        /// </summary>
        private void cargaComboAnios()
        {
            DropDownList combo = lstAnios;
            for (int i = 1900; i <= 2100; i++)
                combo.Items.Add(
                    new ListItem(
                        text: i.ToString("#,#"),
                        value: i.ToString()
                    )
                );
            // Selección del año actual
            combo.SelectedValue = DateTime.Today.Year.ToString();
        }

        /// <summary>
        /// Realiza la carga de los calendarios en la tabla.
        /// </summary>
        private void cargaCalendarios()
        {
            Table tabla = tbContenido;
            tabla.Rows.Clear();

            // Formato de la tabla
            tabla.CellSpacing = 5;

            // Bucle de filas
            int mes = 1;
            for (int f = 0; f < filas; f++)
            {
                TableRow tr = new TableRow();

                // Bucle de celdas
                for (int c = 0; c < columnas; c++)
                {
                    TableCell tc = new TableCell();
                    tc.Controls.Add(GetCalendar(mes++));
                    tr.Cells.Add(tc);
                }
                tabla.Rows.Add(tr);
            }

            /* Cargados los calendarios, se marcan los días de 
             * semana santa */
            cargaDiasSemanaSanta();
        }

        /// <summary>
        /// Obtiene un objeto Calendar del mes pasado como parámetro.
        /// Este objeto Calendar se agregará a la tabla.
        /// </summary>
        private Calendar GetCalendar(int mes)
        {
            int anio = int.Parse(lstAnios.SelectedValue);

            Calendar c = new Calendar();
            // Id del control
            c.ID = "mes" + mes.ToString("00");
            // Selección del mes
            c.VisibleDate = new DateTime(anio, mes, 1);
            // Se desactiva la navegación
            c.ShowNextPrevMonth = false;
            // Sólo se muestra el mes en el encabezado.
            c.TitleFormat = TitleFormat.Month;
            // Desactivamos la selección
            c.SelectionMode = CalendarSelectionMode.None;
            // Pongamos la firma
            c.ToolTip = "JnSoftware - Calendario de "
                    + c.VisibleDate.ToString("MMM/yy");
            return c;
        }

        /// <summary>
        /// Marca los días de semana santa en el calendario
        /// </summary>
        private void cargaDiasSemanaSanta()
        {
            int anio = int.Parse(lstAnios.SelectedValue);

            SemanaSanta s = new SemanaSanta(anio);
            DateTime JuevesSanto = s.JuevesSanto;

            /* Tomamos como fiestas de semana Santa desde el 
             * jueves hasta el lunes */
            DateTime[] fechasSemanaSanta = new DateTime[5];
            for (int i = 0; i < 5; i++)
                fechasSemanaSanta[i] = JuevesSanto.AddDays(i);

            /* Se marcan los días en los calendarios */
            Table tabla = tbContenido;
            foreach (DateTime dia in fechasSemanaSanta)
            {
                int mes = dia.Month - 1;
                int fila = mes / columnas;
                int columna = mes % columnas;

                Calendar calendar = 
                    tabla.Rows[fila].Cells[columna].Controls[0] as Calendar;
                calendar.SelectedDates.Add(dia);
            }
        }
    }
}
 
 
Al final, este ha sido el resultado. Lo que no llegué a conseguir es la posibilidad de incluir algún tipo de ToolTip a las fechas añadidas para poder insertar el día de la fiesta o fecha señalada (si algún alma caritativa ha logrado algo similar, siempre sería de agradecer cualquier comentario).
Bueno, hasta aquí el post actual. Incluyo también la clase que ya publiqué en su día para el cálculo de los días de la Semana Santa, así queda todo como más ordenado.
 
using System;

namespace JnSoftware.Calculos
{

    /// <summary>
    /// Cálculo de Semana Santa
    /// </summary>
    public class SemanaSanta
    {

        #region Campos 
        private int a;
        private int b;
        private int c;
        private int d;
        private int e;
        private DateTime pascuaResurreccion;

        private int anio;

        #endregion

        /// <summary>
        /// Constructor de la clase
        /// </summary>
        /// <param name="anio">Entero que representa el año del que 
        /// se quiere calcular la semana santa.</param>
        /// <exception cref="ArgumentOutOfRangeException">
        /// Se produce cuando se intenta calcular
        /// la semana santa de un año no contemplado.</exception>
        public SemanaSanta(int anio)
        {
            try
            {
                this.anio = anio;
                calculaDomingoPascua();
            }
            catch { throw; }
        }

        /// <summary>
        /// Cálculo del domingo de Pascua o domingo de Resurrección.
        /// </summary>
        private void calculaDomingoPascua()
        {
            ParConstantes p = getPar(anio);
            a = anio % 19;
            b = anio % 4;
            c = anio % 7;
            d = (19 * a + p.M) % 30;
            e = (2 * b + 4 * c + 6 * d + p.N) % 7;

            if (d + e < 10)
                pascuaResurreccion = new DateTime(anio, 3, d + e + 22);
            else
                pascuaResurreccion = new DateTime(anio, 4, d + e - 9);

            // Excepciones
            if (pascuaResurreccion == new DateTime(anio, 4, 26))
                pascuaResurreccion = new DateTime(anio, 4, 19);

            if (pascuaResurreccion == new DateTime(anio, 4, 25) 
                    && d == 28 && e == 6 && a > 10)
                pascuaResurreccion = new DateTime(anio, 4, 18);
        }

        #region Constantes cálculo
        private struct ParConstantes
        {
            public int M { get; set; }
            public int N { get; set; }
        }

        private ParConstantes getPar(int anio)
        {
            ParConstantes p = new ParConstantes();
            if (anio < 1583)
            {
                throw
                    new ArgumentOutOfRangeException
                        ("El año deberá ser superior a 1583");
            }
            else if (anio < 1700) { p.M = 22; p.N = 2; }
            else if (anio < 1800) { p.M = 23; p.N = 3; }
            else if (anio < 1900) { p.M = 23; p.N = 4; }
            else if (anio < 2100) { p.M = 24; p.N = 5; }
            else if (anio < 2200) { p.M = 24; p.N = 6; }
            else if (anio < 2299) { p.M = 25; p.N = 0; }
            else
            {
                throw
                    new ArgumentOutOfRangeException
                        ("El año deberá ser inferior a 2299");
            }
            return p;
        }
        #endregion

        #region Propiedades públicas

     

        public DateTime MiercolesCeniza
        {
            get { return SabadoSanto.AddDays(7 * -6 - 3); }
        }

        public DateTime ViernesDolores
        {
            get { return pascuaResurreccion.AddDays(-9); }
        }

        public DateTime DomingoRamos
        {
            get { return pascuaResurreccion.AddDays(-7); }
        }

        public DateTime JuevesSanto
        {
            get { return pascuaResurreccion.AddDays(-3); }
        }

        public DateTime ViernesSanto
        {
            get { return pascuaResurreccion.AddDays(-2); }
        }

        public DateTime SabadoSanto
        {
            get { return pascuaResurreccion.AddDays(-1); }
        }

        public DateTime DomingoResurreccion
        {
            get { return pascuaResurreccion; }
        }

        #endregion

    }
}

Validar un número de cuenta bancaria

En el blog anterior tenía una serie de utilidades que intentaré revisar y traspasar al actual. En esta ocasión le ha tocado el turno a un clásico: la validación de un número de cuenta bancaria:

using System;
using System.Text.RegularExpressions;

namespace JnSoftware.Validaciones
{

    /// <summary>
    /// Servicios de validación de las cuentas bancarias españolas
    /// </summary>
    public static class CuentasBancarias
    {

        /// <summary>
        /// Validación de una cuenta bancaria española
        /// </summary>
        /// <param name="banco">Código del banco en formato "0000"</param>
        /// <param name="oficina">Código de la sucursal en formato "0000"</param>
        /// <param name="dc">Dígito de control en formato "00"</param>
        /// <param name="cuenta">Número de cuenta en formato "0000000000"</param>
        /// <returns>true si el número de cuenta es correcto</returns>
        public static bool ValidaCuentaBancaria(string banco, string oficina, 
                                                        string dc, string cuenta)
        {
            /*
             * Se comprueba que realmente sean números los datos pasados 
             * como parámetros y que las longitudes sean correctas 
             */
            if (!IsNumeric(banco) || banco.Length != 4)
                throw new ArgumentException
                    ("El banco no tiene un formato adecuado");

            if (!IsNumeric(oficina) || oficina.Length != 4)
                throw new ArgumentException
                    ("La oficina no tiene un formato adecuado");

            if (!IsNumeric(dc) || dc.Length != 2)
                throw new ArgumentException
                    ("El dígito de control no tiene un formato adecuado");

            if (!IsNumeric(cuenta) || cuenta.Length != 10)
                throw new ArgumentException
                    ("El número de cuenta no tiene un formato adecuado");

            return CompruebaCuenta(banco, oficina, dc, cuenta);
        }

        /// <summary>
        /// Validación de una cuenta bancaria española
        /// </summary>
        /// <param name="cuentaCompleta">Número de cuenta completa con 
        /// carácteres numéricos y 20 dígitos</param>
        /// <returns>true si el número de cuenta es correcto</returns>
        public static bool ValidaCuentaBancaria(string cuentaCompleta)
        {
            // Comprobaciones de la cadena
            if (cuentaCompleta.Length != 20)
                throw new ArgumentException
                    ("El número de cuenta no el formato adecuado");

            string banco = cuentaCompleta.Substring(0, 4);
            string oficina = cuentaCompleta.Substring(4, 4);
            string dc = cuentaCompleta.Substring(8, 2);
            string cuenta = cuentaCompleta.Substring(10, 10);

            return ValidaCuentaBancaria(banco, oficina, dc, cuenta);

        }

        /// <summary>
        /// Validación de una cuenta bancaria española
        /// </summary>
        /// <param name="banco">Código del banco</param>
        /// <param name="oficina">Código de la oficina</param>
        /// <param name="dc">Dígito de control</param>
        /// <param name="cuenta">Número de cuenta</param>
        /// <returns>true si el número de cuenta es correcto</returns>
        public static bool ValidaCuentaBancaria(UInt64 banco, UInt64 oficina, 
                    UInt64 dc, UInt64 cuenta)
        {
            return ValidaCuentaBancaria(
                                banco.ToString("0000")
                                , oficina.ToString("0000")
                                , dc.ToString("00")
                                , cuenta.ToString("0000000000")
                                );
        }

        /// <summary>
        /// Comprueba que la cadena sólo incluya números
        /// </summary>
        /// <param name="numero">Cadena de texto en formato número</param>
        /// <returns>true si <paramref name="numero"/> contiene sólo números</returns>
        /// <remarks>No se contemplan decimales</remarks>
        private static bool IsNumeric(string numero)
        {
            Regex regex = new Regex("[0-9]");
            return regex.Match(numero).Success;
        }

        /// <summary>
        /// Una cuenta bancaria está validada si los dígitos de control 
        /// calculados coinciden con los que se han pasado en los argumentos
        /// </summary>
        private static bool CompruebaCuenta(string banco, string oficina, 
            string dc, string cuenta)
        {
            return GetDigitoControl("00" + banco + oficina) 
                + GetDigitoControl(cuenta) == dc;
        }

        /// <summary>
        /// Obtiene el dígito de control de una cuenta bancaria. 
        /// La función sólo devuelve un número que corresponderá a una de las dos opciones.
        ///     - Codigo + CódigoOficina
        ///     - CuentaBancaria
        /// </summary>
        private static string GetDigitoControl(string CadenaNumerica)
        {
            int[] pesos = { 1, 2, 4, 8, 5, 10, 9, 7, 3, 6 };
            UInt32 suma = 0;
            UInt32 resto;

            for (int i = 0; i < pesos.Length; i++)
            {
                suma += (UInt32)pesos[i] * 
                    UInt32.Parse(CadenaNumerica.Substring(i, 1));
            }
            resto = 11 - (suma % 11);

            if (resto == 10) resto = 1;
            if (resto == 11) resto = 0;

            return resto.ToString("0");
        }
    }
}