lunes, 8 de junio de 2009

Archivos y flujos

Jerarquía de datos
Básicamente, todos los elementos de datos que procesan las computadoras se reducen a combinaciones de ceros y unos. Esto ocurre debido a que es simple y económico el construir dispositivos electrónicos que puedan suponer dos estados estables: un estado representa 0 y el otro, 1. Es increíble que las impresionantes funciones realizadas por las computadotas impliquen solamente las manipulaciones más fundamentales de Os y ls.

El elemento más pequeño de datos que soporta las computadoras es el bit (abreviatura de "dígito binario : un dígito que puede asumir uno de dos valores). Cada elemento de datos, o bit, puede asumir ya sea el valor 0 o 1. Los circuitos de la computadora realizan varias manipulaciones simples de bits, cómo examinar el valor de un bit. establecer el valor de un bit e invertir un bit (de 1 a 0 o de 0 a 1).

Es muy difícil para los programadores trabajar con los datos en el formato de bits de bajo nivel. Es preferible programar con datos en formatos como dígitos decimales (es decir, 0, 1, 2, 3, 4, 5, 6, 7, 8 y 9), letras (es decir, A-Z y a-z) y símbolos especiales (es decir, S, @, %, &, C ), -, +, ", :,?,/ y muchos otros). Los dígitos, las letras y ios símbolos especiales se conocen como caracteres. El conjunto de caracteres de una computadora es el conjunto de todos los caracteres que se utilizan para escribir programas y representar elementos de datos en una computadora específica. Debido a que las computadoras sólo pueden procesar Os y ls, cada carácter en el conjunto de caracteres de una computadora se representa como un patrón de Os y ls. Los bytes están compuestos de ocho bits. C# utiliza el conjunto de caracteres Unicode- (www. uni code. org), en el cual los caracteres están compuestos de 2 bytes. Los programadores crean programas y elementos de datos con los caracteres; las computadoras manipulan y procesan estos caracteres como patrones de bits.

Un campo es un grupo de caracteres que transmiten cierto significado. Por ejemplo, un campo que consiste de letras mayúscu­las y minúsculas puede representar el nombre de una persona.
Los elementos de datos procesados por las computadoras forman una jerarquía de datos, en la cual los elementos de datos se hacen más grandes y complejos en estructura, a medida que progresamos de bits a caracteres, de caracteres a campos y de campos hasta llegar a conjuntos de datos más grandes. Por lo general un registro esta compuesto por varios campos relacionados.


Archivos y flujos
C# considera a cada archivo como un flujo secuencial de bytes. Cada archivo termina ya sea con un marcador de fin de archivo, o en un número específico de byte que se registra en una estructura de datos adminis­trativa, mantenida por el sistema. Cuando se abre un archivo, se crea un objeto que se asocia con un flujo. Cuando se ejecuta un programa, el entorno en tiempo de ejecución crea tres objetos flujo, a los cuales se puede acceder mediante las propiedades Console.Out, Console.In y Consolé. Error, respectivamente. Estos objetos facilitan la comunicación entre un programa y un archivo o dispositivo específico. Console.In se refiere al objeto flujo de entrada estándar, el cual permite á un programa introducir datos desde el teclado. Consol e.Out se refiere al objeto flujo de salida estándar, el cual permite a un programa imprimir datos en la pantalla. Console. Error se refiere al objeto flujo de error estándar, el cual permite a un programa imprimir mensajes de error en la pantalla.Console.Out y Console.In en consola; los métodos Write y WriteLine utilizan Console.Out para las operaciones de salida, y los métodos Read y ReadLine de Console utilizan Console. In para las operaciones de entrada.

Las clases File y Directory
La información se almacena en archivos, que se organizan en directorios. Las clases File y Directory permiten a los programas manipular archivos y directorios en el disco. La clase File puede determinar información acerca de los archivos y puede usarse para abrir archivos en modo de lectura o de escritura. En las secciones subsiguientes hablaremos sobre las técnicas para escribir hacia, y leer desde, archivos.

Metodos para acceder a la informacion de archivos y directorios:

AppendText: Devuelve un objeto StreamWriter que adjunta texto a un archivo existente, o crea un archivo si no existe.

Copy: Copia un archivo a un archivo nuevo.

Create: Crea un archivo y devuelve su objeto FileStream asociado.

CreateText: Crea un archivo de texto y devuelve su objeto StreamWriter asociado.

Delete: Elimina el archivo especificado.

Exists: Devuelve true si existe el archivo especificado, y false en caso contrario.

GetCreationTime: Devuelve un objeto DateTime que representa la fecha y hora en la que se creó el archivo.

CetLastAccessTime: Devuelve un objetcrDateTime que representa la fecha y hora del último acceso al archivo.

GetLastWriteTime: Devuelve un objeto DateTime que representa la fecha y hora de la última modificación del archivo.

Move:Mueve el archivo especificado a una ubicación especificada.

Open: Devuelve un objeto Fi 1 eStream asociado con el archivo especificado y equipado con los permisos de lectura/escritura especificados.

OpenRead:Devuelve un objeto FileStream de sólo lectura, asociado con el archivo especificado.

OpenText: Devuelve un objeto StreamReader asociado con el archivo especificado.

OpenWrite: Devuelve un objeto FileStream de lectura/escritura, asociado con el archivo especificado.

CreateDirectory:Crea un directorio y devuelve su objeto Directorylnfo asociado

Delete:Elimina el directorio especificado.

Exists:Devuelve true si existe el directorio especificado y false en caso contrario.

CetDi rectories:Devuelve un arreglo string que contiene los nombres de los subdirectorios en el directorio especificado.

CetFiles:Devuelve un arreglo string que contiene los nombres de los archivos en el directorio especificado.

CetCreationTime:Devuelve un objeto DateTime que representa la fecha y hora de creación del directorio.

CetLastAccessTime:Devuelve un objeto DateTime que representa la fecha y hora del último acceso al directorio.

CetLastWri teTime:Devuelve un objeto DateTime que representa la fecha y hora en que se escribieron los últimos elementos en el directorio.

Move:Mueve el directorio especificado a una ubicación especificada.


Creación de un archivo de texto de acceso secuencial
no impone una estructura en los archivos. Por ende, el concepto de un "registro" no existe en los archivos de C#. Esto significa que usted debe estructurar los archivos para cumplir con los requerimientos de sus aplica­ciones. En el siguientes ejemplo, se usara texto y caracteres especiales para organizar un propio concepto de un "registro".

El siguientee ejemplo demuestran el procesamiento de archivos en una aplicación de mantenimiento de cuentas bancarias. Estos programas tienen interfaces de usuario similares, por lo que creamos la clase reutilizable Banco-UlForm para encapsular la GUI de una clase base. La clase BancoUIForm contiene cuatro controles Label y cuatro controles TextBox. Los métodos BorrarControlesTextBox, EstablecerValoresControlesTextBox v ObtenerValoresControlesTextBox borran, establecen los valores de, y obtienen los valores del texto en los controles TextBox, respectivamente.

1 // Fig. 18.7: BancoUIForm.cs
2 // Un formulario Windows Form reutilizable para los ejemplos en este capitulo.
3 using System;
4 using System.Windows.Forms; 5

6 public partial class BancoUIForm : Form
7 {
8 protected int CuentaTextBox = 4; // número de controles TextBox en el formulario 9
10 // las constantes de la enumeración especifican los Índices de los controles TextBox
11 public enum IndicesTextBox
12 {

13 CUENTA,
14 NOMBRE,
15 APELLIDO,
16 SALDO
17 } // fin de enum
18
19 // constructor sin parámetros
20 public BancoUIForm()
21 {
22 InitializeComponentO;
23 } // fin del constructor24
25 // borra todos los controles TextBox
26 public void BorrarControlesTextBox()
27 {

28 // itera a través de cada Control en el formulario
29 for ( int i = 0; i < Controls.Count; i++ )
30 {
31 Control miControl = Controls[ i ]; // obtiene el control32
33 // determina si el Control es TextBox
34 if ( miControl is TextBox ) « {

36 // borra la propiedad Text ( la establece a una cadena vacía )
37 miControl.Text = "";
38 } // fin de if
39 } // fin de for
40 } // fin del método BorrarControlesTextBox
41
42 // establece los valores de los controles TextBox con el arreglo string valores
43 public void EstablecerValoresControlesTextBox( stringí] valores )
44 {
45 // determina si el arreglo string tiene la longitud correcta
46 if ( valores.Length != CuentaTextBox )
47 {
48 // lanza excepción si no tiene la longitud correcta throvjf nevé ArgumentException ( "Debe haber " +
50 ( CuentaTextBox + 1 ) + " objetos string en el arreglo" ) );
51 } // fin de if
52 // establece el arreglo valores si el arreglo tiene la longitud correcta
53 else
54 {
55 // establece el arreglo valores con los valores de los controles TextBox
56 CuentaTextBox.Text = valores[ ( int ) IndicesTextBox.CUENTA ]; 57 primerNombreTextBox.Text = valores[ ( int ) IndicesTextBox.NOMBRE ]; 58 apellidoPaternoTextBox.Text ■ valores[ ( int ) IndicesTextBox.APELLIDO ]; 59saldoTextBox.Text = valores[ ( int ) IndicesTextBox.SALDO ];
60 } // fin de else
61 } // fin del método EstablecerValoresControlesTextBox
62
63 // devuelve los valores de los controles TextBox como un arreglo string
64 public stringQ ObtenerValoresControlesTextBoxC)
65 {
66 string[~] valores = new stringl CuentaTextBox ];
67
68 // copia los campos de los controles TextBox al arreglo string
69 valores[ ( int ) IndicesTextBox.CUENTA ] = CuentaTextBox.Text;
70 valores[ ( int ) IndicesTextBox.NOMBRE ] = primerNombreTextBox.Text;
71 valores[ ( 7'nt ) Indi cesTextBox. APELLIDO ] = apellidoPaternoTextBox.Text;
72 valores[ ( int ) IndicesTextBox.SALDO ] = saldoTextBox.Text;
73
74 çreturn valores;
75 } // fin del método ObtenerValoresControlesTextBox
76 }// fin de la clase BankilIForm


