Saltar a contenido

IO Structs

IO: Descriptores de fichero

Llamadas al sistema

open() y close(): apertura y cierre
fileno(): correspondencia fd y FILE *
read(): leer datos
write(): escribir datos

Ejemplo utilizaci贸n: copiar datos desde un descriptor a otro

in2out.c
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>


typedef struct fichs {
   char *fin;
   int fdin;
   char *fout;
   int fdout;
} st_fichs;


/*
 * Obtener descriptores de ficheros de entrada y salida
 * Invocar open() para abrir el fichero de entrada en modo lectura O_RDONLY
 * y el de salida en modo O_CREAT|O_WRONLY|O_TRUNC
 * https://man7.org/linux/man-pages/man2/open.2.html
 * En caso de error utiliza stdin y stdout
 */
int st_open(st_fichs *pfichs)
{
    if ((pfichs->fdin = open (pfichs->fin, O_RDONLY)) < 0)
        pfichs->fdin = fileno(stdin); /* el fd de stdin siempre es 0 */

    if ((pfichs->fdout = open (pfichs->fout, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR)) < 0)
        pfichs->fdout = 1;

    if ((pfichs->fdin == 0) || (pfichs->fdout == 1)) {
        fprintf(stderr, "\nError al abrir!!!\n");
        return -1; /* OJO: informar del error */
    }
    return 0;
}

/*
 * Funci贸n que lee datos de un descriptor y escribe en otro descriptor
 */
int in2out(int fdin, int fdout, int bytes)
{
    char buff[1024];
    int rbytes = 0, wbytes = 0;

    memset(buff, '\0', 1024); /* Inicializar buffer */
    while((rbytes = read(fdin, buff, bytes)) > 0) {
        wbytes = write(fdout, buff, rbytes);
        if(wbytes != rbytes)
            fprintf(stderr, "ERROR: ");
        else
            fprintf(stderr, "OK: ");
        fprintf(stderr, "Lee %d bytes; Escribe %d bytes\n", rbytes, wbytes);
        memset(buff, '\0', rbytes);
    }

    return (rbytes == wbytes);
}

int main(int argc, char * argv[]) {
    st_fichs stEj;
    int ret;

    memset(&stEj, '\0', sizeof(st_fichs));  /* Inicializar struct */
    stEj.fin = argv[1];
    stEj.fout = argv[2];

    ret = st_open(&stEj);
    fprintf(stderr,"fin: %s; fout: %s;\nfdin: %d; fdout: %d; sizeof(st_fichs): %ld\n",
            stEj.fin, stEj.fout, stEj.fdin, stEj.fdout, sizeof(st_fichs));
    fprintf(stderr,"===============================================================================\n");
    if(ret == 0)
        ret = in2out(stEj.fdin, stEj.fdout, 1024);
    else
        ret = in2out(stEj.fdin, stEj.fdout, 5);
    if(stEj.fdin != 0)
        close(stEj.fdin);
    if(stEj.fdout != 1)
        close(stEj.fdout);

    return ret;
}

Tests in2out.c

Suponiendo que la primera ejecuci贸n es correcta, 驴qu茅 hace el programa en las 3 restantes?

# udsclient.log es un fichero de texto en el directorio actual
./bin/in2out ./udsclient.log /tmp/udsclient.log
# udsclient1.log no existe
./bin/in2out ./udsclient1.log /tmp/udsclient.log
./bin/in2out ./udsclient.log /etc/udsclient.log
./bin/in2out ./udsclient.log /etc/udsclient.log 2>/tmp/err.log

Entrada/Salida (IO) con tipos de datos compuestos (structs)

1. Datos. Definiciones de tipos

Las siguientes declaraciones son las mismas que las vistas en LabLibros

/*
 * Libros.h
 *
 * Ver: https://www.tutorialspoint.com/cprogramming/c_structures.htm
 */

typedef struct Libros {
   int isbn;
   char titulo[50];
   char autor[50];
   char descripcion[100];
} Libro;

typedef Libro *ptrLibro;

2. Lectura binaria de structs en un fichero

  • Llamadas al sistema fread() / fwrite()
    fread_fwrite.png
  • El fichero de entrada es siempre el mismo, en este caso la ruta es dat/fich1.dat
    /*
     * Funci贸n que lee structs de un archivo binario fich1.dat
     */
    void readLibIO(void) {
           Libro ejLibro1;                 /* Se declara un libro (Libro es un tipo struct) */
    
           printf("readLibIO:\n");
           printf("----------\n");
    
            /* Abrir el fichero, OJO, para lectura "r" */
            fich = fopen ("dat/fich1.dat", "r"); 
            if (fich == NULL) 
            { 
                fprintf(stderr, "\nError al abrir\n"); 
                exit (1); /* Salir a las bravas */
            } 
    
            /* Leer del fichero un registro cada vez */
            while(fread(&ejLibro1, sizeof(Libro), 1, fich)) {
             /* Visualiza ejLibro1 campo por campo */
               printf("\tISBN del libro: %d\n", ejLibro1.isbn);
            printf("\tT铆tulo del libro: %s\n", ejLibro1.titulo);
                printf("\tAutor del libro: %s\n", ejLibro1.autor);
            printf("\tDescripci贸n del libro: %s\n", ejLibro1.descripcion);
            }
    
            /* Cerrar fichero */
            fclose (fich);
    }
    

