ENTRADAS/SALIDAS STANDARD en C++

| 2013-04-26 | No hay comentarios »

Entradas/Salidas  Standard

EL FICHERO STDIO.H

Primer ejemplo de programa con entradas/salidas standard.

SIMPLEIO.C


#include "stdio.h"    /* cabecera standar para e/s */

main()

{

char c;

printf("Teclee cualquier caracter, X = parar el programa.\n");

do {

c = getchar();    /* toma un caracter simple del teclado */

putchar(c);       /* visualiza el caracter en el monitor  */

putchar("c");

} while (c != 'X');  /* mientras no se pulse X */

printf("\nFin del programa.\n");

}

Llamamos ENTRADA/SALIDA STANDARD(E/S a partir de ahora, para abreviar), a los sitios donde los datos se toman del teclado y se muestran en la pantalla del monitor. Dado que estos dispositivos, teclado y monitor se usan muy a menudo, no necesitan ser mencionados en las instrucciones de E/S. Esto tomará sentido cuando empecemos a usarlos en el programa ejemplo.

Lo primero que advertimos es la primera línea del fichero, “#include stdio.h“. Se parece mucho a la línea #define, ya estudiada anteriormente, excepto en que hay una pequeña modificación y el fichero entero es leído en esta sección. El sistema encontrará el fichero denominado “stdio.h” y leerá el total de su contenido, sustituyendo el mandato correspondiente al #define, por uno adecuado al #include. Obviamente el contenido de “stdio.h” es código válido para C, y por tanto, compilable como parte del programa. Este fichero está compuesto por varias #defines standard, para definir algunas operaciones de E/S standard. Se llama a este fichero desde la cabecera del programa.

Cada cabecera de esta forma tiene un propósito específico, y cualquiera de ellos pueden incluirse en el programa.

La mayoría de compiladores C utilizan el doble signo de ” para indicar que un fichero “include” será encontrado en el directorio actual. Un signo “menor que (<)” o un signo “mayor que (>)” indica que el fichero se encontrará en una cabecera de fichero standard. Prácticamente todos los compiladores bajo entorno MS-DOS usan este signo y muchos requieren el fichero “include” presente en el directorio actual.

OPERACIONES DE E/S EN C

Por el momento no tenemos operaciones de E/S definidas como parte del lenguaje, por tanto debemos crearlas.

Dado que nadie ha reinventado sus propias rutinas de E/S las que los programadores crearon son las que se usan habitualmente, un buen número de operaciones de E/S que nos ayudan en nuestros programas. Estas funciones ya se consideran standard, y vienen en la mayoría de compiladores. De hecho, la industria del lenguaje C se ha fijado en la definición dada en el libro de Kernigan y Ritchie.(K&R).

OTROS FICHEROS INCLUDE

Cuando se escriban programas largos y se tenga la necesidad de partirlos en varios ficheros compilados por separado se tendrá ocasión de utilizar mandatos comunes a cada una de las porciones de programa. Sería una ventaja hacer ficheros separados, contiendo el mandato y, usando un #include para insertar cada uno en los ficheros. Si se necesita modificar alguno de estos mandatos, sólo deberá hacerse en un bloque, y automáticamente la modificación se realizará en los restantes. Esto queda lejos, pero es una idea de como usar la directiva “#include”.

VOLVAMOS AL FICHERO “SIMPLEIO.C”

Continuemos examinando el fichero. Se define la variable “c” y aparece un mensaje por pantalla, hecho con un printf ya familiar. Nos encontramos en un bucle de tantos pasos mientras no se cumpla la condición, que “c” sea igual a X mayúscula. Las 2 nuevas funciones incluidas en el bucle son de la mayor importancia en este programa, ya que son una novedad. Estas rutinas leen caracteres desde teclado y los muestran por el monitor, uno a uno.

La función “getchar()lee un caracter del dispositivo de entrada standard (normalmente el teclado), y lo asigna a la variable “c”. La siguiente función, “putchar()” usa el dispositivo standard de salida (el monitor, en este caso), para mostrar el contenido de la variable “c”.

El caracter es visualizado en la posición actual del cursor y este avanza un espacio. El sistema se encarga de hacer muchas cosas por nosotros. El bucle continua leyendo y mostrando caracteres mientras no tecleemos X para finalizar.

