03 – Entrada y salida en lenguaje C Diego Andrés Alvarez Marín Profesor Asociado Universidad Nacional de Colombia Sede Manizales 1 Temario ● Entrada/salida básica (stdio.h) ● Entrada/salida avanzada (ncurses.h) 2 Funciones de salida/entrada de datos básicas ● ● Las provee la librería estándar stdio.h y no el lenguaje mismo. stdio.h significa "standard input-output header" (cabecera estandar E/S) – stdin = standard input = teclado – stdout = standard output = pantalla – stderr = standard error 3 Funciones básicas de salida a pantalla (stdout) ● ● Sin formato: – putchar() escribe un caracter a stdout – puts() escribe una cadena de caracteres a stdout Con formato: – printf() escribe una cadena de caracteres con formato a stdout 4 int putchar(int ch); ● Escribe un caracter en la pantalla (stdout) ● Retorna ch 5 Algunos caracteres especiales Alerta (audible) \a Backspace \b Tabulador horizontal \t Escape (solo GNU) \e Número octal (ej: \o32)\o Backslash \\ Nulo \0 Retorno de carro Formfeed Comillas Cambio de linea=\r\f Número hex (ej: \xF3) Apóstrofe Pregunta \r \f \” \n \x \' \? int puts(const char* str); Escribe la cadena str a stdout y agrega automáticamente un “\n”. La cadena str termina cuando se encuentra un caracter “\0”. El “\0” no se copia a stdout 8 int printf(const char* formato,...) ); printf() retorna el número de caracteres impresos, o un valor negativo si ocurre un error. printf() toma un número variable de entradas 9 int printf(const char* formato,...) formato en printf() ● ● Es una cadena de texto que indica el formato como se debe escribir lo deseado Sigue el siguiente prototipo: %[banderas][ancho][.precisión][longitud]especificador Ver detalles en: http://pubs.opengroup.org/onlinepubs/009695399/function http://en.cppreference.com/w/cpp/io/c/fprintf http://en.wikipedia.org/wiki/Format_string_attack 10 %[banderas][ancho][.precisión][longitud]especificador Introducido en C99 Introducido en C99 Introducido en C99 11 %[banderas][ancho][.precisión][longitud]especificador %[flags][width][.precision][longitud]especificador 12 %[banderas][ancho][.precisión][longitud]especificador Introducido en C99 Introducido en C99 Introducido en C99 Introducido en C99 Introducido en C99 13 14 Observe que el \b no borró el guión - 15 Funciones básicas de entrada desde el teclado (stdin) ● ● Sin formato: – getchar() lee un caracter desde stdin – gets() lee una cadena de caracteres desde stdin (C99 – desaconseja su uso, C11 – obsoleto) – gets_s() lee una cadena de caracteres desde stdin (es el reemplazo de gets()) Con formato: – scanf() lee una cadena de caracteres con formato desde stdin 16 int scanf(const char* formato, ...); ● ● ● ● Lee datos del teclado (stdin) y lo almacena de acuerdo al formato dado, en las direcciones de memoria indicadas. De forma a similar a printf() puede leer un número variable de entradas; en este caso se supone que cada entrada está separada por un espacio en blanco Ignora los espacios en blanco Retorna el número de items de la cadena de argumentos exitosamente leídos o un EOF (constante igual a -1) si un error ocurre. 17 int scanf(const char* formato, ...); ● El formato está dado por: %[*][ancho][longitud]especificador ● Ver detalles en: http://en.cppreference.com/w/cpp/io/c/fscanf 18 %[*][ancho][longitud]especificador C99 C99 C99 19 %[*][width][length]especificador 20 Observe este comportamiento inesperado!! 21 | 22 int getchar(void); Lee un caracter desde el teclado (mostrándolo). A pesar de todo estoy leyendo en un char 23 char* gets (char* str); ● El C99 no recomienda usar este comando. El C11 lo vuelve obsoleto. La razón de esto es que este comando hace vulnerable el programa al ataque por hackers. Ver detalles en: – http://en.cppreference.com/w/c/io/gets – http://en.wikipedia.org/wiki/Buffer_overflow – http://c-faq.com/stdio/getsvsfgets.html 24 char* gets (char* str); Buffer overflow!! 25 char* gets_s(char *str, rsize_t n); ● ● ● Función sugerida en el C11, en reemplazo de gets() Lee a lo más n-1 caracteres de stdin en la cadena apuntada por str. Automáticamente agrega el \0 al final de la cadena No está implementada todavía en el gcc v.4.6 (el que tengo instalado) 26 Otro reemplazo de gets(): fgets() strchr() busca desde la dirección buf hasta que encuenta la primera aparición de un \n 27 Buffer Un buffer (de datos) es un espacio de memoria, en el que se almacenan datos para evitar que el programa o recurso que los requiere, ya sea hardware o software, se quede sin datos durante una transferencia. Un buffer es como tener dinero en el banco (buffer), un trabajo (entrada) y unos gastos fijos (salida). Si tienes un trabajo inestable, mientras tengas ciertos ahorros, puedes mantener tus gastos fijos sin problemas, e ir ingresando dinero cuando puedas según vas trabajando. Si los ahorros son pequeños, en seguida que no tengas trabajo, no vas a poder acometer los gastos fijos. De la misma forma si escuchas música en Internet y tu programa de audio usa un buffer pequeño, en cuanto que haya alguna interrupción en la descarga (porque las descargas nunca tienen una velocidad constante), notarás cortes de sonido, ya que faltará información. El buffer de teclado es una memoria intermedia en la que se van almacenando los caracteres que un usuario teclea, los cuales son 28 tratados por el computador a penas se libere un recurso. ¿Por qué este código aparentemente correcto no funciona? Se presionó 20 (ENTER) y nada más. El programa no me preguntó el nombre y terminó 29 Aquí el primer printf() pregunta la edad. Se escribe "20" y se presiona ENTER. El buffer del teclado contiene: 2 0 \n El scanf() lee el %d, es decir el 20 y lo guarda en la variable edad; sin embargo deja el '\n' en el buffer del teclado; ahora entra el fscanf() lee el '\n' y no alcanza a leer el nombre. El problema es que scanf() dejó basura en el buffer del teclado (concretamente el \n). Según http://c-faq.com/stdio/gets_flush2.html la solución es vaciar el buffer del teclado lo cual se puede hacer con alguno de los siguientes comandos: ● ● while((c = getchar()) != '\n' && c != EOF); //un ciclo vacío __fpurge(stdin); //solo funciona con gcc/Linux (stdio_ext.h) 30 31 Nota con respecto a la entrada de datos desde el teclado scanf() no es muy versátil que digamos y tiene problemas como el anteriormente mostrado. La mejor opción que se tiene si se requiere seguridad procesando la entrada es utilizar fgets(), sscanf() y/o alguna librería de regular expressions: http://en.wikipedia.org/wiki/Regular_expression No existen librerías de expresiones regulares en el estándar de C, pero si algunas para ciertos sistemas como regex.h o PCRE para UNIX/Linux: http://www.lemoda.net/c/unix-regex/index.html http://www.pcre.org/ Se podría también utilizar una interface con el comando de consola grep. 32 33 NOTA: lenguajes como PERL, PYTHON, MATLAB tienen un muy buen soporte para expresiones regulares. Les aconsejo sinceramente aprender a manejar las expresiones regulares cuando tengan problemas con validar entradas de texto, o procesar una gran cantidad de datos en archivos. Es una herramienta que los puede sacar de apuros en más de una ocasión. 34 Tomado de: http://xkcd.com/208/ Preferiblemente no use scanf() http://c-faq.com/stdio/scanfprobs.html Una buena interface debe permitir la posibilidad que el usuario entre errores: por ejemplo letras en un campo donde se piden números, más o menos caracteres que los esperados, etc. scanf() no es capaz de tratar este tipo de situaciones. Por lo tanto, se sugiere mejor utilizar fgets() y luego interpretar lo leído utilizando sscanf() y/o algunas otras técnicas como expresiones regulares. Funciones como strtol(), strtok(), y atoi() son bastante útiles. 35 Funciones de salida/entrada de datos básicas <ncurses.h> ● ● ● curses.h es una biblioteca que provee rutinas de entrada/salida avanzadas y que permite al programador escribir interfaces basadas en texto. Fue creada por Ken Arnold para el sistema UNIX BSD. Sin embargo, existen implementaciones para Linux (ncurses.h) y para Windows (pdcurses.h). Permite manejar detalladamente la pantalla, manejar el mouse, crear interfaces de texto 36 amigables. Tomada de: http://dmcradio.sourceforge.net/ 37 38 Instalación en Windows de pdcurses ● Descargue de: http://pdcurses.sourceforge.net/ ● Lea las instrucciones de instalación. ● O si está utilizando el MinGW, simplemente en la línea de comandos escriba: mingw-get install mingw32-libpdcurses mingw32-pdcurses 39 En Linux ● Instale la librería libncurses5-dev o similar, ej.: $ sudo apt-get install libncurses5-dev ● Verifique que los siguientes archivos existan: ● ● ● ● ● Observe que en este caso ncurses.h y curses.h son el mismo archivo 40 Un primer ejemplo Si no se pone este comando, la consola se comportará de modo extraño. En linux esto se arregla con el comando de consola reset. De todos modos se debe poner siempre endwin(). 41 42 43 Otros comandos de curses.h ● int addch(const chtype letra_char); ● int mvaddch(int y, int x, const chtype letra_char); ● int move(int y, int x); ● chtype inch(void); ● int mvprintw(int y, int x, char *format, ...); ● int getstr(char* string); 44 Material basado en: ● http://www.slideshare.net/amraldo/introduction-to-c-programming-7898353 ● http://www.slideshare.net/petdance/just-enough-c-for-open-source-program ● http://www.cplusplus.com/reference/cstdio/printf/ ● http://www.cplusplus.com/reference/cstdio/scanf/ ● http://faq.cprogramming.com/cgi-bin/smartfaq.cgi?answer=1049157810&id ● Wikipedia