3. Escribir structs en un fichero

Escritura binaria

  • El fichero de salida es siempre el mismo, en este caso la ruta es dat/fich1.dat
    /*
     * Funci贸n que escribe un struct en un archivo binario fich1.dat
     */
    void wbinLibIO(void) {
           Libro ejLibro1;                 /* Se declara un libro (Libro es un tipo struct) */
           int code = 0;
    
           printf("wbinLibIO:\n");
           printf("----------\n");
                                           /* Inicializa ejLibro1 campo por campo. Usa '.' */
           ejLibro1.isbn = 56470887;
           strcpy(ejLibro1.titulo, "Programaci贸n en C");
           strcpy(ejLibro1.autor, "Pepito Grillo");
           strcpy(ejLibro1.descripcion, "Buena gu铆a de introducci贸n al C");
    
                                           /* Visualiza ejLibro1 campo por campo */
           printf("\tISBN del libro: %d\n", ejLibro1.isbn);
           printf("\tT铆tulo del libro: %s\n", ejLibro1.titulo);
           printf("\tAutor del libro: %s\n", ejLibro1.autor);
           printf("\tDescripci贸n del libro: %s\n", ejLibro1.descripcion);
           printf("Cu谩nto ocupa un libro? %d bytes\n", (int) sizeof(Libro));
    
            /* Abrir el fichero, OJO, para escritura "w" */
            fich = fopen ("dat/fich1.dat", "w"); 
            if (fich == NULL) 
            { 
                fprintf(stderr, "\nError al abrir\n"); 
                exit (1); /* Salir a las bravas */
            } 
    
            /* Escribir struct mediante fwrite */ 
            code = fwrite (&ejLibro1, sizeof(Libro), 1, fich); 
    
            if(code != 1) 
                printf("\n\tError escribiendo al fichero !\n"); 
            else
                printf("\n\tEscritura realizada correctamente !\n"); 
    
            /* Cerrar fichero */
            fclose (fich);
    }
    

Escritura modo texto

  • El fichero de salida es siempre el mismo, en este caso la ruta es dat/fich2.dat
  • En el siguiente ejemplo se usa fprintf()
  • Recomendable consultar sprintf(): salida formateada (print formatted output)
    /*
     * Funci贸n que escribe los campos de un struct en un archivo de texto fich2.dat
     */
    void wtxtLibIO(void) {
           Libro ejLibro1;                 /* Se declara un libro (Libro es un tipo struct) */
    
           printf("wtxtLibIO:\n");
           printf("----------\n");
                                           /* Inicializa ejLibro1 campo por campo. Usa '.' */
           ejLibro1.isbn = 56470887;
           strcpy(ejLibro1.titulo, "Programaci贸n en C");
           strcpy(ejLibro1.autor, "Pepito Grillo");
           strcpy(ejLibro1.descripcion, "Buena gu铆a de introducci贸n al C");
    
            /* Abrir el fichero, OJO, para escritura "w" */
            fich = fopen ("dat/fich2.dat", "w"); 
            if (fich == NULL) 
            { 
                fprintf(stderr, "\nError al abrir\n"); 
                exit (1); // Salir a las bravas
            } 
    
          /* Escribe ejLibro1 campo por campo */
           fprintf(fich, "\tISBN del libro: %d\n", ejLibro1.isbn);
           fprintf(fich, "\tT铆tulo del libro: %s\n", ejLibro1.titulo);
           fprintf(fich, "\tAutor del libro: %s\n", ejLibro1.autor);
           fprintf(fich, "\tDescripci贸n del libro: %s\n", ejLibro1.descripcion);
    
            /* Cerrar fichero */
            fclose (fich);
    }
    

Ejercicio de consolidaci贸n

IO con datos tipo texto vs datos binarios

  1. Escribir un programa tester que permita probar los programas en los 3 ejemplos anteriores utilizando los argumentos en la l铆nea de orden para determinar el caso a ejecutar, el fichero, etc.
  2. Completar el c贸digo para que si en la l铆nea de orden no se indica el fichero a leer/escibir el programa lea de la entrada est谩ndar o escriba en la salida est谩ndar

Material relacionado

脫rdenes de compilaci贸n

# in2out.c
gcc -Wall -Wextra -pedantic -Werror in2out.c -o bin/in2out
# librosIO.c
gcc -Wall -Wextra -pedantic -Werror librosIO.c -o bin/librosIO