Turbo Pascal Forever – Input/Output

Amigos, após um tempo sem posts, volto ao nosso curso de Pascal certo de que a fase inicial de fundamentação na linguagem já passou e que agora estamos aptos a avançar para tópicos de complexidade intermediária, ficando cada vez mais avançados.

Com base nisso vamos a uma das fundações mais importantes de qualquer linguagem  computacional e um dos pilares  da computação, que é a camada de I/O (Input/Output), ou em português, E/S (Entrada/Saída) de dados.

Tudo na computação envolve entrada e saída de dados, desde uma simples tecla digitada no teclado do computador, leitura/gravação de arquivos em um disco rígido, até a transferência de dados em grande escala através de dispositivos mais complexos de comunicação, como as placas de redes, seja com fio (Ethernet) ou sem fio (Wireless).

Por ser uma das principais fundações da computação, as linguagens computacionais implementam o conceito de E/S de forma bem abstrata na maioria dos casos, e principalmente contam com a possibilidade de futuras extensões a seu mecanismo de E/S padrão, visando o operações de E/S em novos dispositivos.

Feita a devida introdução, como de costume, vamos a….

 …E/S no Turbo Pascal. 

Os dispositivos de E/S mais comuns que utilizamos em  um computador, principalmente nos computadores antigos, é o teclado e o monitor de vídeo, sendo o teclado reconhecido com um dispositivo apenas de entrada de dados e o vídeo um dispositivo apenas de saída.

Os dispositivos de armazenamento (disquetes, hard disks, …) por sua vez, são dispositivos tanto de entrada quanto de saída, uma vez que é possível ler e escrever dados nos mesmos.

Como podemos ver, existem diversos dispositivos e se para cada um desses dispositivos padrão existisse uma forma proprietária para acessá-lo, teríamos uma verdadeira Torre de Babel na programação de novos dispositivos.

Felizmente os sistemas operacionais existem para abstrair uma série de dispositivos de tal forma que os mesmos sejam reconhecidos e acessados de maneira uniforme e transparente para o desenvolvedor, que por sua vez não precisa se preocupar com os detalhes internos do dispositivo em uso.

Felizmente tanto o MSX-DOS quanto o seu pai, o CP/M80, padronizaram alguns dispositivos de E/S, conhecidos como dispositivos lógicos. Abaixo segue a lista de dispositivos lógicos padronizados pelo CP/M e consequentemente o MSX-DOS:

CON: Console, ou a tela de texto do computador;
TRM: Terminal. Geralmente é redirecionado para o console ou CON;
LST: Dispositivo de listagem ou impressora;
AUX: Dispositivo de comunicação auxiliar. Também conhecido como COM:;
COM: Dispositivo de comunicação externo, comumente conhecido como COM1, ou dispositivo de comunicação externo, como uma porta serial RS-232;
USR: Dispositivo do usuário. Geralmente uma peça de software, conhecida como device driver, carregada pelo sistema operacional para responder às chamados ao dispositivo de hardware e/ou software a ser controlado. Podemos associa-lo aos modernos  device drivers, encontrados nos sistemas operacionais modernos, só que aqui em seu estado mais primitivo;
KBD: Dispositivo de teclado;

Cada um desses dispositivos lógicos está mapeado no Turbo Pascal a arquivos  pré-definidos, conforme descrito abaixo:

Input, associado ao dispositivo CON:
Output, associado ao dispositivo CON:
Con, associado ao dispositivo CON:
Trm, associado ao dispositivo TRM:
Kbd, associado ao dispositivo KBD:
Lst, associado ao dispositivo LST:
Aux, associado ao dispositivo AUX:
Usr, associado ao dispositivo USR:

Conhecendo agora os dispositivos padrão suportados pelo Turbo Pascal, vamos verificar a seguir como interagir com esses dispositivos, lendo ou escrevendo informações para os mesmos.

Rotinas padrão de E/S.

