Kodowanie Huffmana + Cpp/Qt + plik wynikowy

0

Witam,
mam za zadanie zaimplementować algorytm **dynamicznego **(adaptacyjny) kodowania Huffmana oraz utworzyć plik wynikowy zawierający zakodowany tekst.

Działanie algorytmu zostało przedstawione tutaj : http://www.cs.sfu.ca/CourseCentral/365/li/squeeze/AdaptiveHuff.html (do uruchomienia aplet javy).

Jak można zaobserwować w aplecie dla:

input: alaola
output: a 0l 1 00o 01 0

defacto wynik wydaje mi się powinien być bez spacji więc mamy: a0l100o010, gdzie o ile dobrze rozumiem zapisując to do pliku binarnego (?) literę 'a' zapiszemy jako kod ASCII, natomiast '1' czy '0' symbolizuje tutaj jeden bit, tylko jak go zapisać? (bo zapisując go do pliku binarnego będzie zajmował więcej niż jeden bit i w sumie kompresja nie zajdzie ? --> bo zapiszemy to także na 8-bitach ?)

Podsumowując: jak przedstawiony wyżej problem zapisać do pliku wykorzystując c++/Qt i uzyskując plik o skompresowanej wielkości, tak aby potem można było poddać go dekompresji..? (bo prawodpodobnie czegoś nie wiem/ nie widzę..)

0

Sorry za podbicie, ale nikt nie wypowie się w kwestii zapisu pojedynczych bitów do pliku ? :-|

Osobiście na razie rozwiązałem to tak, że zapisuje ciąg tekstu ASCII do postaci Hex i tak mam:

 
tekst skompresowany:               a0l100o010
zapisuje w pliku binarnym jako:    61 30 6c 31 30 30 6f 30 31 30  //przedzieliłem (teraz ręcznie) poszczególne znaki spacją..

I teraz co ciekawe.. (albo i nie bo widać ze ciąg binarny jest dłuższy) mamy, że plik z znakami ASCI zajmuje 14 bajtów a z HEX 28 bajtów..
I gdzie ta kompresja? ;>

0
mikajlo napisał(a):

I teraz co ciekawe.. (albo i nie bo widać ze ciąg binarny jest dłuższy) mamy, że plik z znakami ASCI zajmuje 14 bajtów a z HEX 28 bajtów..
I gdzie ta kompresja? ;>

Zapis binarny ma pełne 256 znaków po 8 bitów, czyli pełne pokrycie: 28 = 256, i dlatego jest najkrótszy.

Hex ma tylko 16 znaków, i każdy nadal zajmuje bitów, i stąd 2 razy więcej potrzeba.

liczba 128
dziesiętnie: 3 bajty
dwójkowo 10000000, 8 bajtów
hex: 80 - dwa znaki
no a binarnie to przecież 1 bajt, bo to jakiś jeden znak o kodzie 128.

0

Twój output to kolejno:
'a' - jeden bajt dosłowny,
0 - jeden bit (0 oznacza, że dodajemy nowy element do słownika i następne 8 bitów reprezentuje kolejny bajt, gdyby była to 1 że wyciągamy ze słownika litrę a)
'l' - jeden bajt oznaczjący nową literę
1 - jeden bit - tu w słowniku mamy 3 elementy, jeśli pierwszy bitem jest 1 to oznacza to litrę a (ten przypadek), jeśli byłby to bit 0 to następny bit określałby czy jest to litera l czy nowy element słownika
00 - dwa bity - słownik bez zmian, pierwszy bit to zero, drugi też zero, czyli dodajemy nową literę do słownika
'o' - jeden bajt z nową literą, od teraz w słowniku mam teraz 3 znaki i oznaczanie dodawania nowych znaków (przebudowa drzewa)
01 - dwa bity, pierwszy bit oznacza, że nie jest to a, a drugi bit oznacza, że jest to l. gdyby miało by to być znowu o to byłby to ciąg bitów: 001
0 - jeden bit oznaczający a

w sumie: 3 bajty i 7 bitów, co mieści się w 4 bajtach

Cały problem w tym algorytmie to aktualizowanie drzewa kodowania w zależności od tego jak zmienia się częstość występowania liter, co powoduje zmianę sposobu kodowania znaków (i zmianę długości w bitach poszczególnych znaków).
Musisz napisać sobie klasę, która będzie przesuwać bity i łączyć wcześniej zapisane bity z nowymi dodawanymi.
Coś w tym stylu:

class MyOutputBitStream {
    QIODevice *dev;
    unsigned int bitOffset, data;

public:
    MyOutputBitStream(QIODevice *dev) : dev(dev), bitOffset(0), data(0) {}

    bool writeBits(unsigned char x, int numberOfBits) {
        Q_ASSERT(numberOfBits<=8 && numberOfBits>0);
        bool result = true;
        x &= (1<<numberOfBits)-1; // clear additional bits
        data |= ((unsigned int)x)<<bitOffset; // merge with previous data
        bitOffset += numberOfBits;
        if (bitOffset>=8) {
             char toWrite = data&&0xff; // endians
             result = 1==dev->write(&toWrite, 1);
             data >>=8;
             bitOffset-=8;
        }
        return result;
    }

    int flush() { // returns number of bits flushed or -1 on error
         int result = -1;
         if (bitOffset) {
              char toWrite = data&0xff; // endians
              if (1==dev->write(&toWrite, 1)) {
                  result = bitOffset;
                  bitOffset = 0;
                  data = 0;
              }
         }
        return result;
    }
};

1 użytkowników online, w tym zalogowanych: 0, gości: 1