MC102
Algoritmos e Programação de Computadores
Parte XIII
Norton Trevisan Roman
|
Já vimos como escrever, com write e writeln, e como ler uma entrada, com read e readln. Mas, como lemos mais de uma entrada por vez, sem usarmos vários reads ou readlns?
PROGRAM P;
VAR i1,i2:integer;
BEGIN
read(i1, i2); {lê dois inteiros}
END.
|
Só que para que isso funcione, o usuário deve separar os dois inteiros com um ou mais espaços. Veja algumas entradas válidas:
Da mesma forma, podemos ler outros tipos:
PROGRAM P;
VAR r1,r2:real;
b1,b2:boolean;
BEGIN
read(r1, r2); {lê dois reais}
read(b1, b2); {lê dois booleanos}
END.
|
Algumas entradas válidas são:
2.3e22 2.345E2
true false
-2.3e22 2.45E-2
false true
0.4 -3.980
false false
0.498e-2 12.3
true true
etc
|
Em suma, o read lê de forma diferente os diferentes tipos de entrada (tipo este determinado pela variável que é passada como parâmetro para o read, que receberá a entrada):
- Inteiros: todos os espaços e caracteres de nova linha iniciais são pulados até que se encontre um caracter diferente destes. Se este caracter não for um dígito, ou '+' ou '-', um erro é acusado. Se for dígito, o read vai lendo cada caracter até encontrar um não-dígito, quando o read pára e transforma o monte de dígitos lidos em um inteiro.
- Reais: novamente o read pula espaços e caracteres de nova linha até encontrar um caracter diferente. Esse caracter deve ser '+', '-' ou número, senão um erro é reportado. Então o read vai lendo os dígitos até que encontre um caracter diferente de número, '.', 'e', 'E', 'e+', 'E+', 'e-' ou 'E-', ou seja, as entradas permitidas são do tipo 2, -2, +2, 2.0, +2.0E10, -2.05e-10 etc.
- Booleanos: da mesma forma, o read pula espaços e nova linha até encontrar as palavras 'true' ou 'false seguidas de ' ' ou algum outro delimitador('.', ',' etc).
- Char: o read lê o próximo caracter, sem pular nada.
Em Pascal, caracteres são guardados em um tipo de variável especial, declarada assim:
Aqui, ch é do tipo caracter, o que significa que pode guardar qualquer caracter (mas apenas um), quer seja, letra, número, sinais gráficos etc.
Mas, e como armazenamos caracteres nessas variáveis?
ch := 'a';
ch := 'A';
ch := ' '; {espaço}
ch := '"'; {aspas duplas}
|
Note que 'a' é diferente de 'A', ou seja, há distinção entre caracteres maiúsculos e minúsculos.
Um outro ponto interessante é que caracteres são ordenados e seqüenciais:
'A' < 'B' < 'C' < ... < 'Z'
'a' < 'b' < 'c' < ... < 'z'
'0' < '1' < '2' < ... < '9'
|
Não confunda 9 com '9', o primeiro é um número e o segundo um caracter.
Então podemos fazer:
FOR ch:='A' TO 'Z' DO write(ch);
|
e teremos o alfabeto.
LENDO E ESCREVENDO CARACTERES
|
Para ler um único caracter, fazemos:
onde ch é do tipo char. Já para escrever:
Isso é muito útil na interação com o usuário, por exemplo:
ch := 's';
WHILE (ch<>'n') AND (ch<>'N') DO
BEGIN
faz algo
write('quer continuar? (s/n) ');
readln(ch)
END;
|
ou
REPEAT
faz algo
write('quer continuar? (s/n) ');
readln(ch)
UNTIL (ch='n') OR (ch='N');
|
Novamente, como o usuário pode entrar tanto com 'n' quanto com 'N', e como 'n' ≠ 'N', devo testar os dois.
Agora suponha que temos a seguinte entrada:
e queremos gerar a saída:
Ou seja, pegamos uma frase que termina com '.' e separamos as palavras dela. Como faremos?
enquanto for diferente de '.' (senão acabou a frase)
enquanto for diferente de ' ' (senão ainda é a palavra)
leio uma letra
escrevo essa letra
dou nova linha
|
ou
repito
enquanto for diferente de ' '
leio uma letra
escrevo a letra lida
dou nova linha
até que encontre um '.'
|
mas isso tem um problema, ' ' será lido e escrito. Quero testar antes de escrever, então ler tem que ser a última coisa a fazer:
leio uma letra
enquanto for diferente de '.' (senão acabou a frase)
enquanto for diferente de ' ' (senão ainda é a palavra)
escrevo a letra lida
leio uma letra
dou nova linha
|
mas isso também tem um problema: repare que, se lemos um ' ', entraremos num laço infinito (confira!). Então, para corrigir, devemos fazer:
leio uma letra
enquanto for diferente de '.' (senão acabou a frase)
enquanto for diferente de ' ' (senão ainda é a palavra)
escrevo a letra lida
leio uma letra
dou nova linha
leio uma letra
|
Pronto! Como fica isso em Pascal?
read(ch);
WHILE ch<>'.' DO
BEGIN
WHILE ch<>' ' DO
BEGIN
write(ch);
read(ch)
END
writeln;
read(ch)
END;
|
Mas, em vez de 2 WHILEs, não poderíamos por tudo em um só? Claro, mas teríamos que mudar o código, se fizermos somente:
enquanto for direrente de '.' e diferente de ' ' faça
|
o programa pararia no primeiro espaço, o que não queremos. Então, como fazemos?
read(ch);
WHILE ch<>'.' DO
IF ch<>' ' THEN
BEGIN
write(ch);
read(ch)
END
ELSE BEGIN {é = ' '}
read(ch);
writeln
END
END;
|
Agora, qual o maior problema com esse código? O read exige . Não seria legal o usuário digitar e nós lermos no mesmo isntante, sem precisar do ? Como faríamos isso? Com:
readkey espera que o usuário digite algo e, assim que ele digitar, pega o caracter que ele digitou. Então não preciso do .
Legal, não? Mas como nem tudo é uma maravilha, há um detalhe sobre o readkey: como o nome diz, ele lê uma tecla somente. Você não consegue ler integer, real ou palavras com ele, somente char. Outra coisa, enquanto que com read você escreve, vê o que escreveu na tela e então dá enter, com readkey isso não acontece. Ao você digitar algo, readkey lê diretametne a tecla, sem mostrar o que você digitou na tela. Se você quiser que mostre, terá que ecoar o caracter lido, ou seja, escrevê-lo na tela, com write.
Então o programa fica:
ch := readkey;
WHILE ch<>'.' DO
BEGIN
WHILE ch<>' ' DO
BEGIN
write(ch);
ch := readkey
END
writeln;
ch := readkey
END;
|
CARACTERES E A TABELA ASCII
|
Até agora vimos como usar caracteres, mas como será que o computador entende caracteres? Bem, ele não entende. Quando você digita um caracter, o Sistema Operacional se encarrega de traduzí-lo para um código numérico que ele possa manipular mais tarde. Assim, os caracteres nada mais são do que números. Mas como ele faz essa tradução?
Para fazer a tradução de caracteres em números, o Sistema Operacional usa uma tabela, chamada ASCII (veja no próximo tópico). Existem vários tipos de tabela, dependendo do computador que é usado e do país em que é usado, apesar disso, ela não muda dos números 0 ao 127.
Mas você não precisa decorar essa tabela para brincar com caracteres. O Pascal tem duas funções para lidar com caracteres:
VAR n : integer;
c : char;
BEGIN
n := Ord('a'); {n recebe o código ascii de 'a'}
c := 'z';
n := Ord(c); {n recebe o código ascii do caracter armazenado em c, ou seja, 'z'}
c := Chr(120); {c recebe 'x' (veja tabela abaixo)}
c := Chr(Ord('z')); {c recebe 'z'}
END.
|
Assim, Ord recebe um caracter e retorna o código ascii deste, e Chr recebe um código ascii e retorna o caracter correspondente a este.
Mas para que podemos usar isso? Vejamos alguns exemplos:
Exemplo 1: Uma codificação simples.
Suponha que queremos codificar uma determinada frase de 10 letras digitada, para isso, lemos cada caracter e somamos 5 ao seu ascii, resultando num caracter diferente:
PROGRAM codifica;
VAR n : integer; {ascii de c1}
c1 : char; {caracter digitado}
c2 : char; {caracter codificado}
i : integer; {contador do FOR}
BEGIN
FOR i:=1 TO 10 DO
BEGIN
c1 := readkey; {leio o caracter digitado}
n := Ord(c1); {pego o ascii do caracter digitado}
n := n + 5; {incrementei o código em 5}
c2 := Chr(n); {pego o caracter desse novo código}
write(c2) {escrevo o caracter codificado}
END
END.
|
Exemplo 2: Transformar minúsculas em maiúsculas e vice-versa.
Nesse caso, o Pascal já tem uma função pré-definida
para transformar minúsculas (ch2) em maiúsculas (ch1), apesar disso, não tem uma função para transformar maiúsculas em minúsculas. Então vamos construir a nossa função
Para tal, precisamos de algumas informações:
- Tanto as letras maiúsculas quanto as minúsculas são seqüenciais na tabela ascii, ou seja, se 'C' está ná segunda posição após 'A', 'c' estará na segunda posição após 'a' também.
- Dessa forma, na tabela ascii, 'C' - 'A' = 'c' - 'a'.
- Assim, dado um caracter x (não confunda com 'x'), minúsculo, sei que X - 'A' = x - 'a', ou seja, X = x - 'a' + 'A'. Assim, tenho como calcular a maiúscula de x, a partir da minúscula, x.
- Da mesma forma, dado um caracter maiúsculo X, sei que X - 'A' = x - 'a', ou seja, x = X - 'A' + 'a'. Tenho, então, a minúscula a partir da maiúscula.
Agora, sim, vamos fazer procedimentos que transformem minha minúscula em maiúscula e vice-versa:
PROGRAM transforma;
VAR l : char; {a letra a ser transformada}
{recebe c por referência, e o substitui pela sua maiúscula}
PROCEDURE Maiuscula(VAR c : char);
VAR mai : integer; {guarda o ascii da maiúscula}
BEGIN
IF (c>='a') AND (c<='z') THEN {c é minúscula}
BEGIN
mai := Ord(c) - Ord('a') + Ord('A');
c := Chr(mai)
END
{se não for minúscula (ou mesmo não for letra), deixa como está}
END;
{recebe c por referência, e o substitui pela sua minúscula}
PROCEDURE Minuscula(VAR c : char);
VAR mai : integer; {guarda o ascii da maiúscula}
BEGIN
IF (c>='A') AND (c<='Z') THEN {c é maiúscula}
BEGIN
mai := Ord(c) - Ord('A') + Ord('a');
c := Chr(mai)
END
{se não for maiúscula (ou mesmo não for letra), deixa como está}
END;
BEGIN
l := 'd';
Minuscula(l);
writeln(l); {escreve 'd', não alterou}
Maiuscula(l);
writeln(l); {escreve 'D', a maiúscula}
l := 'P';
Maiuscula(l);
writeln(l); {escreve 'P', não alterou}
Minuscula(l);
writeln(l) {escreve 'p', a minúscula}
END.
|
note que o procedimento "Maiuscula" poderia ser tão somente:
{recebe c por referência, e o substitui pela maiúscula dela}
PROCEDURE Maiuscula(VAR c : char);
BEGIN
IF (c>='a') AND (c<='z') THEN {c é minúscula}
c := Chr(Ord(c) - Ord('a') + Ord('A'))
{se não for minúscula (ou mesmo não for letra), deixa como está}
END;
|
mas fizemos mais longo para ficar mais legível a operação.
Essa é a tabela usada em linux (ISO 8859-1):
Dec Char Dec Char Dec Char Dec Char
-------------------------------------------------------------------------
0 NUL '\0' 32 SPACE 64 @ 96 `
1 SOH 33 ! 65 A 97 a
2 STX 34 " 66 B 98 b
3 ETX 35 # 67 C 99 c
4 EOT 36 $ 68 D 100 d
5 ENQ 37 % 69 E 101 e
6 ACK 38 & 70 F 102 f
7 BEL '\a' 39 ' 71 G 103 g
8 BS '\b' 40 ( 72 H 104 h
9 HT '\t' 41 ) 73 I 105 i
10 LF '\n' 42 * 74 J 106 j
11 VT '\v' 43 + 75 K 107 k
12 FF '\f' 44 , 76 L 108 l
13 CR '\r' 45 - 77 M 109 m
14 SO 46 . 78 N 110 n
15 SI 47 / 79 O 111 o
16 DLE 48 0 80 P 112 p
17 DC1 49 1 81 Q 113 q
18 DC2 50 2 82 R 114 r
19 DC3 51 3 83 S 115 s
20 DC4 52 4 84 T 116 t
21 NAK 53 5 85 U 117 u
22 SYN 54 6 86 V 118 v
23 ETB 55 7 87 W 119 w
24 CAN 56 8 88 X 120 x
25 EM 57 9 89 Y 121 y
26 SUB 58 : 90 Z 122 z
27 ESC 59 ; 91 [ 123 {
28 FS 60 < 92 \ '\\' 124 |
29 GS 61 = 93 ] 125 }
30 RS 62 > 94 ^ 126 ~
31 US 63 ? 95 _ 127 DEL
|
Códigos ASCII extendidos:
‹— Parte XII — Página da disciplina — Parte XIV —›