Como se trata de Entrada e Saída, o procedimento padrão implícito nessas operações é ler no caso de requisição de entrada de dados e escrever no caso de requisição de saída de dados. No Turbo Pascal essas operações são feitas ou para arquivos no FileSystem do sistema operacional, ou para qualquer um dos arquivos mapeados, conhecidos como file handlers, para os dispositivos, conforme descrito anteriormente.

Procedure Write e WriteLn

Para escrever uma informação em um determinado arquivo, ou dispositivo, utilizamos o comando Write e WriteLn, que já utilizamos bastante durante o tutorial. Segue abaixo a definição do mesmo:

Write( Var1, Var2, Var3, ...VarN );

Onde Var1, Var2, Var3, …VarN, são variáveis e/ou constantes a serem escritas no dispositivo padrão do Turbo Pascal, que no caso é o arquivo pré-definido, Con, conforme pode ser visto no código exemplo abaixo.

Program TestWrite;
Var     strText : String[50];

Begin
  strText := 'And this is a variable.';
  Write( 'This a constant for Write Procedure.', strText );
End.

Entretanto, como citamos anteriormente, é possível escrever em outros dispositivos diferentes do padrão (Con)  do comando Write, e para isso você deve especificar a variável do arquivo associada ao dispositivo desejado, como pode ser visto na definição abaixo:

Write( VarFile, Var1, Var2, Var3, ...VarN );

Onde VarFile é a variável associada ao arquivo/dispositivo em que se deseja escrever. Segue abaixo um exemplo onde ao invés de ter a saída direcionada para o dispositivo Con (Console), o mesmo é direcionado para a impressora conectada ao micro, lógicamente que só funcionará em computadores com a impressora conectada e senão estiver conectada, o seu programa ficará travado até que se pressione CTRL+STOP.

Program TestPrinter;
Var     strText : String[50];

Begin
  strText := 'And this is a variable.';
  Write( Lst, 'This a constant for Write Procedure.', strText );
End.

No caso acima, utilizamos o arquivo/dispositivo pré-mapeado do Turbo Pascal, Lst, que já está devidamente associada ao dispositivo LST: do sistema operacional MSX-DOS.

A procedure WriteLn tem sua sintaxe e uso idêntico a de Write, entretanto WriteLn, salta uma linha, inserindo um Carriage Return/LineFeed no final do dado escrito.

A procedure Read e ReadLn

Para ler dados de um arquivo/dispositivo temos os comandos Read e ReadLn, que utilizaremos bastante durante todo o tempo em que estivermos programando em Pascal e seus filhotes, como o Delphi. Segue abaixo a sintaxe do comando Read:

Read( Var1, Var2, ..., VarN );

Onde Var1, Var2, … VarN, são as variáveis que receberão os dados de entrada do arquivo/dispositivo em que se está fazendo a operação de leitura, no caso de Read, o dispositivo padrão é o Kdb, ou teclado (Keyboard).

O modo mais comum é utilizar Read/ReadLn para transferir dados do teclado, para uma variável em memória, como podemos ver no exemplo a seguir.

Program TestRead;
Var
      strData : String[255];

Begin
  Write( 'Please type a word' );
  Read( strData );
End;

No exemplo acima o programa aguarda uma entrada de uma string através do teclado, até que o usuário digite a tecla enter para finalizar a entrada.

Da mesma forma como nos comandos de escrita Write/WriteLn, os comandos Read/ReadLn aceitam uma entrada de um arquivo/dispositivo diferente do padrão estabelecido para o comando, conforme pode ser visto na definição abaixo:

Read( VarFile, Var1, Var2, Var3, ...VarN );

Onde VarFile é a variável associada ao arquivo/dispositivo em que se deseja realizar a leitura. Um arquivo/dispositivo padrão que pode ser utilizado para captura de dados é a porta AUX:, geralmente conhecida COM1: nos PC’s. Entretanto esse comando só retornará uma informação com sucesso, caso exista algum dispositivo conectado à porta auxiliar (COM) do computador.

Abaixo segue um exemplo utilizando o arquivo/dispositivo padrão, Kbd, que é o teclado, utilizado por padrão em Read/ReadLn, caso não seja especificado o arquivo/dispositivo a utilizar.

Program TestReadKbd;
Var
      strData : String[255];