Lectura de ciatos desde un archivo de texto de acceso secuencial
En el subtitulo anterioir se demostró cómo crear un archivo para usarlo en aplicaciones con acceso secuencial. En esta parte se verá cómo leer (o recuperar) datos de un archivo en forma secuencial.
Cuando el usuario hace clic en el botón Abrir archivo, el programa llama al manejador de eventos abrir-Button_Click (líneas 20-50). La línea 23 crea un cuadro de diálogo OpenFileDialog, y la línea 24 llama a su método ShowDialog para mostrar el cuadro de diálogo Abrir. El comportamiento y la GUI para los tipos de cuadros de diálogo Guardar y Abrir es idéntico, excepto que Guardar se sustituye por Abrir. Si el usuario introduce un nombre de archivo válido, las líneas 41-42 crean un objeto Filestream y lo asignan a la referencia de entrada.


1 // Fig. 18.11: LeerArchivoAcceso5ecuencialForm.es
2 // Lectura de un archivo de acceso secuencial.
3 using System;
4 using System.Windows.Forms;
5 using System.10;
6 using BibliotecaBanco;
7
8 public partial class LeerArchivoAccesoSecuencialForm : BancoUIForm
9 {
10 private FileStream entrada; // mantiene la conexión a un archivo
11 private StreamReader archivoReader; // 7ee datos de un archivo de texto
12
13 // constructor sin parámetros
14 publ ic LeerArchi voAccesoSecuenci al FormO
15 {
16 InitializeComponentO ;
17 } // fin del constructor
18
19 // se invoca cuando el usuario hace clic en el botón Abrir
20 private void abrirButton_Click( object sender, EventArgs e )
21 {
22 // crea un cuadro de diálogo que permite al usuario abrir el archivo
23 OpenFileDialog selectorArchivo = new OpenFileDialog();
24 DialogResult resultado = selectorArchivo.ShowDialogO ;
25 string nombreArchivo; // nombre del archivo que contiene los datos
26
27 // sale del manejador de eventos si el usuario hace clic en Cancelar
28 if ( resultado == DialogResult.Cancel )
29 return;
30
31 nombreArchivo = selectorArchivo.FileName; //obtiene el nombre de archivoespecificado
32 BorrarControlesTextBoxO ;
33
34 // muestra error si el usuario especifica un archivo inválido
35 if ( nombreArchivo == "" nombreArchivo == nuil )
36 MessageBox.Show( "Nombre de archivo inválido", "Error",
37 MessageBoxButtons.OK, MessageBoxIcon.Error );
38 e7se
39 {
40 // crea objeto FileStream para obtener acceso de lectura al archivo
41 entrada = new FileStream( nombreArchivo, FileMode.Open,
42 FileAccess.Read );
43
44 // establece el archivo del que se van a leer los datos
45 archivoReader = new StreamReader( entrada );
46
47 abrirButton.Enabled = fa7se; // deshabilita el botón Abrir archivo
48 siguienteButton.Enabled = true; // habilita el botón Siguiente registro
49 } // fin de el se
50 } // fin del método abrirButton_Click
51
52 // se invoca cuando el usuario hace clic en el botón Siguiente
53 private void siguienteButton_Click( object sender, EventArgs e )
54 {
55 try
56 {
57 // obtiene el siguiente registro disponible en el archivo
58 string regi stroEntrada = archi voReader. ReadLineO ;
59 stringQ camposEntrada; // almacena piezas individuales de datos 60
61 if ( registroEntrada != nuil )
62 {
63 camposEntrada = registrcEntrada.SplitC ',' );
64
65 Registro registro = new RegistroC
66 Convert.ToInt32( camposEntrada[ 0 ] ), camposEntrada[ 1 ],
67 camposEntrada[ 2 ], Convert.ToDecimal( camposEntrada[ 3 ] ) );
68
69 // copia los valores del arreglo string a los valores de los controles
TextBox
70 EstablecerValoresControlesTextBox( camposEntrada );
71 } // fin de if
72 e7se
73 {
74 archivoReader .CloseO; // cierra StreamReader
75 entrada.Close(); // cierra FileStream si no hay registros en el archivo
76 abrirButton.Enabled = true; // habilita el botón Abrir archivo
77 siguienteButton.Enabled = false; // deshabilita el botón Siguiente registro
78 BorrarControlesTextBoxC);
80 // notifica al usuario si no hay registros en el archivo
81 MessageBox.Show( "No hay más registros en el archivo", '"
82 MessageBoxButtons.OK, MessageBoxIcon.Information );
83 } // fin de el se
84 } // fin de try
85 catch ( IOException )
86 {
87 MessageBox.Show( "Error al leer del archivo", "Error"
88 MessageBoxButtons.CK, MessageBoxIcon.Error );
89 } // fin de catch
90 } // fin del método siguienteButton_Click
91 } // fin de 7a clase LeerArchivoAccesoSecuencialForm