Compilando y ejecutando el programa se verá que todo lo que se teclee aparecerá por pantalla inmediatamente después de pulsar ENTER.

D.O.S NOS AYUDA (O NOS INDICA EL CAMINO)

Debemos saber un poco acerca del DOS y sus procesos para entender que pasa aquí. Cuando el dato es leído desde el teclado, bajo el control del sistema operativo, los caracteres se almacenan en un buffer(reserva de memoria con un fin específico), mientras no se pulse ENTER, ya que entonces el total de caracteres almacenados pasará al programa. En el ejemplo, a medida que se introducen los caracteres, van apareciendo por pantalla. A esto se le llama ECO, y sucede en algunas aplicaciones que utilizamos habitualmente.

Con el anterior párrafo en mente, debería quedar claro que cuando se está tecleando una línea de datos en “SIMPLEIO.C” los caracteres están siendo reflejados por el DOS y que cuando se pulsa ENTER, el conjunto de estos caracteres pasa al programa. A medida que se introduce un caracter, éste aparece por pantalla, con lo cual en la pantalla se forma una repetición de la línea tecleada. Para entender mejor esto, tecleemos una línea, con una X en mayúsculas, en alguna parte de la línea, en medio, por ejemplo. Podemos escribir tantos caracteres después de X como deseemos, y aparecerán en el monitor debido a que el DOS los repite, y los mete en el buffer. DOS no sabe que X es el caracter finalizador. Cuando la línea de texto se pasa al programa, los caracteres son aceptados uno a uno, hasta llegar a X. Tras esto, finaliza el bucle, y el programa. Los caracteres que siguen a X no aparecerán en la línea.

OTRO EXTRAÑO MÉTODO DE E/S

SINGLEIO.C


#include "stdio.h"

main()

{

char c;

printf("Pulse cualquier caracter, acaba el programa con X\n");

do {

c = getch();                     /* toma un caracter */

putchar(c);                  /* lo visualiza por pantalla */

} while (c != 'X');

printf("\nFin del programa.\n");

}

Otra vez empezamos con una cabecera de E/S, y definimos una variable, “c” e imprimimos un mensaje de bienvenida. Como en el programa anterior, estamos en un bucle que continúa ejecutándose mientras no tecleemos una X mayúscula, pero aquí la acción es sensiblemente distinta.

getch()” es una nueva función de “captación de caracteres”. Es diferente a “getchar()” en el sentido de que no tiene relación con el DOS. Lee los caracteres sin eco, y los manda directamente al programa, donde son procesados al momento. Esta función, entonces, lee un caracter, lo manda al programa, aparece por pantalla y continúa el proceso mientras no aparezca la famosa X.

Cuando el programa se ejecute, las líneas en pantalla no se repetirán cuando se pulse ENTER, y cuando se teclee X, el programa finalizará. No es necesario el retorno de carro para aceptar una línea con X. Pero hay otro problema, no hay línea de retorno“(otro tipo de ENTER) con el ENTER.

AHORA NECESITAMOS UN “LINE-FEED”

Esto no es aparente en muchos programas, pero cuando pulsamos ENTER, el programa añade otra cosa al retorno de carro. Debe volver a la primera posición izquierda de la siguiente línea. Esto, conocido como “alimentación de línea”, no es automático. Necesitamos implementarlo en el programa. En el fichero “BETTERIN.C” encontraremos un cambio para incorporar este elemento.


#include "stdio.h"

#define CR 13       /* definición de CR como 13 */

#define LF 10       /* definición de LF como 10 */

main()

{

char c;

printf("Entre algunos caracteres, pulse X para terminar.\n");

do {

c = getch();                    /* toma un caracter */

putchar(c);                     /* lo saca por pantalla */

if (c == CR) putchar(LF);       /* si es un retorno de carro

inserta una nueva línea */

} while (c != 'X');

printf("\nFin de programa.\n");

}

Aquí  tenemos dos mandatos adicionales al principio que definen códigos para el “line-feed” y para el retorno de carro. Si miramos cualquier tabla del código ASCII, encontraremos  los códigos 10 y 13 definidos exactamente como aquí. En el programa principal, tras mostrar el caracter lo comparamos con retorno de carro, y si es igual, también mostramos un cambio de línea, el line-feed. Podríamos considerar correcto lo que aparece en las #define, “if (c==13) putchar(10);”, pero no sería muy descriptiva sobre que hace aquí. El método usado en el programa representa la mejor práctica de programación.