Begin
  Write( 'Please type a word' );
  Read( Kbd, strData );
End;

Arquivos

Já que estamos falando de E/S e dispositivos, não podemos deixar de falar sobre E/S em  disco utilizando arquivos e as operações em arquivos consistem em 2 formas básicas. A primeira são os arquivos tipados e a segunda os arquivos sem tipo definido.

Para trabalhar com arquivos em Turbo Pascal, basta seguir a sequencia abaixo:

  1. Criar uma variável do tipo arquivo (file handle);
  2. Associar um  nome de arquivo a uma variável do tipo arquivo;
  3. Abrir o arquivo para escrita, leitura ou escrita/leitura;
  4. Ler dados do arquivo ou escrever dados para o arquivo, na sequencia desejada;
  5. Fechar o arquivo após o uso;

Primeiro de tudo devemos conhecer um tipo de dados importante na manipulação e gerenciamento de arquivos em Pascal, é o tipo File. Nos casos de arquivos tipados, podemos também definir um arquivo juntamente com o tipo de dados a ser manipulado nesse arquivo, conforme descrito abaixo:

Var
       fpIntegerFile : File Of Integer;
       fpRealFile    : File Of Real;
       fpStringFile  : File Of String[100];
       fpFileRecord  : File Of Record 
                                 strAddress : String[50];
                                 nNumber    : Integer;
                                 fSalary    : Real;
                               End;

Ao definir uma variável do tipo File, você está criando um file handle que será utilizado em todas as operações envolvendo arquivos e a primeira dessas operações é associar esse file handle a um arquivo no filesystem do sistema operacional, para isso utilizamos o comando Assign, cujo formato está descrito logo abaixo:

Assign( FileVar : File, strFileName : String );

Onde FileVar é uma variável do tipo File, podendo ser de qualquer um dos tipos especificados, conforme o exemplo anterior. strFileName é uma variável, ou constante do tipo String, que contém o nome do arquivo a ser aberto/criado para leitura/escrita;

Após associar o nome do arquivo à variável do tipo File, essa variável estará pronta para toda e qualquer operação subsequente a ser realizada com arquivos.

Abrindo arquivo para E/S.

A primeira operação que devemos fazer após associar um file handle a um arquivo no filesystem é abrir o arquivo para que possamos realizar qualquer operação de E/S no mesmo. Para isso Turbo Pascal conta com duas procedures que podem ser utilizadas para abrir um arquivo, que serão descritas a seguir.

Reset

A procedure Reset é utilizada para abrir um arquivo existente no filesystem e sua sintaxe é descrita abaixo:

Reset( FileVar : File );

Onde FileVar é a variável do tipo File, associada anteriormente a um arquivo através da procedure Assign, descrita anteriormente.

Rewrite

A procedure Rewrite é utilizada para abrir um arquivo independente se o mesmo existe no filesystem ou não. Caso o arquivo não exista o mesmo será criado.

Rewrite( FileVar : File );

Onde FileVar é a variável do tipo File, associada anteriormente a um arquivo através da procedure Assign, descrita anteriormente.

 Fechando arquivos

Bom, após todo esse processo de abertura de arquivos existe um processo importantíssimo a ser realizado ao final do uso do mesmo, que é o fechamento do arquivo previamente aberto, uma vez que as funções de E/S de Turbo Pascal, assim como as funções de E/S de ANSI C, são bufferizadas, ou seja, elas utilizam um buffer interno para cada leitura/escrita feita em um dispositivo, visando assim otimizar as operações de E/S, principalmente em dispositivos lentos.

Apesar do buffer interno proporcionar um ganho de performance nas operações de E/S, existe um problema, pois como tanto as operações de leitura quanto as de escrita são feitas em memória antes do buffer ser completado e despejado no dispositivo de armazenamento, no caso o disco rígido ou até mesmo um disquete.

Caso o desenvolvedor não instrua o programa a despejar esse buffer no disco, o Turbo Pascal irá fazê-lo “automagicamente“, quando o buffer for completado ou quando o arquivo for fechado.

Close

