'''~+Tratamiento de archivos y canales de entrada/salida+~''' <> == == Para el tratamiento de archivos y canales de entrada/salida en general, GLib provee un tipo de dato llamado `GIOChannel`, que permite encapsular archivos, sockets y tuberías. Esta abstracción no sólo posibilita el acceso uniforme a todos estos recursos, sino que también asegura portabilidad e integración al bucle de eventos. Actualmente sólo hay soporte completo para UNIX, aunque el tratamiento con archivos puede hacerse independientemente de la plataforma. === Obtención de un GIOChannel. === Para crear un `GIOChannel`, primero se debe crear el descriptor de archivo de UNIX por los métodos convencionales (open, socket, etc.) y luego utilizar ''[[#g_io_channel_unix_new|g_io_channel_unix_new]]'' o bien ''[[#g_io_channel_new_file|g_io_channel_new_file]]'' para acceder a archivos directamente. <> {{{ GIOChannel *g_io_channel_unix_new(int fd); }}} Esta función devuelve el canal creado a partir del descriptor. Dicho parámetro se puede recuperar después utilizando la función ''g_io_channel_unix_get_fd''. <> {{{ GIOChannel *g_io_channel_new_file(const gchar *nombre_archivo, const gchar *modo, GError **error); }}} Devuelve un nuevo canal para acceder al archivo `nombre_archivo`. El modo tiene la misma forma que el parámetro `modo` de la función de C estándar ''fopen''. Esto es: * `r` para lectura. * `r+` para lectura y escritura. * `w` para escritura (Si el archivo no existe, lo crea; en caso contrario lo trunca). * `w+` para lectura y escritura (con el archivo hace lo mismo que el parámetro w * `a` para escritura al final del archivo (lo crea si no existe). * `a+` para lectura y escritura al final del archivo (lo crea si no existe). El parámetro error debe apuntar a una estructura tipo `GError` previamente creada y es aquí donde se informa de cualquier problema al abrir el archivo. En este último caso la función retorna NULL. === Generalidades en el trabajo con GIOChannels. === Una vez obtenido el `GIOChannel`, en general, todas las llamadas a funciones para realizar alguna operación sobre el canal (lectura, escritura, etc.) tomarán como primer parámetro al propio canal y como último un puntero, error, a una estructura `GError`, donde se informará de cualquier inconveniente ocurrido al intentar la operación. Además, la mayoría de las funciones retornan un código del tipo `GIOStatus` que puede tomar los siguientes valores: * `GIO_STATUS_ERROR`: ocurrió un error (los detalles del mismo se encuentran en el parámetro error). * `G_IO_STATUS_NORMAL`: la operación se realizó con éxito. * `G_IO_STATUS_EOF`: se alcanzó el fin de archivo. * `G_IO_STATUS_AGAIN`: el recurso solicitado no está disponible (p.e., se intentó leer datos, pero no estaban en ese momento). En caso de que el código de retorno sea `G_IO_STATUS_ERROR`, en la estructura `GError` se almacena otro código, indicando con mayor detalle la naturaleza del problema. Este nuevo código es de tipo `GIOChannelError` y puede tomar los siguientes valores: * `G_IO_CHANNEL_ERROR_FBIG`: el archivo es muy grande. * `G_IO_CHANNEL_ERROR_INVAL`: argumento inválido. * `G_IO_CHANNEL_ERROR_IO`: error general de entrada/salida. * `G_IO_CHANNEL_ISDIR`: el archivo es un directorio, pero se intentó acceder a él como un archivo regular. * `G_IO_CHANNEL_ERROR_NOSPC`: No hay más espacio disponible para escritura. * `G_IO_CHANNEL_ERROR_NXIO`: No existe tal dispositivo o dirección. * `G_IO_CHANNEL_ERROR_OVERFLOW`: el valor proporcionado es mayor de lo que permite su tipo. * `G_IO_CHANNEL_ERROR_PIPE`: algunos de los extremos de la tubería lo desconectó. * `G_IO_CHANNEL_ERROR_FAILED`: otros errores. Los primeros valores tienen su correspondencia directa con los valores de la variable `errno` estándar de C. El último se reserva para otra clase de errores no identificados. Los canales tienen un tiempo de vida controlado por conteo de referencias. Inicialmente, los canales se crean con una cuenta de 1 y, cuando la misma cae a 0, se destruyen. Para obtener una referencia se utiliza la función ''[[#g_io_channel_ref|g_io_channel_ref]]'' y para liberar una referencia ''[[#g_io_channel_unref|g_io_channel_unref]]''. Cabe aclarar que, aunque el objeto `GIOChannel` se destruya, no necesariamente se cierra el canal. En concreto, si el objeto fue creado a partir de un descriptor de UNIX, el programador es responsable de cerrarlo (a menos que se utilice la función ''g_io_channel_set_close_on_unref''. Las funciones mencionadas tienen la siguiente forma: <> {{{ void g_io_channel_ref(GIOChannel *canal); }}} <> {{{ void g_io_channel_unref(GIOChannel *canal); }}} Para cerrar de un canal se utiliza g_io_channel_shutdown: <> {{{ GIOStatus g_io_channel_shutdown(GIOChannel *canal, gboolean descarga, GError **error); }}} Aquí, el parámetro descarga controla si se escriben los datos que pueda haber en el buffer interno del canal antes de cerrarlo (para forzar una descarga en cualquier momento se puede utilizar ''g_io_channel_flush''. === Operaciones básicas. === GLib provee una serie de funciones para acceder a un canal abierto de diversas maneras: por caracteres individuales, por bloques de cantidad de bytes fijos y por líneas. Si el tipo de canal lo permite, la función ''[[#g_io_channel_seek_position|g_io_channel_seek_position]]'' sirve para establecer la siguiente posición donde se leerá o escribirá. Normalmente sólo los archivos permiten posicionamiento aleatorio. <> {{{ GIOStatus g_io_channel_seek_position(GIOChannel *canal, glong posicion, GSeekType tipo_posicion, GError **error); }}} La semántica de `posicion` (que es un número con signo) queda determinada por el `tipo_posicion`, que puede tomar los siguientes valores: * `G_SEEK_CUR`: la nueva posición es relativa a la actual. * `G_SEEK_SET`: la nueva posición se toma a partir del principio del archivo. * `G_SEEK_END`: la nueva posición es absoluta respecto al fin de archivo. Para efectuar lecturas en un canal se utilizan algunas de las siguientes funciones: <> {{{ GIOStatus g_io_channel_read_chars(GIOChannel *canal, gchar *buffer, gsize tamaño_buffer, gsize *bytes_leidos, GError **error); }}} Lee una cantidad máxima (`tamaño_buffer`) de bytes del canal como caracteres y los almacena en `buffer`. Éste debe estar previamente reservado (p.e. con ''[[#g_malloc|g_malloc]]''). `bytes_leidos` contiene la cantidad de bytes efectivamente leídos (puede ser menor que la solicitada). Es importante notar que si el canal está configurado para trabajar con caracteres Unicode, un carácter puede ocupar más de un byte y, si el `buffer` no es lo suficientemente grande, puede que no sea posible almacenar ningún carácter. En estas condiciones `bytes_leidos` es cero, pero no hay indicación de error. <> {{{ GIOStatus g_io_channel_read_line(GIOChannel *canal, gchar **puntero_cadena, gsize *bytes_leidos, gsize *terminador, GError **error); }}} Lee una línea completa del canal y almacena el puntero a la misma en `puntero_cadena`. Una vez finalizada la operación con la línea, es responsabilidad del programador liberar la memoria con ''[[#g_free|g_free]]''. En `bytes_leidos` se devuelve la cantidad total de bytes leídos (incluyendo caracteres de fin de línea) y en terminador, sólo la cantidad perteneciente a la línea en sí. Tanto `bytes_leidos` como `terminador` pueden ser nulos si la información no es de interés. <> {{{ GIOStatus g_io_channel_read_line_string(GIOChannel *canal, GString *cadena, gsize *terminador, GError **error); }}} Similar a ''[[#g_io_channel_read_line|g_io_channel_read_line]]'', sólo que, para devolver el resultado, utiliza una estructura GString. `terminador` puede ser nulo. <> {{{ GIOStatus g_io_channel_read_to_end(GIOChannel *canal, gchar **puntero_cadena, gsize *bytes_leidos, GError **error); }}} Lee todos los bytes disponibles en el canal hasta encontrar el fin de archivo. En `puntero_cadena` se devuelve un puntero al espacio de memoria asignado a tal fin que luego debe ser liberado con ''[[#g_free|g_free]]''. Para la escritura se utiliza la contra parte de ''[[#g_io_channel_read_chars|g_io_channel_read_chars]]'': <> {{{ GIOStatus g_io_channel_write_chars(GIOChannel *canal, gchar *buffer, gsize cantidad_bytes, gsize *bytes_escritos, GError **error); }}} Como su propio nombre indica, esta función escribe en el canal `cantidad_bytes` del `buffer`. Puede que no lleguen a escribirse todos los bytes que se solicitaron, por lo que la función devuelve la cantidad de bytes efectivamente escritos. Si `cantidad_bytes` es -1 se considera al `buffer` como terminado en nulo. Se aplican las mismas restricciones en cuanto a codificación del canal que para ''[[#g_io_channel_read_chars|g_io_channel_read_chars]]''. === Integración de canales al bucle de eventos. === Uno de los aspectos más interesantes de los GIOChannels es que es posible integrarlos fácilmente al bucle de eventos y así, por ejemplo, hacer que la aplicación reaccione ante la llegada de datos. Los posibles tipos de eventos que puede generar un canal están dados por el tipo GIOCondition que toma los siguientes valores: * `G_IO_IN`: hay datos disponibles en el canal. * `G_IO_OUT`: se pueden escribir datos al canal sin que éste bloquee. * `G_IO_PRI`: hay datos urgentes para leer. * `G_IO_ERR`: condición de error. * `G_IO_HUP`: desconexión; normalmente, se aplica a sockets y tuberías. Alguno de los extremos rompió la conexión. * `G_IO_NVAL`: solicitud no válida (p.e. el descriptor UNIX no está abierto). La forma básica de integrar un objeto generador de eventos al bucle es obtener una estructura `GSource`. En el caso de los canales, la función para ello es ''[[#g_io_create_watch|g_io_create_watch]]'': <> {{{ GSource *g_io_create_watch(GIOChannel *canal, GIOCondition condición); }}} Esta función devuelve un `GSource` que luego se registra en algún bucle de eventos con ''[[#g_source_attach|g_source_attach]]''. Sin embargo, por conveniencia, GLib provee un par adicional de funciones para integrar directamente un canal al bucle principal: ''[[#g_io_watch|g_io_watch]]'' y ''[[#g_io_add_watch_full|g_io_add_watch_full]]''. <> {{{ guint g_io_add_watch(GIOChannel *canal, GIOCondition condición, GIOFunc función, gpointer datos_usuario); }}} Esta función, que devuelve el identificador de evento, registra el canal para que sea controlado según las condiciones especificadas. En caso de que se cumpla alguna de las condiciones, se llama a la función con los `datos_usuario` adicionales. La función especificada en el registro debe tener la siguiente forma: {{{ gboolean (* GIOFunc)(GIOChannel *canal, GIOCondition condición, gpointer datos_usuario); }}} La condición que la función recibe es aquella que causó el evento en primer lugar. `datos_usario` es lo que se especificó en el momento del registro. La otra función, ''g_io_add_watch_full'' permite además especificar la prioridad del canal y una función de notificación cuando el canal se desconecta del bucle de eventos. === Configuración avanzada de canales. === ==== Codificación ==== Los GIOChannels son capaces de codificar o decodificar los caracteres que se escriben o leen del mismo. La codificación que se utiliza internamente es UTF-8, por lo que, eventualmente, se puede transformar de UTF-8 a alguna otra codificación para escribir a un archivo, o viceversa. Si se necesita que un canal no efectúe transformación alguna a los datos (por ejemplo, para tratamiento de datos binarios) se debe optar por la codificación nula (`NULL`). Las funciones para manipular la codificación son ''[[#g_io_channel_set_encoding|g_io_channel_set_encoding]]'' y ''[[#g_io_channel_get_encoding|g_io_channel_get_encoding]]''. <> {{{ GIOStatus g_io_channel_set_encoding(GIOChannel *canal, const gchar *codificación, GError **error); }}} <> {{{ G_CONST_RETURN gchar *g_io_channel_get_encoding(GIOChannel *canal); }}} Por omisión, la codificación externa utilizada para archivos es UTF-8. Para cambiarla se deben tener en cuenta las siguientes condiciones: * El canal acaba de ser creado. * El canal es para escritura solamente. * El canal es sobre un archivo y el puntero de lectura y escritura acaba de ser colocado con ''[[#g_io_channel_seek_position|g_io_channel_seek_position]]''. * La codificación actual es UTF-8 o nula. * Se ha llamado a una función de lectura y ha devuelto `G_IO_STATUS_EOF` o, en el caso de ''[[#g_io_channel_read_to_end|g_io_channel_read_to_end]]'', `G_IO_STATUS_NORMAL`. ==== Terminador de línea. ==== En UNIX, el fin de línea se indica con un carácter de `linefeed` (código ASCII 10), pero esto puede no ser cierto para otras plataformas (p.e. Win32). Para que las funciones de lectura y escritura por líneas den los resultados esperados para cada plataforma, en caso de ser necesario el acceso a archivos escritos en otra plataforma, se debe configurar el terminador de línea en el canal. Las funciones para ello son: <> {{{ void g_io_channel_set_line_term(GIOChannel *canal, const gchar *terminador, gint longitud); }}} <> {{{ G_CONST_RETURN gchar *g_io_channel_get_line_term(GIOChannel *canal, gint *longitud); }}} ==== Configuración de buffer interno. ==== La API de GIOChannel permite modificar alguno de los parámetros de funcionamiento interno del canal. Es posible modificar el tamaño del buffer interno del mismo para optimizar el rendimiento de lectura y escritura en casos especiales. Para ello se utilizan las funciones ''[[#g_io_channel_get_buffer_size|g_io_channel_get_buffer_size]]'' y ''[[#g_io_channel_set_buffer_size|g_io_channel_set_buffer_size]]''. <> {{{ gsize g_io_channel_get_buffer_size(GIOChannel *canal); }}} <> {{{ void g_io_channel_set_buffer_size(GIOChannel *canal, gsize tamaño); }}} Si en ''[[#g_io_channel_set_buffer_size|g_io_channel_set_buffer_size]]'' se especifica un tamaño 0, GLib elegirá un tamaño adecuado al canal. Se ha visto antes que, utilizando el bucle de eventos, se puede supervisar un canal y producir llamadas a funciones ante determinados eventos. También es posible verificar manualmente si un canal tiene datos listos para la lectura o si se pueden escribir datos en él. Para ello se utiliza la función ''[[#g_io_channel_get_buffer_condition|g_io_channel_get_buffer_condition]]''. <> {{{ GIOCondition g_io_channel_get_buffer_condition(GIOChannel *canal); }}} Esta función puede devolver solamente `G_IO_IN` y `G_IO_OUT` y, por supuesto, su significado es exactamente el mismo que se explicó antes. Por último, con las funciones `[#g_io_channel_set_flags g_io_channel_set_flags]` y `[#g_io_channel_get_flags g_io_channel_get_flags]`, es posible saber y, en parte, modificar el comportamiento que tiene un canal. <> {{{ GIOFlags g_io_channel_get_flags(GIOChannel *canal); }}} <> {{{ GIOStatus g_io_channel_set_flags(GIOChannel *canal, GIOFlags banderas, GError **error); }}} Los valores que puede tomar el parámetro banderas son `G_IO_FLAG_APPEND` y/o `G_IO_FLAG_NONBLOCK` (los demás valores son de sólo lectura). El valor obtenido en ''[[#g_io_channel_get_flags|g_io_channel_get_flags]]'' es una composición de: * `G_IO_FLAG_APPEND`: canal en modo de agregado. * `G_IO_FLAG_NONBLOCK`: canal en modo no bloqueante; esto es, si se intenta, p.e., leer y no hay datos disponibles, se devuelve el control inmediatamente con un estado de `G_IO_STATUS_AGAIN`. * `G_IO_FLAG_IS_READABLE`: el canal se puede leer. * `G_IO_FLAG_IS_WRITEABLE`: se puede escribir en el canal. * `G_IO_FLAG_IS_SEEKABLE`: se puede mover el puntero en el canal mediante la utilización de ''[[#g_io_channel_seek_position|g_io_channel_seek_position]]''. ---- '''''Volver a:''''' [[Documentacion/Desarrollo/Glib|GLib]]