Ir para o conteúdo

PARTE IV - Registos. Ficheiros de Texto Simples.

Segue-se agora para uma parte do Pascal mais complexa e que começa a mostrar novas potencialidades desta linguagem de programação. Trabalhar-se-á com variáveis do tipo Record, extremamente importante em Pascal, e dar-se-ão as bases sobre as variáveis Text, variáveis que permitem criar ficheiros de texto simples. Serão dados de igual forma códigos úteis sobre strings e sobre o teclado e o cursor.

1. A variável Record

Estas variáveis são extremamente úteis para, por exemplo, programas de registos. Uma variável Record tem várias “subvariáveis”, designadas por campos. Eis um exemplo da declaração de uma variável Record:

var registo : record
              nome : string;
              idade : integer;
              sexo : char;
    end;

Para pedir ao utilizador, por exemplo, para registar a idade, fá-lo-á do seguinte modo:

write('Registe a idade: '); readln(registo.idade);

Ou seja, a variável Record faz-nos lembrar um formulário onde devemos preencher os vários campos, por exemplo num CV. A variável Record é um formulário, e as suas várias “subvariáveis” (campos) são os campos desse formulário. É possível criar uma “resma” de formulários sob a forma de um array de Record – isto revela que é possível criar milhares de campos em memória, organizados por grupos individuais, de uma forma muito simples e eficaz, sem recorrer a milhares de variáveis. Ou seja, em vez de termos as variáveis nome1, nome2, nome3, idade1, idade2, idade3, sexo1, sexo2 e sexo3, temos três registos (Record), cada um com os seus campos idade, nome e sexo. Em código:

type tipo_reg = record
              nome : string;
              idade : integer;
              sexo : char;
     end;

var registo : array [1..3] of tipo_reg;

Então, pode-se preencher os 3 formulários (array [1..3]) recorrendo a um ciclo for.

for i:=1 to 3 do begin
    writeln('A FAZER REGISTO NUMERO ',i);
    write('Nome: '); readln(registo[i].nome);
    repeat
          write('Sexo (M/F): '); readln(registo[i].sexo);
    //só aceita os caracteres “M” e “F”:
    until (upcase(registo[i].sexo) = 'M') or (upcase(registo[i].sexo) = 'F');
    repeat
          write('Idade: '); readln(registo[i].idade);
    // a idade é positiva ou nula:
    until (registo[i].idade >= 0);
    writeln('REGISTO ',i,' DE 3 EFECTUADO.');
    writeln;
end;

Da mesma forma, estes registos podem ser escritos com um ciclo for:

for i:=1 to 3 do begin
    writeln('A VER REGISTO NUMERO ',i);
    writeln('Nome: ',upcase(registo[i].nome));
    writeln('Sexo: ',upcase(registo[i].sexo));
    writeln('Idade: ',registo[i].idade);
    writeln;
end;

Assim acabou por ser criado um programa completo que realiza um registo básico de 3 pessoas, escrevendo o que foi introduzido no final. Este fica, então, como um programa exemplo sobre este tipo de variáveis, essenciais em Pascal:

program variavel_record;
uses crt;

type tipo_reg = record
              nome : string;
              idade : integer;
              sexo : char;
     end;

var registo : array [1..3] of tipo_reg;
    i : integer;

begin
     for i:=1 to 3 do begin  // REGISTO
         writeln('A FAZER REGISTO NUMERO ',i);

         write('Nome: '); readln(registo[i].nome);  // registo do NOME

         repeat  // registo controlado do SEXO
               write('Sexo (M/F): '); readln(registo[i].sexo);
         until (upcase(registo[i].sexo) = 'M') or (upcase(registo[i].sexo) = 'F');

         repeat  // registo controlado da IDADE
               write('Idade: '); readln(registo[i].idade);
         until (registo[i].idade >= 0);

         writeln('REGISTO ',i,' DE 3 EFECTUADO.');
         writeln;
     end;

     writeln;
     writeln;

     for i:=1 to 3 do begin  // ESCRITA
         writeln('A VER REGISTO NUMERO ',i);
         writeln('Nome: ',upcase(registo[i].nome));
         writeln('Sexo: ',upcase(registo[i].sexo));
         writeln('Idade: ',registo[i].idade);
         writeln;
     end;

     readln;  // pausa até ENTER