Fecha um arquivo especificado, despejando o buffer de E/S no dispositivo anteriormente aberto e invalidando o file handle para uso. Sua sintaxe é descrita abaixo:

Close( FileVar : File );

Onde FileVar é a variável do tipo File, associada anteriormente a um arquivo através da procedure Assign, descrita anteriormente.

Despejando o buffer de E/S.

Conforme descrito na procedure Close, as operações de E/S em arquivos de Turbo Pascal são bufferizadas e esse buffer é gerenciado “automagicamente” por Turbo Pascal.

Entretanto podemos forçar que o mesmo seja liberado no disco a qualquer momento e podemos fazer isso através da procedure Flush, cuja sintaxe está descrita abaixo:

Flush( FileVar : File );

Onde FileVar é a variável do tipo File, associada anteriormente a um arquivo através da procedure Assign, descrita anteriormente.

Abaixo um exemplo que demonstra o uso de todas as funções descritas acima:

(** Sample program to show the power 
  * of Turbo Pascal I/O procedures 
  * by PopolonY2k 2012.
  *)
Program Test_IO;
Var
       fpFile : File Of String[100];

Begin
  Assign( fpFile, 'temp.txt' ); { Assign  the file name to file variable }
  Rewrite( fpFile ); { Create a new file }
  WriteLn( fpFile, 'This is a Test' ); { Write text to file }
  Flush( fpFile ); { Force the buffer dump to disk }
  WriteLn( fpFile, 'Another text to file' );
  Close( fpFile ); { Close the file }
End.

Acesso aleatório ao arquivo.

Devemos conhecer que existe um ponteiro lógico que indica a posição de onde a próxima operação de leitura ou gravação será realizada, sendo que em cada operação de E/S realizada no arquivo, esse ponteiro lógico é movido ‘n’ bytes para frente, principalmente nos exemplos descritos até aqui, onde trabalhamos com arquivos sequenciais.

Sabendo disso, devemos conhecer também que existe uma forma, em Turbo Pascal, para manipulação desse ponteiro lógico, o que nos permite navegar pelo arquivo de maneira aleatória, não necessitando assim varrer um arquivo inteiro em busca de uma informação, e assim podemos trabalhar de maneira mais eficiente e veloz para os casos onde sabemos a posição do registro a ser lido ou escrito.

Para manipular a posição do ponteiro do arquivo, Turbo Pascal possui a procedure descrita abaixo.

Procedure Seek( fpFile : File; nRec : Integer );

A procedure Seek recebe um parâmetro do tipo File, que contém o file handle para o arquivo aberto através de Reset ou Rewrite e também aceita um valor inteiro que descreve  a nova posição do ponteiro no arquivo. Lembrando que a posição inicial do ponteiro no arquivo é sempre zero.

Essa posição não é o número de bytes a mover no arquivo e sim o número de registros do arquivo e sua posição em bytes varia de acordo com o tipo do arquivo. Abaixo um exemplo do uso de Seek.

(** This is a sample to show how to make a random access
  * to file, using Seek.
  * by PopolonY2k 2012.
  *)
Program Test_Seek;
Type TMyData = Record
  strName : String[50];
  nAge    : Integer;
End;

Var
       fpFile : File Of TMyData;
       rec    : TMyData;

Begin
  { The first step is write some data to file }
  Assign( fpFile, 'Data.dat' );  { Create the data file }
  Rewrite( fpFile );

  rec.strName := 'PopolonY2k';
  rec.nAge    := 60;
  Write( fpFile, rec );          { Write the first record }
  rec.strName := 'AphroditeY2k';
  rec.nAge    := 40;
  Write( fpFile, rec );          { Write the second record }
  rec.strName := 'HudnosY2k';
  rec.nAge    := 1000;
  Write( fpFile, rec );          { Write the third record }
  Close( fpFile );               { Close the file }

  { Now we start to read the file written previously }

  Assign( fpFile, 'Data.dat' );  { Open the data file }
  Reset( fpFile );

  { Our file has 3 record and the first record is in position 0 zero }
  { Then we start to read the mid record }
  Seek( fpFile, 1 );
  Read( fpFile, rec );
  WriteLn( 'Name -> ', rec.strName );
  WriteLn( 'Age  -> ', rec.nAge );

  { Now reading the last record }
  Seek( fpFile, 2 );
  Read( fpFile, rec );
  WriteLn( 'Name -> ', rec.strName );
  WriteLn( 'Age  -> ', rec.nAge );

  { Now reading the first record }
  Seek( fpFile, 0 );
  Read( fpFile, rec );
  WriteLn( 'Name -> ', rec.strName );
  WriteLn( 'Age  -> ', rec.nAge );
  Close( fpFile );
