Newsgroup: it.comp.lang.c++
Titolo: leggere sequenze di n bit da file

Sto realizzando un decodificatore LZW e mi farebbe comodo poter
leggere sequnze di bit  (3,4,5,6,7,8,9,10,11,12) da un file binario in
modo sequenziale. Esiste un sistema per implementare in modo semplice
tale lettura ?
Grazie Guido.

Per quanto ne sappia io, un file si legge per quanti corrispondenti a
byte. Una volta letto un byte (interamente), estrai i bit che ti servono
con una operazione di mascheratura. Quando vedi che il byte attuale č
finito, ne prendi un altro dal file.
Saluti,
            Flavio
--
Ing. Flavio Poletti
STS s.r.l.
Via Odoardo Beccari, 13
00154 Roma, Italia
Tel. +39 6 5755096
FAX  +39 6 57287214
company e-mail: 
sts...@tin.it
private e-mail: 
pol...@writeme.com

di...@tin.it
 (Guido) writes:

La soluzione O-O canonica per qualsiasi problema e` incapsulare
la funzionalita` richiesta in una classe apposita:-).  Vediamo
una trattazione un poco didattica, che puo` interessare piu` in
generale.
Per "disaccoppiare" l'uso della classe 'readbits' dalla sua
implementazione scegliamo di usare ad esempio un idioma handle-body.
Quindi, in "readbits.hpp", scriviamo soltanto:
class readbits_impl;	// forward/opaque
class readbits {
private:
    readbits_impl* p;	// la classe "body" ("misteriosa")
public:
    readbits(const char* filename);
    ~readbits();
    int nextbits(int nBits);	// legge i prossimi nBits bits, 3..12
    // e li torna nei bit meno significativi del risultato; torna -1
    // in caso di errore, fine file, file non trovato, ecc ecc.
    bool isOK();		// falso per eof, errore, file non trov.
private: // non implementate -- vietiamo assegnazione e copia
    readbits(const readbits&);
    void operator=(const readbits&);
};
e tutti i clienti si limitano ad includere "readbits.hpp" e usarlo.
In readbits.cpp, scriviamo l'implementazione.  Naturalmente,
per trasformare uno stream di byte in uno di bit, bisogna decidere
se operare in modo "little-endian" (presentando i bit a partire
dal meno significativo di ogni byte) o "big-endian" (viceversa).
Qui, ad esempio, adottiamo la convenzione little-endian, lasciando
la conversione a big-endian come esercizio per il lettore.
#include <fstream>
#include "readbits.h"
#ifdef WIN32
    // per qualsiasi compilatore a Standard C++:
    #include <limits>
    #define BB std::numeric_limits<unsigned char>::digits
    #define MO std::ios_base::in|std::ios_base::binary
#else
    // per egcs 1.02, non ancora molto standard:
    #define BB 8
    #define MO ios::in|ios::binary
#endif
// classe d'implementazione: incapsula lo stream, e un buffer
// di 1 carattere con l'indicazione di quanti suoi bit siano validi
class readbits_impl {
// tutto privato, tanto va acceduto solo da readbits (la classe handle).
// handle e body sono due classi che insieme implementano una "vera"
// classe, ed e` normale fare il body tutto privato e friend dell'handle
    friend class readbits;
    union {
	unsigned char buf;
	char cbuf;
    };
    int valid_bits;
    // buf conserva i prossimi valid_bits gia` letti dallo stream
    // ma non ancora usati.
    std::ifstream fin;
    readbits_impl(const char *filename): valid_bits(0),
	// fin(filename, std::ios_base::in|std::ios_base::binary)
	fin(filename, MO)
    { }
    int nextbits(int nBits);
};
int
readbits_impl::nextbits(int nBits)
{
    // assert(nBits>=3 && nBits<=12) -- precondizione, da specifiche
    if(!fin) return -1;
    int result = 0;
    int bits_done = 0;
    // avremo da 0 a 2 esecuzioni del ciclo qui sotto:
    while((nBits-bits_done) > valid_bits) {
	if(valid_bits) {
	    result |= buf<<bits_done;
	    bits_done += valid_bits;
	}
	if(!fin.get(cbuf)) return -1;
	// valid_bits = std::numeric_limits<unsigned char>::digits;
	valid_bits = BB;
    }
    // assert(valid_bits <= nBits-bits_done);
    // poi 0 o 1 casi di questo:
    if(nBits > bits_done) {
	result |= (buf & ~(0xFFFF << (nBits-bits_done))) << bits_done;
	valid_bits -= (nBits-bits_done);
	if(valid_bits)
	    buf >>= (nBits-bits_done);
    }
    return result;
}
readbits::readbits(const char *filename):
	p(new readbits_impl(filename))
{}
readbits::~readbits() { delete p; }
int readbits::nextbits(int nBits) { return p->nextbits(nBits); }
bool readbits::isOK() { return p->fin!=0; }
Ecco fatto!  Il bello, naturalmente, e` potere incapsulare
questa complessita` una volta per tutte e poi usarla in piena
liberta`, ad esempio con questo programmillo di prova:
#include <iostream>
#include "readbits.h"
int main()
{
    readbits po("po");
    for(int nb=3; nb<=12; ++nb) {
	int x = po.nextbits(nb);
	if(x<0) break;
	std::cout << x << " ";
    }
    std::cout << std::endl;
    return 0;
}
Per testare la correttezza dell'implementazione, puo` convenire
scrivere una "impalcatura" che stampi in termini binari (little
endian, per coerenza) tutti i bit letti dallo stream, e tutti
quelli ritornati come risposta.  Non e` difficile: aggiungiamo
a readbits.cpp una funzione:
void show(std::ostream&o, int v, int nbits, const char *title)
{
    o<<title<<" ("<<nbits<<" bit): ";
    for(int i=0; i<nbits; ++i) {
	o << (v&1);
	v >>= 1;
    }
    o<<std::endl;
}
e, in readbits_impl::nextbits(), le chiamate
 
	show(std::cout, buf, BB, "Letto");
dopo la fin.get(cbuf), e
	show(std::cout, result, nBits, "Risultato");
subito prima del return.
Questo permette agevole verifica che lo stream di bit letti (8 alla
volta) e quello di bit tornati (N alla volta) siano eguali.
Una volta convinti e soddisfatti di cio`, il prossimo passo
dovrebbe essere quello di pensare, in termini di *riusabilita`*:
come posso rendere disponibile quest'utile funzionalita` a
programmi scritti in futuro con altri compilatori e linguaggi?
Ad esempio, se mi servisse la funzionalita` "leggi i prossimi
N bit" da Visual Basic, o Javascript, o Perl, o Delphi...?
La soluzione a quest'ultimo enigma, nel mondo Win32, si chiama
COM, il Component Object Model.  Usando l'Active Template
Library (ATL) release 2.1 inclusa nel VC++ 5.0, si puo`
abbastanza agevolmente "incapsulare" la funzionalita`
realizzata come un componente riutilizzabile, rendendola
cosi` usabile da tutti i linguaggi suddetti e altri ancora.
Se interessa, posso mostrare come si faccia anche questo...
Alex
--
 ____    Alex Martelli, Bologna, Italia -- email: 
al...@magenta.com
 \SM/___                                 
http://magenta.com/~alex
  \/\bi/     You never know what is enough unless you know what
     \/          is more than enough.