Kodowanie Huffmana, problem z odczytem pliku

0

Na zajęcia mamy napisać kompresor/dekompresor plików tekstowych. Większość napisałem, jednak od dłuższego czas męczę się z odczytywaniem (a wydawało mi się, że to tylko prosta odwrotność...), kilka wersji już napisałem, jedna zapisywała śmieci (próbna była, nie miała prawa działać).

Jestem pewien, że samo kodowanie (tworzenie kodów) działa (program wypisuje mi kody znaków i są poprawne), zapis do pliku wydaje mi się też dobry (ale pewnie coś się znajdzie, bo błędy ew. można znaleźć jak po dekompresji będę wiedział, że jest źle ;] ).

Nie mogę sobie poradzić z odczytywaniem bit po bicie i porównywaniem z kodami. Oto kod (trochę długi, ale zamieszczam całość (prawie, bez pliku moimi klasami), żeby było widać, co jest czym.

typedef map<char, int> mapType;
typedef map<char, string> mapString;
typedef map<char, unsigned> mapBin;
typedef ListEl<char> listType;

bool compareList(listType* el1, listType* el2)                                  // funkcja porownywawcza dla listy
{
    if ((el1->getRoot()->getCount()) >= (el2->getRoot()->getCount()))
    {
        return false;
    }
    else return true;
}

void countSymbols(mapType & symbols, istream & file)                            // zlicza ilosc wystapien znaku w tekscie
{
    char c;
    while (file.good())
    {
        c = file.get();
        if (file.eof())
        {
            break;
        }
        symbols[c] += 1;
    }
}

void code(Node<char>* temp, string& charCode, mapString& cMap, Node<char>* root)// tworzy kody znakow na podst. drzewa huffmana
{
    Node<char>* backup = new Node<char>;  
    backup = temp;
    if (temp->getVisited() == false)
    {
        if ((temp->getLeft() == NULL) && (temp->getRight() == NULL))
        {
            char c;
            c = temp->getData();
            cMap[c] = charCode;
            charCode = "";
            temp->setVisited(true);
            code(root, charCode, cMap, root);
        } 
    }
    if ((temp->getLeft() != NULL) && (temp->getRight() != NULL))
    {
        if ((temp->getLeft()->getVisited() == true) && (temp->getRight()->getVisited() == true))
        {
            temp->setVisited(true);
        }
    } 
    else if ((temp->getLeft() != NULL) && (temp->getRight() == NULL))
    {
        if (temp->getLeft()->getVisited() == true)
        {
            temp->setVisited(true);
        } 
    }
    else if ((temp->getLeft() == NULL) && (temp->getRight() != NULL))
    {
        if (temp->getRight()->getVisited() == true)
        {
            temp->setVisited(true);
        }        
    }
    if (temp->getVisited() == false)
    {
        if (temp->getLeft() != NULL)
        {
            if (temp->getLeft()->getVisited() == false)   
            {
                temp = temp->getLeft();
                charCode += '1';
                code(temp, charCode, cMap, root); 
            }
        }   
        if (temp->getRight() != NULL)
        {
            if (temp->getRight()->getVisited() == false)   
            {
                temp = temp->getRight();
                charCode += '0';
                code(temp, charCode, cMap, root);
            }
        }
    }
}

Node<char>* binTree(mapType::iterator & iter, Node<char>* left, Node<char>* right, Node<char>* parent) // tworzy "poddrzewka" na potrzby huffmana
{
    Node<char>* element = new Node<char>;
    
    element->setData(iter->first);
    element->setCount(iter->second);
    element->setLeft(left);
    element->setRight(right);
    element->setParent(parent);
    return element;
}

void huffman(mapType & map, mapString & codeMap)                                // kodowanie huffmana
{
    mapType::iterator it;
    list<listType* > treeList;
    listType* treeNode = new listType;
    treeNode = NULL;
    
    for (it = map.begin(); it != map.end(); it++)
    {
        treeNode = new listType;
        treeNode->setRoot(binTree(it, NULL, NULL, NULL));
        treeList.push_back(treeNode);
    }
    
    treeList.sort(compareList);
    
    int tmpcount;
    
    
    while (treeList.size() > 1)
    { 
         
        Node<char>* newTree = new Node<char>;
        newTree->setLeft(treeList.front()->getRoot());
        tmpcount = treeList.front()->getRoot()->getCount();
        treeList.pop_front();
        newTree->setRight(treeList.front()->getRoot());
        tmpcount += treeList.front()->getRoot()->getCount();
        treeList.pop_front();
        newTree->setCount(tmpcount);
    
        treeNode = new listType;
        treeNode->setRoot(newTree);
        treeList.push_front(treeNode);
        treeList.sort(compareList);
         
    }
    
    
    cout << tmpcount << endl;
    cout << treeList.size() << endl;
    
    string codecomp = "";
    while (treeList.front()->getRoot()->getVisited() != true)
    {
        code(treeList.front()->getRoot(), codecomp, codeMap, treeList.front()->getRoot());
        codecomp = "";
    }

}	

void saveCodes(mapString codeMap, ofstream& file)                               // zapisuje kody znakow do plku wynikowego
{
    mapString:: iterator mapIt;
    for (mapIt = codeMap.begin(); mapIt != codeMap.end(); mapIt++)
    {
        file << mapIt->first << mapIt->second << " ";
    }
    file << "\n\n";
}   

void codeBits(char* c, mapString codes, ofstream& fileName, unsigned& binary)   // zapisuje binarnie znaki, wg ich kodow (przesuniecia bitowe)
{
    int i, j, size;
    for (j = 0; j < 4; j++)
    {
        for (i = 0; i < codes[c[j]].length(); i++)
        {
            if (codes[c[j]].at(i) == '1')
            {
                binary <<= 1;                          
                binary += 1;
            }
            else if (codes[c[j]].at(i) == '0')
            {
                binary <<= 1;
            }
            size++;
        }    
        if (size%8 == 0)
        {
            fileName.write((char*) &binary, sizeof(char));
            binary = 0;
        }
    }    
}
    
void loadCodes(mapBin& codeMap, ifstream& file)                                 // pobiera kody znaków z pliku do dekompresji
{
    char c1;
    char c2;
    unsigned binCodes;
    
    while (file.good())
    {
        c1 = file.get();
        if (file.eof())
        {
            break;
        }
        c2 = file.get();
        if (file.eof())
        {
            break;
        }
        else if ((c1 == '\n') && (c2 == '\n'))
        {
            break;
        }
        else 
        {
            do
            {
                if (c2 == '1')
                {
                    binCodes <<= 1;
                    binCodes += 1;
                }
                else if (c2 ==  '0')
                {
                   binCodes <<= 1;
                }       
                c2 = file.get();  
            } 
            while (c2 != ' ');
            codeMap[c1] = binCodes;
            binCodes = 0;
        }
        
    }
}

void decode(mapBin& codeMap, ifstream& file, string fname)                      // ma dekodowac...nie dziala narazie.
{
     unsigned buff, tmp;
     int i, mask;
     bool checked = false;
     fname += "_d";
     ofstream output(fname.c_str());
     
     
     while (file.good())
     {
         file.read((char*) &buff, sizeof(char));
         mask = 8;
         tmp = buff;
         for (i = 7; i< 0; i++)
         {
              checked = false;
              tmp >>= i;
              tmp = tmp & ((1 << mask) - 1);
              for (mapBin::iterator it = codeMap.begin(); it != codeMap.end(); it++)
              { 
                  if ((tmp ^ it->second) == 0)
                  {
                      output << it->first;
                      checked = true;
                      break;
                  }
              }
              if (checked == true)
              {
                  mask = i;
                  tmp == buff;
              }
         } 
     }
         
}
    
int main (int argc, char* argv[])
{
    
    
    string fname;
    ifstream file;
    mapType counter;
    mapType::iterator it;
    mapString charCodeMap;
    mapString::iterator codeIt;
    char key;
    
    while (key != 'q')
    {
        cout << "Co chcesz zrobic?       <q zeby wyjsc>" << endl;
        cout << "1. Skompresowac plik" << endl;
        cout << "2. Zdekompresowac plik" << endl;
        cin >> key;
        if (key == 'q')
        {
            break;
        }
        else if (key == '1')
        {
            if (argc != 1)
            {
	            fname = argv[1];
            }
            else
            {
	            cout << "Podaj nazwe pliku \n";
                cin >> fname;
            }
            file.open(fname.c_str());
            if (!file)
            {
                cout << "Nie ma takiego pliku, wprowadz inna nazwe" << endl << endl;
                continue;
            }
            countSymbols(counter, file);
            for (it = counter.begin(); it != counter.end(); it++)
            {
                cout << it->first << " => " << it->second << endl;
            }   
            file.close(); 
            huffman(counter, charCodeMap);
    
            for (codeIt = charCodeMap.begin(); codeIt != charCodeMap.end(); codeIt++)
            {
                cout << codeIt->first << " => " << codeIt->second << endl;
            }   
    
            string newFile;
            unsigned binaryData = 0;
            newFile = fname + "_rg";
            ofstream fOut(newFile.c_str(), ios_base::binary);
            ifstream refile(fname.c_str());
            saveCodes(charCodeMap, fOut);
        
            char c[4];
            while (refile.good())
            {
                for (int i=0; i < 3; i++)
                {        
                    c[i] = refile.get();
                    if (refile.eof())
                    {
                        break;
                    }
                }    
                codeBits(c, charCodeMap, fOut, binaryData);
            }
            refile.close();
            fOut.close();
        }
        else if (key == '2')
        {
            {
                fname = "";
	            cout << "Podaj nazwe pliku \n";
                cin >> fname;
            }
            ifstream fIn(fname.c_str(), ios_base::binary);
            if (!file)
            {
                cout << "Nie ma takiego pliku, wprowadz inna nazwe" << endl << endl;
                continue;
            }
            
            mapBin binMap;
            loadCodes(binMap, fIn);
            decode(binMap, fIn, fname);
         
            fIn.close();
        }
    }
    system("pause");
}

Czy jest mi ktoś w stanie (i ma chęć) pomóc?

0

Oczywiście w dekodowaniu w pętli for ma być
for (i = 7; i > 0; i--)

Co nie zmienia faktu, że dalej nie działa poprawnie - z pliku o zawartości "qwerty" robi mi "rttttt". Z pliku o standardowej testowej zawartości ;] "dupa" robi "dddddddddddddddddddd"