End.

Aquivos Texto.

Arquivos texto são arquivos cujo conteúdo é formado basicamente por caracteres imprimíveis, sendo compostos por diversas linhas de caracteres imprimíveis separados por um terminador de linha, que no caso do MSX-DOS é o caractere de controle carriage-return seguido de line-feed, sendo o final de arquivo delimitado por um caractere especial representado pelo código ASCII de número 26 (1A em hexadecimal), também representado pela combinação de teclas CTRL+Z.

Abaixo segue a descrição de alguns comandos importantes para a manipulação de arquivos em modo texto:

Function EOF( fpFile : File) : Boolean.

Essa função é especifica para o uso em arquivos no modo texto e sua finalidade é reconhecer se o final de arquivo foi alcançado ou não. O nome dessa função vem da abreviação de End Of File.

Conforme pode ser visto no protótipo da função acima, EOF recebe um parâmetro que é um file handle para um arquivo texto anteriormente aberto por Reset ou Rewrite. A função testa se arquivo chegou ao seu fim e retorna True caso o teste seja satisfeito e Falso caso contrário.

Function EOLn( fpFile : File ) : Boolean;

Assim como EOF, EOLn aceita um file handle, previamente aberto através de Reset ou Rewrite. A função testa se a ultima operação de leitura feita através da procedure Read, atingiu o final da linha delimitado por Carriage-return/Line-feed. O nome da função vem da abreviação de End Of Line.

Abaixo temos um exemplo demonstrando o uso das funções de manipulação de arquivo texto.

(** Sample code to show the Turbo Pascal text files related
  * procedures and functions;
  * This sample copy a complete input file to an output file specified
  * by the user;
  * by PopolonY2k 2012;
  *) 
Program Test_TextFiles;

Type TString = String[255];

Var
      fpFileIn,
      fpFileOut  : File Of TString;
      strLine,
      strFileIn,
      strFileOut : TString;

Begin
  WriteLn( 'Please type the input file name' );
  ReadLn( strFileIn );
  WriteLn( 'Please type the output file name' );
  ReadLn( strFileOut );

  Assign( fpFileIn, strFileIn );   { Assign the input and output   }
  Assign( fpFileOut, strFileOut ); { file names to the File handle }
  Reset( fpFileIn );               { variables                     }
  Rewrite( fpFileOut );

  {
    Now the we enter in loop reading the input file until reach the
    end of input file and writing the output to the output file
    previously opened for writing.
  }
  While( Not EOF( fpFileIn ) )  Do  { EOF function sample }
  Begin
    { 
      Here another loop to read the content for each line of input
      file, until the end of line.
    }
    While( Not EOLn( fpFileIn ) ) Do
    Begin
      Read( fpFileIn, strLine );    { Read content from input file }
      Write( fpFileOut, strLine );  { Write content to output file }
    End;
    WriteLn( fpFileOut ); { After read a complete line. We must to }
                          { add a CR/LF at the end of line for the }
                          { output file                            }
  End;
  Close( fpFileIn );      { Close the input and }
  Close( fpFileOut );     { the output files    }
End.

Arquivos sem tipo definido.

Já passeamos pelas funções de E/S de Turbo Pascal, para manipulação de arquivos tipados incluindo arquivos texto, entretanto não podemos deixar de citar que Turbo Pascal é uma linguagem poderosa no nível de que você tenha pleno controle da máquinam o que possibilita a criação de programas e bibliotecas de uso geral, incluindo a capacidadede manipulação de arquivos independente do tipo, ou seja, podemos manipular e gerenciar arquivos binários diversos com extrema facilidade devido a esse conjunto de funções  que veremos à seguir.