¿QUÉ MÉTODO ES MEJOR?

Hemos examinado 2 métodos de leer caracteres en un programa en C, y nos encontramos en la necesidad de elegir uno. Esta elección depende de la aplicación a usar, porque cada método tiene ventajas e inconvenientes. Demos una mirada a ambos.

Cuando usamos el primer método, DOS hace todo el trabajo por nosotros, almacenando los caracteres en un buffer de entrada y señalando cuando una línea ha sido completada. Podríamos escribir un programa que, por ejemplo, hiciera muchos cálculos y, pidiera datos. Mientras estuviéramos haciendo esos cálculos, DOS acumularía una línea de caracteres para nosotros, y estarían ahí cuando estuviéramos preparados para recibirlos. No obstante, no podríamos conocer los caracteres del buffer, hasta que no pulsemos ENTER, ya que hasta entonces el DOS considera el buffer incompleto e inaccesible.

El segundo método, usado en BETTERIN.C, nos permite recoger un caracter y mostrarlo inmediatamente. No tenemos que esperar a que DOS llene su buffer. La máquina no hace nada más que esperar a que pulsemos un caracter. Método muy útil para aplicaciones interactivas.

Le corresponde al programador decidir cual es mejor.

Debemos mencionar ahora que existe la función “ungetchar()“, que trabaja como “getch”. Si recoge un caracter, y decide que ha ido demasiado lejos, puede deshacerse de ese caracter y eliminarlo del dispositivo de entrada. Esto simplifica algunos programas porque no sabe que no necesita el caracter mientras no lo recoja. Nosotros sólo podemos “olvidar” un caracter en el dispositivo de entrada, pero esto es suficiente para cumplir el objetivo para el cual la función fue creada. Es complicado demostrar el uso de esta función en un programa, por tanto la estudiaremos cuando nos haga falta.

Trabajando Con Enteros

Ejemplo de lectura en algunos tipos formateados.

INTIN.C.


#include "stdio.h"

main()

{

int valin;

printf("Ingrese un número de 0 a 32767, para con 100.\n");

do {

scanf("%d",&valin);   /* Lee un entero y lo asigna a valin */

printf("El valor es %d\n",valin);

} while (valin != 100);

printf("Fin de programa\n");

}

La estructura de este programa es muy parecida a los últimos 3 ejemplos, excepto en que aquí definimos un entero y, un bucle que funcionará hasta que la variable tome el valor 100.

Tenemos ejemplos de lectura de caracteres simples, en los últimos 3 programas, pero en este  leemos un valor entero de una sola vez, usando la función “scanf“. Esta función es muy similar a “printf”, que hemos estado usando hasta ahora, excepto en que en esta ocasión se usa para introducir ejemplos de salida. Examinando la línea con “scanf”  vemos que no pide para la variable “valin” directamente, pero da su dirección,  dado que espera que la función devuelva un valor. La función necesita tener la dirección de la variable para poder devolver un valor al programa. Ya que nos falta un puntero en “scanf”, es probable que encontremos problemas usando esta función.

La función “scanf” examina la línea de entrada, mientras encuentra el primer dato. Ignora los espacios en blanco, y, en este caso, lee datos de tipo entero, mientras no encuentre un espacio en blanco o un caracter decimal no válido, en cuyo caso cesará la lectura y devolverá el valor.

Recordando lo que hablamos acerca del buffer que crea DOS, y como trabaja, deberá quedar claro que no sucede nada hasta que no se encuentre el ENTER. Entonces, el buffer se cierra y, nuestro programa buscará en la línea de datos todos los enteros  hasta examinarla totalmente. Esto es así porque estamos en un bucle y le hemos dicho que busque un valor, lo imprima, busque otro, lo imprima, etc. Si entra valores en una línea, leerá cada uno sucesivamente, y los sacará por pantalla. Entrando el valor 100, acabaremos el programa, y entrando el valor 100 junto con otros valores, se causará la finalización del programa antes de la lectura de los valores posteriores a 100.

DA RESPUESTAS A VECES ERRÓNEAS