0

codeBits(..., unsigned& binary)
..
binary <<= 1;
..
if (size%8 == 0)
fileName.write((char*) &binary, sizeof(char));

w ogolnosci, tak nie wolno robic. unsinged=int. nie masz pewnosci, ze int zrzutowany
na chama na char da Ci to, czego sie spodziewasz (8 najmlodszych bitow). to samo w decode.
chcesz przeczytac/zapisac 1 char i na nim operowac, uzyj UNSIGNED CHAR, a nie unsigned..

btw. czemu j zawsze obiega 4ry razy?
btw2. czemu 'binary' podajesz z zewnatrz? przeciez to moglaby byc zmienna lokalna..
btw3. masz pewnosc ze owo binary ktore podasz jest puste? a size jaka ma wartosc na starcie funkcji?

decode():
tmp == buff -- wredna literowka

decode():
..
if ((tmp ^ it->second) == 0) ...
zaczynasz np. od TMP>>=7, tzn. niszczysz sobie caly tmp, zostawiasz 1 bit,
potem gdy checked==true zmieniasz maske i odtwarzasz tmp z buff'a.
a co jesli petla sie zakonczy a checked wciaz bedzie false? prefix nie znaleziony,
kontynuujesz ze starym TMP, shiftujesz go.. ale przeciez tam juz nic nie ma

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