As procedures padrão para manipulação de arquivos não tipados são canais de E/S de baixo nível que usam acesso direto a arquivos no disco. Esse acesso é feito através de um registro, ou buffer, que tem um tamanho lógico de 128 bytes.

Esse tamanho virtual pode ser comparado aos setores de um arquivo de um sistema operacional, como no MSX-DOS que é de 512 bytes.

Vamos ao que interessa que são as rotinas de baixo nível para acesso direto a arquivos do Turbo Pascal, que descrevo abaixo.

Procedure BlockRead( fpFile : File; 
                     Var Buffer; 
                     nBytes : Integer );
{ Variation of the same procedure }
Procedure BlockRead( fpFile : File; 
                     Var Buffer; 
                     nBytes : Integer; 
                     Var nResult : Integer );

A procedure BlockRead é uma poderosa rotina para leitura de grandes blocos de dados de qualquer arquivo em qualquer formato. Como podemos ver, a rotina aceita um file handle que especifica o arquivo aberto anteriormente através de Reset ou Rewrite, uma variável buffer que irá receber o conteúdo da leitura feita por BlockRead, uma variável que especifica o número de bytes a ler do arquivo e também uma variável referência (vide passagem por referência no segundo post), que irá receber o número de registros lidos na ultima operação.

O buffer pode ter qualquer tamanho e apontar para qualquer área de memória que esteja disponível no computador. O parâmetro inteiro representado por nBytes, deve especificar o tamanho, em bytes, do buffer a ser carregado na operação de leitura e nResult irá retornar o número de registros de 128 bytes realmente lidos, ou seja se o seu buffer for de 256 bytes, nResult terá o valor 2 após a leitura de BlockRead.

Após a ultima operação de BlockRead, a posição do ponteiro do arquivo será  movida nBytes para frente no arquivo, ou até a posição final do arquivo, caso a mesma seja atingida.

Procedure BlockWrite( fpFile : File;
                      Var Buffer; 
                      nRecs : Integer );
{ Variation of the same procedure }
Procedure BlockWrite( fpFile : File;
                      Var Buffer; 
                      nRecs : Integer; 
                      Var nResult : Integer );

A procedure BlockWrite tem basicamente a mesma estrutura de BlockRead porém com função inversa, ou seja, ao invés de ler, a mesma grava dados de um buffer em memória para o disco. Da mesma forma que BlockRead, a rotina aceita um file handle que especifica o arquivo aberto anteriormente através de Reset ou Rewrite, uma variável buffer que contém a informação a ser escrita por BlockWrite, uma variável que especifica o número de registros a ser escrito e também uma variável referência, que irá receber o número de registros gravados na ultima operação de escrita.

buffer pode ter qualquer tamanho, ou apontar para qualquer área de memória, desde que estejam disponíveis para o computador. O parâmetro inteiro representado por nRecs, deve especificar o tamanho, em registros de 128 bytes, do buffer a ser gravado na operação de escrita e nResult irá retornar o número de registros de 128 bytes realmente escritos, ou seja se o seu buffer for de 256 bytesnResult terá o valor 2 após a operação de escrita feita por BlockWrite.

Após a ultima operação de BlockWrite, a posição do ponteiro do arquivo será  movida nBytes para frente no arquivo.

Abaixo segue um exemplo que demonstra o uso das rotinas de baixo nível, não tipadas, de Turbo Pascal.

(** Sample code to show the Turbo Pascal direct access low level
  * related procedures and functions;
  * This sample copy a complete input file to an output file specified
  * by the user;
  * by PopolonY2k 2012;
  *) 
Program Test_DirectIO;

Type TString = String[255];
Const ctMaxBuffer : Integer = 1024; { Uses 1024 bytes copy buffer }

Var
      fpFileIn,
      fpFileOut  : File;
      strLine,
      strFileIn,
      strFileOut : TString;
      nRecs     : Integer;
      aBuffer   : Array[0..ctMaxBuffer] Of Byte;

