Stefano Guerrini
A.A. 2000/01
Dall'analisi delle soluzioi inviate. Uno dei punti che sembrano meno chiari nella gestione dell'input del progetto è l'uso delle funzioni getch e ungetch.
Come già discusso a lezione e nel testo della prima parte del progetto, lo scopo principale di getch e ungetch è permettere una certa reversibilità nell'acquisizione dell'input. In certi casi, l'analizzatore lessicale si trova nella situazione di poter decidere se un token è terminato solo dopo aver letto il primo carattere del successivo token. La coppia di funzioni getch/ungetch assicura una bufferizzazione dell'input ad (almeno) un carattere e permette di reinserire in testa allo stream di input il carattere erroneamente letto. Di conseguenza, usando in modo appropriato getch e ungetch si può assicurare che il primo carattere disponibile in testa allo stream di input o nel buffer sia sempre quello iniziale del token che si deve acquisire.
Va però detto che l'uso della bufferizzazione dell'input può causare problemi se utilizato in modo improprio o in modo non pulito. In particolare, è molto facile scrivere programmi complicati o errati se si combinano letture dirette dall'input mediante read o readln e letture bufferizzate mediante la getch. A tale scopo è consigliabile che, se si usa la getch, tutte le letture dell'input avvengano per mezzo di tale funzione; in pratica, che il programma non contenga altre read/readln oltre a quelle necessarie per implementare la getch. Analogamente, è anche consigliabile programmare la getch in modo che la gestione del fine-file o del fine-linea avvengano attraverso il carattere letto dalla getch.
Vediamo ora in dettaglio una possibile soluzione poer l'implementazione di getch/ungetch che permetta di gestire anche fine-linea e fine-file.
Per prima cosa, come già detto a lezione, usiamo una variabile globale per il buffer attraverso cui avviene la lettura. Le corrispondenti definizioni saranno:
type tipo_buffer = record empty: boolean; val : char end; var buf: tipo_buffer;L'idea per la gestione del fine-file e del fine-linea è:
const NULL = chr(0); CR = chr(13);
Quindi vediamo le definizioni delle funzioni. La ungetch è molto semplice e non richiede particolari commenti. Si osservi solo che non viene effettuato alcun controllo sul contenuto del buffer. In pratica, se si fa l'ungetch di un carattere quando il buffer è pieno, il contenuto del buffer al momento dell'ungetch è perso.
procedure ungetch(ch : char); begin buf.val := ch; buf.empty := false; end;
Passiamo ora a vedere la getch, supponendo che la lettura dell'input avvenga dal file assegnato alla variabile fl di tipo TEXT.
function getch: char; var ch : char; { var temporanea per la lettura del carattere } begin if (buf.empty) then begin if (eof(fl)) then ch := NULL { segnala il fine-file } else if (eoln(fl)) then begin readln(fl); { avanza alla riga successiva } ch := CR { segnala il fine-linea } end else begin read(fl, ch); { legge il successivo carattere nel file } end end else begin { il buffer non e' vuoto } buf.empty := true; { svuota il buffer } ch := buf.val { legge il contenuto del buffer } end; getch := ch end;
Si noti in particolare il caso in cui ci si trova a fine-linea: il
comando readln(fl)
assicura il posizionamento all'inizio della
riga successiva. Metodi che si basano sulla conoscenza della
particolare maniera in cui il fine-linea è rapresentato nel file di
testo non sono da considerare corretti, dato che fanno dipendere il
comportamento del programma dal sistema su cui lo si compila e
utilizza. Ad esempio, in DOS il fine-linea è rappresentato da una
coppia di caratteri (carriage-return seguito da line-feed), mentre in
altri sistemi la rappresentazione del fine-linea è un solo carattere
(nel MAC solo line-feed, in UNIX solo carriage-return). La tecnica
presentata nell'esempio funziona correttamente indipendentemente dalla
raprresentazione del fine-linea e quindi indipendentemente dal sistema
su cui si compila ed esegue il programma.
Riportiamo qui di seguito il codice di un semplice programma che usa le funzioni getch e ungetch utilizzando il valore di ritorno della getch per gestire fine-linea e fine-file.
Per rendere il programma più generale, si è assunto che il file da cui leggere l'input e il buffer da utilizzare siano dei parametri delle funzioni getch e ungetch. In questo modo è possibile pensare ad una generalizzazione delle funzioni al caso in cui l'input è preso simultaneamente da più file.
L'esempio mostra anche come mantenere l'informazione relativa alla posizione corrente del carattere (riga e colonna).
program prova_getch(input, output); { Prova la gestione di un input bufferizzato ad un carattere mediante la } { coppia di funzioni getch/ungetch. } { Fa vedere come gestire eof e eoln direttamente attraverso la getch. } { Mostra come realizzare le funzioni in modo che possano essere utilizzate } { su pi\`u file e con buffer diversi. } { Mostra anche come contare il numero di riga e di colonna dell'input. } { Input: legge mediante getch una sequenza di righe di testo fino } { alla fine del file. } { Output: stampa i caratteri letti, rimpiazzandone alcuni con degli spazi } { bianchi; il rimpiazzamenteo viene fatto usando la ungetch. Alla fine } { della riga viene stampato il numero di riga corrente e la sua lunghezza. } const NULL = chr(0); { carattere usato per il fine file } CR = chr(13); { carriage return o ritorno carrello } type tipo_buffer = record empty : boolean; val : char end; var buf : tipo_buffer; ch : char; col, rig : integer; { Le funzioni getch e ungetch prendono come parametro il buffer } { da utilizzare. } { La getch prende come parametro anche il file da cui leggere. } { Notare che tali parametri devono essere passati per variabile. } procedure ungetch(var buf : tipo_buffer; ch : char); begin buf.val := ch; buf.empty := false; end; function getch(var buf : tipo_buffer; var fl: TEXT): char; var ch : char; { var temporanea per la lettura del carattere } begin if (buf.empty) then begin if (eof(fl)) then ch := NULL { segnala il fine-file } else if (eoln(fl)) then begin readln(fl); { avanza alla riga successiva } ch := CR { segnala il fine-linea } end else begin read(fl, ch); { legge il successivo carattere nel file } end end else begin { il buffer non e' vuoto } buf.empty := true; { svuota il buffer } ch := buf.val { legge il contenuto del buffer } end; getch := ch end; begin buf.empty := true; { inizializza il buffer a vuoto } ch := getch(buf, input); { legge il primo carattere } col := 1; rig := 1; { posizione iniziale col=rig=1 } while (ch <> NULL) do begin { il file non e' finito } while ((ch <> CR) and (ch <> NULL)) do { NB Attenzione al controllo su NULL. } { Serve a gestire un file che finisce senza un fine riga } begin { si trova all'interno della riga } if (ch in ['.',',',';',':']) then begin { prova a fare una ungecth } ungetch(buf, ' '); col := col-1; { ritorna indietro di una colonna } end else write(ch); { stampa il carattere letto } ch := getch(buf, input); { legge il carattere successivo } col := col+1; { avanza di una colonna } end; { si trova a fine riga } rig := rig+1; { avanzo il numero di riga } writeln('<', rig-1, ',', col-1, '>'); { stampo riga e num col totali } ch := getch(buf, input); { legge il primo carattere della nuova riga } col := 1; { si riporta alla prima colonna } end end.
This document was generated using the LaTeX2HTML translator Version 99.2beta8 (1.42)
Copyright © 1993, 1994, 1995, 1996,
Nikos Drakos,
Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999,
Ross Moore,
Mathematics Department, Macquarie University, Sydney.
The command line arguments were:
latex2html -split 0 -nonavigation getch.tex
The translation was initiated by Stefano Guerrini on 2001-06-20