end.

Propostas de Exercícios 8. Crie um programa que registe os dados sobre uma turma inteira e faça a estatística básica. Serão pedidos o número de alunos, e o nome e as avaliações a Matemática e a Português de cada um. No final, o programa faz a estatística e diz quais os alunos aptos a ir a Exame Nacional (média igual ou superior a 9,5) de cada disciplina, qual a média da turma a Matemática e a média a Português. Normas:

  • Deverá ser utilizado um só Record, que inclua todos os campos;
  • Recorra a ciclos for para percorrer os campos;
  • No máximo, só poderão existir 30 alunos;
  • Limite de linhas de código (exclusive comentários e linhas em branco): 100 linhas.

1.1. A cláusula With

Existe um código, também denominado, neste caso, de cláusula, que permite “pegar” num Record e trabalhar com ele, sem que se tenha de escrever constantemente o nome do registo. Por exemplo:

with registo[2] do begin
     nome := 'Thoga31';
     idade := 0;
     sexo := 'M';
end;

Substitui:

registo[2].nome := 'Thoga31';
registo[2].idade := 0;
registo[2].sexo := 'M';

Ou seja, simplifica muito a escrita de código, e permite maior legibilidade.

2. Códigos úteis sobre o teclado e o cursor

2.1. Posição do cursor

Até ao momento, os programas têm sido direccionados para as consolas DOS. Como se sabe, a consola DOS tem sempre o cursor a piscar, numa determinada posição. Em Pascal é possível controlar esta posição através de um comando extremamente simples. Considere-se a janela como um gráfico cartesiano, só de valores inteiros. Seja X a abcissa (horizontal) e Y a ordenada (vertical). A posição inicial do cursor na consola é no canto superior esquerdo: X=1 e Y=1. Ande-se duas linhas para baixo: X=1 e Y=3. Ande-se com o cursor nove colunas para a frente (direita): X=10 e Y=3. Se quisermos posicionar o cursor nesta última posição:

gotoxy(10, 3);

gotoxy(x, y) Este código só é utilizável quando declarada a biblioteca crt. Caso contrário, é um código não declarado.


2.2. Determinação da tecla premida

É possível gravar a qualquer altura qual a tecla premida por um utilizador. Esta função é extremamente útil na criação de menus apelativos. O utilizador em vez de escrever, por exemplo, “1” e, de seguida, premir ENTER, bastar-lhe-á premir a tecla “1” e, se for uma opção válida do menu apresentado, avança imediatamente, sem ser necessário premir ENTER. Por exemplo:

uses crt;  // biblioteca OBRIGATÓRIA
var tecla : char;

// (. . .)

writeln('MENU');
writeln('1 > Repetir operacao');
writeln('2 > Sair');

repeat
      tecla := readkey;
      // grava na variável TECLA qual a tecla (key) premida pelo utilizador
until (char(tecla) = '1') or (char(tecla) = '2');

O código readkey determina a tecla premida pelo utilizador, gravando-a numa variável char. Este código só é utilizável quando declarada a biblioteca crt. Caso contrário, é um código não declarado.



Propostas de Exercícios 9. Crie uma calculadora que grave num Record, em campos distintos, os dois termos da operação, e o operador. O utilizador deverá escolher o operador sem ter de premir ENTER, segundo uma lista dos operadores disponíveis (soma, subtracção, multiplicação e divisão). Preveja situações de erro, e permita que o utilizador as corrija. O utilizador, com um novo menu, sem recurso ao ENTER, irá escolher se deseja sair do programa ou realizar novo cálculo. Utilize coloração de texto variada, e não de fundo. O ecrã deverá ter fundo cinza claro. Normas:

  • Deverá ser utilizado um só Record;
  • Limite de linhas de código (exclusive comentários e linhas em branco): 100 linhas.