Begin
  WriteLn( 'Please type the input file name' );
  ReadLn( strFileIn );
  WriteLn( 'Please type the output file name' );
  ReadLn( strFileOut );

  Assign( fpFileIn, strFileIn );   { Assign the input and output   }
  Assign( fpFileOut, strFileOut ); { file names to the File handle }
  Reset( fpFileIn );               { variables                     }
  Rewrite( fpFileOut );

  { 
    Start reading the input file until reach the end of file, when
    the BlockRead return zero for the number of records loaded.
  }
  Repeat
    BlockRead( fpFileIn, aBuffer, SizeOf( aBuffer ), nRecs );
    BlockWrite( fpFileOut, aBuffer, nRecs );
  Until( nRecs = 0 );

  Close( fpFileIn );
  Close( fpFileOut );
End.

Error handling

Bom, até agora fizemos o caminho perfeito onde problemas não acontecem, discos não tem sua capacidade máxima atingida, erros de escrita e leitura não existem, proteção contra gravação é lenda, enfim, tudo é maravilhoso no mundo de Pokémon.

Porém no mundo real, todos os problemas acima e muitos outros mais, podem acontecer e irão acontecer pois Murphy nos acompanha a cada segundo e fará com que cada um dos problemas possíveis aconteça. Por isso devemos trabalhar nossos softwares buscando prever a maioria dos problemas ou pelo menos tratar a maioria das excessões possíveis de acontecer durante o ciclo de execução do software.

O Turbo Pascal possui um mecanismo de controle do comportamento de run-time conhecida como Diretivas de Compilação, que falaremos em outra oportunidade dedicada exclusivamente a Diretivas de Compilação. Entre os exemplos de diretivas de compilação está a que controla a checagem de acesso indevido a um índice de um array, outra que controla a checagem de compatibilidade entre parâmetros String, e entre as diversas diretivas existentes temos uma em especial para controle da checagem de erro de E/S que possibilita ao programador desabilitar a checagem automática de erros de E/S pelo Turbo Pascal, transferindo assim a responsabilidade para o programador sobre o tratamento de erros de E/S.

As diretivas de compilação são ativadas através de comentários (*$ *) ou {$ } sendo que a diretiva inicia com o caractere $ seguida pela representação da diretiva desejada. Algumas diretivas podem ser ativadas ou desativadas através do + (ativa) ou – (desativa) e tem escopo local ou global.

No caso da diretiva de checagem e erro de E/S, a diretiva é a {$I} (I/O checking) que tem seu estado default ativado {$I+}, ou seja, qualquer erro de E/S que aconteça em seu programa, o Turbo Pascal automagicamente irá adicionar um código de checagem de erro para cada operação de E/S e caso ocorra um erro de E/S, o sistema operacional será acionado informando o erro ocorrido, o que muitas vezes chega a ser um inconveniente pois as mensagens apresentadas pelo sistema operacional nem sempre são “esteticamente” conveninentes ao usuário final. Quem não se lembra das mensagens do MSDOS/MSX-DOS, (A)bort, (R)etry, (I)gnore ????

Pois bem, para contornar inconvenientes como esse e dar maior poder ao desenvolvedor para controlar as excessões de E/S, utilizamos a diretiva {$I}, que é uma diretiva local, ou seja, podemos ativar e desativá-la a qualquer momento durante o fluxo de execução do software, conforme pode ser visto no programa abaixo.

(** Sample code to show the Turbo Pascal directive for I/O control
  * by the programmer;
  * This sample copy a complete input file to an output file specified
  * by the user;
  * by PopolonY2k 2012;
  *) 
Program Test_IO_Compiler_Directive;

Type TString = String[255];
Const ctMaxBuffer : Integer = 1024; { Uses 1024 bytes copy buffer }

Var
      fpFileIn,
      fpFileOut  : File;
      strLine,
      strFileIn,
      strFileOut : TString;
      nRecs     : Integer;
      aBuffer   : Array[0..ctMaxBuffer] Of Byte;