Si se introduce un número como 32767, o menor, aparecerá correctamente en pantalla, pero si se ingresa un número más alto, aparecerá produciendo un error. Por ejemplo, si entramos el valor 32768, aparecerá como -32768, y tecleando 65535 aparecerá 0. En si, no son errores graves, pero están causados por la manera en que está definido un entero. El bit más significativo del patrón de 16 bits, válido para variables enteras es el de signo, por lo tanto sólo hay 15 bits a la izquierda del valor. Este, por tanto, sólo puede tener rango de -32768 a 32767. Cualquier otro valor no comprendido entre estos dos, queda fuera de margen. Debemos tener cuidado con esto en nuestros programas. Es otro ejemplo de la gran responsabilidad que supone programar en C, a cambio de la gran flexibilidad que presta el lenguaje, mayor que en otros de alto nivel, como PASCAL, MODULA-2, etc.

El párrafo anterior es aplicable a la mayoría de compiladores bajo entorno MS-DOS. Existe una pequeña posibilidad de que el compilador utilizado tenga un rango superior a 16 bits, en cuyo caso los límites deben ser modificados.

Puede ejecutarse el programa probando con diferentes valores en una línea, para ver resultados y con varios números entre blancos; o con números demasiado grandes, a ver que pasa y, finalmente con algún caracter no válido para ver que hace el sistema con caracteres no decimales.

ENTRANDO CADENAS DE CARACTERES

El siguiente programa es un ejemplo de lectura de una variable de cadena. Este programa es idéntico al anterior, excepto en que en vez de definir una variable entera, hemos definido una de cadena con un límite de 24 caracteres(recordemos que una cadena de caracteres debe tener un caracter EOL). La variable en el “scanf” no necesita un “&” porque “big es una variable array y, por definición, un puntero. Este programa no debe requerir explicaciones adicionales.


STRINGIN.C

#include "stdio.h"

main()

{

char big[25];

printf("Entre una cadena de caracteres, máximo de 25.\n");

printf("Una X en la columna 1 finalizara el programa.\n");

do {

scanf("%s",big);

printf("La cadena es -> %s\n",big);

} while (big[0] != 'X');

&nbsp;

printf("Fin de programa.\n");

}

Ejecutando el programa vemos que parte a las frases en palabras separadas. Cuando usamos la cadena en modo de entrada, “scanf” lee los caracteres de la cadena hasta que llega al final o se topa con un espacio en blanco. Por tanto, si lee una palabra y un espacio en blanco, imprime el resultado. Dado que estamos en un bucle, el programa continúa leyendo palabras hasta que sature el buffer de entrada del DOS. Hemos escrito el programa para que pare cuando encuentre una X mayúscula en la columna 1, pero ya que la frase se almacena en palabras separadas , el programa se parará cuando encuentre una palabra que empiece en X. Si probamos entrando 5 palabras en una frase, con una X mayúscula en el primer caracter de la tercera palabra, debemos ver las 3 primeras palabras en pantalla y la última, simplemente la ignorará el programa cuando pare.

Entrando más de 24 caracteres puede generarse un error, pero dependerá mucho del sistema que se esté usando. En el programa presente, es responsabilidad del operador contar los caracteres y parar cuando el buffer esté lleno.

Una última observación con las funciones de E/S. Es perfectamente legal mezclar “scanf” y “getchar” durante las operaciones de entrada. De la misma forma, tampoco es incorrecto emplear “printf” y “putchar” juntos.

E/S EN MEMORIA

A continuación vemos otro tipo de E/S que permanece en la memoria del ordenador, la cual no hay que cargar del disco cada vez que la usemos.

INMEM.C


main()

{

int numbers[5], result[5], index;

char line[80];

numbers[0] = 74;

numbers[1] = 18;

numbers[2] = 33;

numbers[3] = 30;

numbers[4] = 97;

sprintf(line,"%d %d      %d %d %d\n",numbers[0],numbers[1],

numbers[2],numbers[3],numbers[4]);

printf("%s",line);

sscanf(line,"%d %d %d %d      %d",&result[4],&result[3],

(result+2),(result+1),result);

for (index = 0;index < 5;index++)

printf("El resultado final es %d\n",result[index]);

}

