Entrada e Saída de Dados via Dispositivos
Padrão
O Modelo de Fluxo de Caracteres
A grande maioria das linguagens de programação adota um modelo de fluxo de caracteres para as
operações de entrada e saída de dados representados de forma simbólica. Conceitualmente estes fluxos
podem ser considerados como fitas potencialmente infinitas com os caracteres registrados
sequencialmente. No modelo conceitual, associado a um fluxo, temos um cabeçote de leitura ou escrita,
de acordo com a natureza do fluxo (i.e., se ele é de entrada ou de saída).
No caso de um fluxo de entrada, o cabeçote se encontra entre o último caractere consumido pela operação
anterior e o seu sucessor no fluxo. Quando o fluxo for de saída, o cabeçote encontra-se após o último
caractere registrado.Nestes fluxos podemos ter todo tipo de caractere, inclusive os de controle e de
formatação de texto, como caracteres de tabulação e nova linha. A apresentação bidimensional de texto é
efetuada por um dispositivo de saída (como uma impressora ou um monitor) para o qual é direcionado
um fluxo de caracteres de acordo com os caracteres de controle nele registrados. Um fluxo tem, assim,
uma natureza linear por ser constituído de uma sequência de caracteres que visualmente é apresentada de
forma bidimensional para o usuário por dispositivos de saída. Um fluxo
aparecerá no monitor como
Na representação acima, o CR e o LF representam caracteres de controle de formatação,
denominados carriage return e line feed, respectivamente. O primeiro retorna o cursor para o início da
linha corrente e o segundo avança uma linha.
Na linguagem C, as operações de entrada e saída padrão encontram-se definidas na biblioteca stdio.h e,
para usá-las, é preciso incluir, no código de um programa, a seguinte linha no início do arquivo:
#include <stdio.h>
Entrada via Dispositivo Padrão
O fluxo de entrada padrão é identificado na linguagem C por sdtin e está associado ao teclado, via de
regra. Tudo que digitarmos no teclado durante a execução de um programa vai para tal fluxo. Ao
pressionarmos a teclaENTER (nova linha), ela gera um ou dois caracteres, dependendo do sistema
operacional. No caso do Windows, dois caracteres são gerados: o de mudança de linha (LF: line feed, de
índice 10 na tabela ASCII) e o de "retorno de carro" (CR: carriage return, de índice 13). A
tecla ENTER sinaliza uma "nova linha"e é representado, na linguagem C, por\n.
Para a entrada elementar de dados através do dispositivo padrão de entrada, que existem as
funções getchar escanf. A função getchar é usada sem parâmetros e ela retorna o próximo caractere
no fluxo de entrada padrão. Exemplo:
c = getchar();
A função scanf requer dois parâmetros: um formato entre aspas e um nome de uma variável precedida
pelo caractere &. Tal caractere indica que queremos que a função scanf altere o valor da variável
mencionada na lista de parâmetros. Os formatos possíveis são:
Especificador
de Formato
Tipo
%d
int
%f
float
%lf
double
%c
char
%s
cadeia de caracteres (sequência de caracteres terminada por um
branco ou por uma marca de "nova linha")
Exemplo:
scanf("%d", &numeroDeParcelas);
A função scanf busca um valor do tipo indicado pelo formato. Se encontrado, ele é convertido em um
valor interno. No caso de scanf("%d", &numeroDeParcelas), a função scanf procura por uma
sequência de caracteres que representem os dígitos de um inteiro possivelmente precedidos por um
sinal + ou -. Neste caso são descartados eventuais caracteres brancos e, a partir do primeiro caractere não
branco, a operação de leitura assume que encontrou uma cadeia de caracteres que está em acordo com a
sintaxe de inteiros. A linguagem C, contudo, não é muito seletiva. Aceita qualquer coisa e gera como
resultado um valor inteiro qualquer (lixo). A operação de leitura continua a consumir caracteres até que
encontre novamente um caractere branco. Caso fornecida uma representação simbólica de um inteiro
válido, então, durante o processo de leitura, a sequência de caracteres lida é convertida em um valor
binário equivalente ao valor correspondente à representação simbólica que acaba de ser "consumida".
Se tivermos, por exemplo, um fluxo de entrada
e o comando
scanf("%d", &k);
for executado, onde k é uma variável inteira, então o fluxo ficará na seguinte situação após a operação de
leitura disparada pelo comando
e à variável k estará associada ao valor binário 10010. Os caracteres anteriores ao cabeçote representado
pelo triângulo já foram consumidos e não podem ser lidos novamente.
Acima falamos que caracteres brancos são descartados. Isto não só ocorre estritamente com o caractere
branco (o caractere de índice 32 na tabela ASCII), mas também com caracteres de formatação de texto
como o de tabulação (o de índice 25), o de mudança de linha (LF: line feed, de índice 10) e o de "retorno
de carro" (CR: carriage return, de índice 13). Portanto, quando falamos no descarte de caracteres brancos,
estamos falando no "sentido amplo" que abrange também aqueles na categoria de caracteres de
formatação de texto.
Voltando ao exemplo acima, se digitarmos um caractere branco, um caractere 1, um caractere 2 e
pressionarmos a tecla ENTER, supormos que temos uma variável inteira m e executarmos o comando
scanf("%d", &m);
então o fluxo ficará no seguinte estado após a operação de leitura
e à variável m estará associado internamente o valor binário 10100.
O esquema acima funciona para valores de tipos primitivos, exceto quando da leitura de caracteres para
variáveis do tipo char. Neste caso, os caracteres no fluxo de entrada são consumidos sequencialmente
sem o descarte dos caracteres branco no "sentido amplo." Isto é, se for solicitada a leitura de um caractere
e o próximo no fluxo de entrada for um branco no "sentido amplo," então ele é lido e ele representa o
resultado desta particular operação de leitura.
Quando queremos "esvaziar" o fluxo de entrada, isto é, descartar os caracteres ainda não consumidos do
fluxo, então utilizamos a função fflush da seguinte forma:
fflush(stdin);
Suponhamos que agora digitássemos o caractere S e pressionássemos, logo a seguir, a tecla ENTER.
Teríamos, então, a seguinte situação no fluxo de entrada:
Se executássemos, agora, o comando
c = getchar();
ou o comando equivalente
scanf("%c", &c);
onde c representa uma variável do tipo char, então seria lido o caractere logo após o cabeçote
e c assumiria o valor correspondente ao caractere CR ao invés do caractere S, como provavelmente
pretendíamos.
Portanto, para simplesmente "descartar o resto do fluxo de entrada" e posicionar o cabeçote no "início do
fluxo", executamos o comando fflush(stdin). A seguir digitamos o caractere S e pressionássemos,
logo a seguir, a tecla ENTER. O fluxo assume, então, a seguinte configuração:
Para ler o valor S, portanto, escreveríamos um código como:
fflush(stdin);
c = getchar();
Após a execução desse trecho de código, a configuração do fluxo de entrada seria a seguinte:
Quando misturamos a leitura de valores do tipo char (como quando utilizamos um caractere para
representar uma seleção de uma opção em relação a um leque de alternativas, por exemplo) com valores
de outros tipos, precisamos, portanto, tomar cuidado. Suponhamos que foi lido um inteiro que o usuário
forneceu ao sistema, via teclado, seguido por um ENTER, e que após esta operação queiramos ler uma
opção como S ou N fornecida em decorrência da solicitação "Você que continuar (s/n)?", por
exemplo. Se o primeiro dado foi lido com uma operação scanf("%d",&n), os caracteres de controle que
indicam uma "nova linha" que seguem o inteiro lido, isto é, o CR e o LF, continuam lá no fluxo de entrada
e a seguir é lido o CR ao invés do S ou N como gostaríamos. Para eliminar o problema acima, precisamos
esvaziar o fluxo de entrada com a operação fflush(stdin).
Ao digitarmos dados via teclado eles são "ecoados" na tela do monitor do computador, isto é, eles são
mostrados na tela conforme vão sendo digitados. Enquanto não pressionarmos a tecla ENTER, o
processo de leitura não é disparado. Caso não estejam disponíveis mais caracteres no fluxo de entrada, o
programa suspende a execução da operação de leitura que está demandando dados do usuário. Ao ocorrer
o disparo via o pressionar da tecla ENTER, a execução da aplicação é retomada no ponto em que foi
suspensa.
Saída via Dispositivo Padrão
O fluxo padrão de saída é chamado de stdout na linguagem C e é, normalmente, associado ao monitor
do computador. Saídas elementares neste dispositivo são geradas através das funções putchar e printf.
Estas operações adicionam caracteres ao fluxo de dados associado ao dispositivo padrão, onde são
devidamente visualizados. A funcção recebe um parâmetro do tipo char. Exemplo:
putchar('a');
putchar(letra);
onde 'a' representa a uma constante equivalente à letra a e letra representa um variável do tipo char.
A função printf recebe dois ou mais parâmetros. O primeiro é o formato que descreve como os valores
associados às variáveis que se seguem devem ser colocados no fluxo de saída. A cadeia de caracteres que
compõem o formato contém, além de caracteres a serem colocados no fluxo de saída padrão,
especificadores de formato (um para cada variável que se seguem ao formato associados na mesma
ordem). Seguem-se os especificadores mais usuais utizados para especificar o formato de uma
função printf:
Especificador
de Formato
Descrição
%d
inteiro
%o
octal
%x
hexadecimal
%f
número em ponto flutuante
%c
caractere
%s
cadeia de caracteres
%%
exibe um %
Se quisermos incluir uma marca de "nova linha" no fluxo de entrada, então precisamos indicar isto no
formato através da inclusão de \n no ponto desejado.
Suponha que a variável k contenha o valor 18 (10010, em binário) e a variável m o valor 20 (10100, em
binário). Se você executar o comando
printf("%d%d", k, m);
e o cursor se encontrar no início de uma linha, então os valores binários das variáveis são convertidos
para a sua representação simbólica e você terá, nesta linha, o seguinte texto
onde _ representa a posição do cursor após a execução de tais comandos. Conforme você pode observar,
os dois valores encontram-se justapostos. Se você quiser evitar isto, você terá que ajustar o formato (o
primeiro parâmetro da função):
printf("%d %d", k, m);
Na situação acima, você obterá a seguinte linha de saída:
Se você quisesse que o cursor fosse posicionado para o início da linha seguinte após a operação, então o
comando deveria ser:
A saída correspondente então seria
Como você pode observar nos exemplos acima, o espaço para representar os valores dos parâmetros na
sua forma simbólica é a menor possível e, portanto, o espaço ocupando depende de tais valores. Se você
quiser ter mais controle sobre o espaço a ser ocupado, você usa parâmetros de formatação. Por exemplo,
o comando
printf("%5d %3d\n", k, m);
indica que para a apresentação simbólica do valor de k será utilizado um "campo" com, no mínimo, 5
posições e para a apresentação simbólica do valor de m será utilizado um "campo" de, no mínimo, 3
posições. Em cada campo também é acomodado o sinal - caso o valor seja negativo. Caso menos do que
5 posições forem necessárias para a representação do valor de k, por exemplo, então a representação é
alinhada junto à margem direita do "campo" e as posições não ocupadas à esquerda são "preenchidas"
com caracteres brancos.
Para reais, além de um possível valor para indicar um comprimento mínimo do "campo", podemos ter
outro precedido por um ponto para indicar a precisão com que será escrito o valor no fluxo de saída
padrão, isto é, quantas casas decimais após o "ponto decimal" serão usados. Um campo acomoda o sinal
do número, a parte inteira, o ponto decimal e a parte fracionária.
Suponha que x contenha o valor 17.527. Se o cursor estiver no início de uma linha e você executar o
comando
printf("%5.2f\n",x);
então você obterá o seguinte resultado:
Como no caso de inteiros, valores que não ocuparem todo o campo alocado são precedidos por caracteres
brancos.
Exercícios
1. Crie um programa que gere uma tabela de duas colunas, onde os elementos da primeira coluna
variam de 1 a 10 e cada elemento da segunda coluna representa o valor correspondente da
primeira coluna multiplicado porn. O valor de n deve ser fornecido pelo usuário. O seu programa
gera colunas alinhadas?
2. Crie um programa que conte quantos caracteres contém uma linha de entrada fornecida pelo
usuário e informe tal valor.
3. Adapte o programa acima para que ele consulte o usuário, após cada geração de um resultado,
para verificar se este quer ou não repetir uma outra operação de contagem com uma nova linha de
dados.
Download

Entrada e Saída de Dados via Dispositivos Padrão