Begin
  WriteLn( 'Please type the input file name' );
  ReadLn( strFileIn );
  WriteLn( 'Please type the output file name' );
  ReadLn( strFileOut );

  Assign( fpFileIn, strFileIn );   { Assign the input and output   }
  Assign( fpFileOut, strFileOut ); { file names to the File handle }
  {$I-}
  Reset( fpFileIn );               { variables                     }
  {$I+}

  If( IOResult <> 0 )  Then
  Begin
    WriteLn( 'Error to open the input file' );
    Halt;
  End
  Else
  Begin
    {$I-}
    Rewrite( fpFileOut );
    {$I+}
    If( IOResult <> 0 )  Then
    Begin
      WriteLn( 'Error to open the output file' );
      Halt;
    End;
  End;

  { 
    Start reading the input file until reach the end of file, when
    the BlockRead return zero for the number of records loaded.
  }
  Repeat
    BlockRead( fpFileIn, aBuffer, SizeOf( aBuffer ), nRecs );
    BlockWrite( fpFileOut, aBuffer, nRecs );
  Until( nRecs = 0 );

  Close( fpFileIn );
  Close( fpFileOut );
End.

No código acima podemos ver que {$I-} e {$I+} delimitam o escopo de onde inicia e onde termina a checagem de erro de E/S, sendo que após isso chamamos a função IOResult que nos retorna o ultimo erro ocorrido na ultima operação de E/S, sendo que qualquer valor diferente de zero sinaliza o código do ultimo erro ocorrido na ultima operação de E/S.

Dessa forma podemos fazer um controle minucioso sobre cada operação de E/S, retornando erros mais detalhados ou até permitindo que o software tome decisões sobre a melhor ação de contorno a ser executada para se resolver a excessão.

Finalizando o esse post percebemos que Turbo Pascal possui um rico conjunto de rotinas para manipulação de E/S, permitindo assim que softwares modernos e com alto grau de controle sejam construídos para plataformas que suportam esse poderoso compilador, o que é verdadeiro para a plataformas baseadas em CP/M80, como o caso do MSX.

Agradecimento

Aproveito para deixar aqui meu agradecimento a Nirvardo Cavalcante, membro atuante na comunidade de MSX nacional, pelo farto material sobre Turbo Pascal enviado a mim, incluindo código fonte.

Obrigado e, sim, em breve faremos aquele universal music player em Pascal. 🙂

[]’s
PopolonY2k

Referência na internet

Wireless network (Wikipedia)
http://en.wikipedia.org/wiki/Wireless_network

Ethernet (Wikipedia)
http://en.wikipedia.org/wiki/Ethernet

MSX-DOS (Wikipedia)
http://en.wikipedia.org/wiki/MSX-DOS

RS-232 (Wikipedia)
http://en.wikipedia.org/wiki/Rs232

Device Driver (Wikipedia)
http://en.wikipedia.org/wiki/Device_driver

Torre de Babel (Wikipedia)
http://pt.wikipedia.org/wiki/Torre_de_Babel

Caracteres imprimíveis (Wikipedia)
http://pt.wikipedia.org/wiki/ASCII#Caracteres_imprim.C3.ADveis

Caracteres não imprimíveis, ou caracteres de controle (Wikipedia)
http://pt.wikipedia.org/wiki/ASCII#Caracteres_n.C3.A3o_imprim.C3.ADveis

ANSI C (Wikipedia)
http://en.wikipedia.org/wiki/ANSI_C

Protótipo de função (Wikipedia)
http://pt.wikipedia.org/wiki/Prot%C3%B3tipo_de_fun%C3%A7%C3%A3o

Turbo Pascal Forever – Begin (PopolonY2k Rulezz)
http://www.popolony2k.com.br/?p=1694

Pokemón ( Wikipedia)
http://pt.wikipedia.org/wiki/Pok%C3%A9mon

Lei de Murphy (Wikipedia)
http://pt.wikipedia.org/wiki/Lei_de_Murphy

Turbo Pascal compiler directives (David I Embarcadero Blog)
http://blogs.embarcadero.com/davidi/2008/11/16/39118/