En “INMEM.C” definimos algunas variables, luego asignamos algunos valores a título de ejemplo a la variable “numbers” y entonces usamos la función “sprintf“. Esta función actúa como un “printf” normal y corriente, excepto en que al contrario que ésta, imprime la línea de manera formateada y la asigna a una variable de cadena, no a la pantalla. En este caso, la cadena va a la variable de cadena “line”, ya que esta es la variable que hemos insertado en el primer argumento del “sprintf”. Los espacios después del segundo %d fueron puestos ahí para ilustrar la siguiente función, que encontramos en la línea. Mostramos el resultado, y encontramos la salida idéntica a la que hubiera producido un “sprintf”.

Dado que la cadena generada está todavía en memoria, podemos leerla ahora con la función “sscanf“, que lee los datos desde una cadena en memoria, no desde teclado. Le decimos a la función en el primer argumento que “line” es la cadena para entradas, y el resto de la línea es exactamente igual que si hubiéramos empleado “scanf”. Es esencial el uso de puntero en los datos, ya que necesitamos recibir los datos de la función. Para ilustrar  que existen diferentes maneras de declarar un puntero, se usan varios métodos, pero todos definen lo mismo. Los dos primeros, simplemente declaran la dirección de los elementos del array, mientras que los tres últimos demuestran que “result” sin subíndice es un puntero. Para darle más interés son leídos al revés. Finalmente, los valores aparecen por pantalla.

¿ES REALMENTE ÚTIL?

Parece un poco estúpido leer datos del ordenador, pero tiene un propósito. Es posible leer datos usando cualquiera de las funciones standard, y hacer una conversión de formato en memoria. Podríamos leer en una línea de datos, ver algunos caracteres significativos, usar rutinas de formateo de entradas, para reducir la línea a una representación interna.

ERRORES STANDARD DE SALIDA

A veces es necesario redireccionar la salida del dispositivo standard a un fichero. Pueden necesitarse algunos mensajes para ir al dispositivo, en este caso el monitor. La siguiente función lo permite.

SPECIAL.C


#include "stdio.h"

main()

{

int index;

for (index = 0;index < 6;index++) {

printf("Ejemplo de función de salida standard.\n");

fprintf(stderr,"Linea de dispositivo de error.\n");

}

exit(4);

}

El programa consiste en un bucle con 2 mensajes de salida, uno en el dispositivo standard de salida y el otro a un dispositivo standard de error. El mensaje al dispositivo standard de error aparece con la función “fprintf” e incluye el dispositivo denominado “stderr”, como primer argumento. Con algún que otro cambio funciona igual que “printf”. Ignoremos la línea con “exit” por el momento, la veremos más adelante.

Ejecutando el programa se verán 12 líneas en la pantalla. Para ver la diferencia, puede ejecutarse otra vez el programa con la salida redireccionada en un fichero, por ejemplo “stuff”, entrando desde DOS la siguiente línea: A>special>stuff. Esta vez  sólo se verán las 6 líneas del dispositivo de error, y si se examina el directorio, se verán las otras 6 contenidas en el fichero STUFF. Puede usarse el redireccionamiento de E/S en cualquier programa que lo precise. También puede usarse esta técnica para leer datos desde un fichero, como lo veremos más adelante.

¿QUÉ ES exit(4)?

Vamos a ver ahora ese extraño mandato, “exit(4)”. Este último mandato simplemente acaba el programa y devuelve el valor 4 al DOS. Cualquier  valor de 0 a 9 es válido en esta función, como diálogo con el DOS. Si se está trabajando con un fichero batch(por lotes), este número puede ser examinado por el mandato “errorlevel”.

Muchos compiladores que operan en varios pasos devuelven 1 como mecanismo para indicar un error crítico ocurrido y podría ser una pérdida de tiempo continuar el programa.

Es siempre aconsejable que el proceso de todo el programa (escritura, compilación y ejecución) se realice desde un fichero batch.

Acerca del autor: Rodrigo Paszniuk

Ingeniero Informático, amante de la tecnología, la música, el ciclismo y aprender cosas nuevas.

Posts Relacionados

  • Manual intermedio de C++
  • Manual básico de C++
  • ESTRUCTURAS Y UNIONES en C++
  • ARREGLOS Y CADENAS en C++



SEGUÍNOS EN FACEBOOK


GITHUB