3. A variável Text – escrita e leitura de ficheiros de texto simples – parte 1

O Pascal tem a capacidade de trabalhar com ficheiros. Existem duas variáveis para o fazer, contudo vamos abordar apenas aquela que permite escrever e ler ficheiros de texto simples, ou seja, ficheiros cujo conteúdo não passa de texto simples, não formatado: é a variável Text, cujo nome denuncia o tipo de ficheiros que vai trabalhar. O funcionamento desta variável é em tudo diferente das variáveis com que já trabalhámos até agora.

Vamos, então, estudar esta variável por partes e com exemplos práticos. O primeiro programa apresentado cria um ficheiro TXT e grava nele o texto “Hello World! For Pascal TEXT!”. O ficheiro é nomeado de texto.txt.

program escrever_ficheiro;
var ficheiro : text;

begin
     assign(ficheiro, 'texto.txt');
     // “assina” em memória o ficheiro de texto simples, e cria-o junto do executável
     rewrite(ficheiro);
     // ordena a re-escrita do ficheiro – caso o ficheiro já exista e contenha texto, este será eliminado
     write(ficheiro, 'Hello World! For Pascal TEXT!');
     // escreve no ficheiro o texto
     close(ficheiro);
     // “encerra” o ficheiro em memória
end.

Quando se tenta aceder a um ficheiro de texto simples que esteja “assinado” em memória por um programa deste género, aparecerá a famosa Message Box a dizer que o ficheiro está a ser utilizado por outro processo. Isto ajuda a explicar o que acontece no sistema aquando o código assign.

Agora, vamos criar um novo programa, que ficará no mesmo directório, e que vai abrir o ficheiro anterior e contar o número de espaços do texto que contém:

program ler_ficheiro;
var ficheiro : text;
     s : string;
     contador, i : integer;

begin
     assign(ficheiro, 'texto.txt');
     reset(ficheiro);
     // “reinicia” o ficheiro, ou seja, acede-lhe mas não pode re-escrever dados nele, só pode ler
     readln(ficheiro, s);
     contador := 0;
     for i:=1 to length(s) do  if s[i] = ' ' then contador += 1;
     // a linha de código anterior recorre à Optimização
     writeln('Existem ',contador,' espacos no ficheiro.');
     close(ficheiro);
     readln;
end.

Desafio 5 Investigue sobre o CV (Curriculum Vitae) europeu – o Europass. Crie um programa que crie um CV deste modelo, cumprindo todos os campos, excepto o da Fotografia. O programa irá registar os dados do utilizador e, de seguida, irá mostrar por passos o que o utilizador introduziu. No caso de confirmação, deverá gravar o CV num ficheiro TXT cujo nome será “CV_nome”, onde “nome” é o nome do utilizador, sem espaços. Os campos não preenchidos não deverão aparecer no ficheiro. Caso o utilizador pretenda alterar um campo, tal deverá ser permitido sem que, para tal, seja necessário refazer o CV todo. Normas:

  • O CV deverá estar registado num só Record, que inclua Records das partes, e dentro de cada parte os seus campos respectivos;
  • O CV só será gravado em ficheiro no fim, aquando a confirmação final por parte do utilizador;
  • O utilizador poderá cancelar o CV a qualquer altura, escrevendo “<cancelar>”;
  • Limite de linhas de código (exclusive comentários e linhas em branco): livre

4. Códigos úteis sobre string

Seja a variável s uma String com o seguinte texto:

 BEM VINDO
pos('M', s);

Determina a posição do primeiro “M” que encontrar: neste caso, 3. Devolve um valor Integer, a ser gravado numa variável deste tipo.

delete(s, 7, 3);

Elimina 3 caracteres a partir da posição 7, inclusive. A string passa a ser “BEM VI”.

insert('- ', s, 4);

Insere, na posição 4, o texto “- ”. A string passa a ser: “BEM – VINDO”.