Señalización
En un subtitulo anterior creacion de un archivo se demostró cómo escribir los campos individuales de un objeto Registro en un archivo de texto, y la sección siguiente lectura de datos se demostró cómo leer esos campos de un archivo y colocar sus valores en un objeto Regi stro en memoria. En los ejemplos. Registro se utilizó para juntar la información para un solo registro. Cuando las varia­bles de instancia para un Regi stro se enviaron a un archivo en disco, se perdió cierta información como el tipo de cada valor. Por ejemplo, si se lee el valor "3" de un archivo, no hay forma de saber si el valor proviene de un int, de un string o de un decimal. Sólo tenemos datos en el disco, no información sobre el tipo. Si el programa que va a leer estos datos "sabe" a qué tipo de objeto corresponden los datos, entonces pueden leerse directamente en objetos de ese tipo. Por ejemplo, si sabemos que introduciremos un int (el número de cuenta), seguido de dos objetos stri ng (el primer nombre y el apellido paterno) y un decimal (el saldo). También sabemos que estos valores están separados por comas, y sólo hay un registro en cada línea. Por lo tanto, podemos analizar los obje­tos string y convertir el número de cuenta en ún int y el balance en un decimal. Algunas veces sería más sencillo leer o escribir objetos completos. C# cuenta con un mecanismo así, al cual se le conoce como señalización de obje­tos. Un objeto serializíido se representa como una secuencia de bytes que incluye los datos de ese objeto, así como información acerca del tipo del objeto y los tipos de los datos almacenados en ese objeto. Una vez que se escribe un objeto serializado en un archivo, puede leerse desde ese archivo y deserializarse, esto es, la información sobre el tipo v los bytes que representan al objeto y sus datos pueden usarse para recrear al objeto en la memoria.


Creación de un archivo de acceso secuencialmediante el uso de la serialización de objetos
Se empieza por crear y escribir objetos serializados en un archivo de acceso secuencial. Definición de la clase RegistroSerial izable
En una clase marcada con el atributo [Seri al i zabl e] o que implementa la interfaz ISeri al izable, debe­mos asegurarnos que toda variable de instancia de la clase también sea serializable. Todas las variables de .tipos simples y los objetos string son serializables. Para las variables de tipos por referencia, hay que comprobar la declaración de la clase (y posiblemente sus clases basel para asegurarnos que el tipo sea serializable. Los objetos tipo arreglo son serializables de manera predeterminada. No obstante, si el arreglo contiene referencias a otros objetos, esos objetos podrían o no ser serializables.

Bibliografia:
Cómo Programar En C# 2a Edición
Harvey M. Deitel Y Paul J. Deitel (Pearson Educación)
ISBN: 9702610567. ISBN-13: 9789702610564(2007).