Propostas de Exercícios 10. Crie um programa em que o utilizador introduza uma string e possa realizar testes variados com ela. A original deverá manter-se intacta, sendo uma string auxiliar a utilizada para as operações. O utilizador poderá escolher a posição, os caracteres e textos, entre outros, relacionados com os códigos anteriores. Normas: livre.


5. Aleatoriedade

Uma das capacidade do computador é o cálculo de números aleatórios. Estes números não são propriamente aleatórios - chama-se a este processo pseudo-aleatoriedade, isto porque o computador não tem a capacidade de "pensar" num número aleatoriamente. Existem diversos algoritmos, praticamente todos baseados no tempo. Este princípio baseia-se, geralmente, num Timer do computador em que, quando é requerido um número pseudo-aleatório, este calcula-o segundo uma fórmula que inclui os limites mínimo e máximo do conjunto no qual deve estar o número, e o Timer. É devolvido então o resultado de um cálculo, e não um número totalmente aleatório. Em Pascal, é necessário, na generalidade dos compiladores, "activar" este Timer para que possam ser debitados valores pseudo-aleatórios.

randomize;

Não existe nenhum procedimento que "pare" o Timer. Contudo, esta última instrução pode ser constantemente utilizada sem "efeitos". O Pascal só admite conjuntos de 0 a maxint (i.e., o número máximo inteiro positivo suportado), inclusive. Para requerer um número pseudo-aleatório que pertença a um conjunto de 0 a n, inclusive:

random(n);

Esta função devolve um número real. Crie-se, então, um pequeno programa que debite em toda a consola um código binário aleatório:

program binario;
uses crt;
var i, j : integer;

begin
     randomize;
     for i:=1 to 20 do begin
         for j:=1 to 80 do begin
             gotoxy(j,i);
             write(TRUNC(RANDOM(2)));
         end;
     end;
     readln;
end.

É utilizada a função Trunc devido ao facto de Random devolver números tais como, por exemplo, 0.365, 1.845 (aqui abreviados). Assim, apenas é mostrado o valor inteiro do número, não arredondado - ou 0 ou 1, tal como desejado.


Propostas de Exercícios 11. Simule a Lei dos Grandes Números num programa que irá "lançar" um dado cúbico regular um milhão (1.000.000) de vezes. O programa só deverá gerar como output o número de vezes que "saiu" cada face. Tenha em conta que não existe a face 0 (zero), isto por causa do "defeito" do comando Random - neste caso, o computador deverá calcular nova face, sem que conte como um lançamento. Normas: livre.


6. Arrays dinâmicos

A utilização de Object Pascal (nomeadamente com Delphi) veio trazer inovações à programação em Pascal, e uma dessas inovações foi a introdução de arrays dinâmicas. Estas arrays são declaradas como qualquer outra array, mas sem especificarmos a sua dimensão (fazemos isso mais tarde com SetLength):

program dynarrays1;

var
  a: array of integer;

begin
  setlength(a, 2);
  a[0] := 1;
  a[1] := 2;
  writeln('Soma: ', a[0] + a[1]);
end.

Como podem reparar, as arrays dinâmicas têm índice de base 0; não é possível alterar isto.

Uma nota sobre a utilização destas arrays: sempre que chamamos SetLength o programa tem que alocar a memória correspondente ao segundo parâmetro multiplicado pelo tamanho (em bytes) do tipo de dados armazenado na array, e seguidamente copiar para lá a array antiga. Isto torna-se extremamente ineficiente se fizermos muitos redimensionamentos das arrays, e por isso esta opção dinâmica deve ser utilizada com consciência dos seus defeitos.

No caso das arrays dinâmicas multidimensionais, a sua declaração e utilização faz-se da seguinte forma:

program dynarrays2;

var
  a: array of array of integer;

begin
  // equivalente a array [0..9, 0..1] of integer
  setlength(a, 10, 2);

  // array [0..1] of array of integer (uma array fixa de arrays dinâmicas)
  setlength(a, 2);

  // podemos depois especificar o tamanho de cada uma das "subarrays"
  setlength(a[0], 4);
  setlength(a[1], 3);

  // ...
end.