#include <iostream> #include <string> #include <cstring> #include <fstream> using namespace std; void Compress(string inputFileName, string outputFileName); void UnCompress(string inputFileName, string outputFileName); int main(int argc, char** argv) { if (argc < 3) return 0; if (! strcmp(argv[1], "nz")) { Compress(argv[2], argv[2]); cout << "Compressed file is " << argv[2] << ".nz" << endl; } else if (! strcmp(argv[1], "unz")) { string outputFileName(argv[2]); string::size_type index = outputFileName.rfind(".nz"); if (index != string::npos) outputFileName = "Unz" + outputFileName.substr(0, index); else return 1; UnCompress(argv[2], outputFileName); cout << "Extracting file is " << outputFileName << endl; } return 0; } void Compress(string inputFileName, string outputFileName) { outputFileName += ".nz"; ifstream fin(inputFileName.c_str(), ios::in | ios::binary); ofstream fout(outputFileName.c_str(), ios::out | ios::binary); if (! fin || ! fout) return; int delimiterPre = 0; int delimiterPost = 0; int rawByte, preByte, num = 0; preByte = fin.get(); while ((rawByte = fin.get()) != EOF) { if(rawByte != preByte || num == 255) { if (num > 1) { fout.write((char*) &delimiterPre, sizeof(char)); fout.write((char*) &delimiterPost, sizeof(char)); fout.write((char*) &num, sizeof(char)); } fout.write((char*) &preByte, sizeof(char)); preByte = rawByte; num = 1; } else if (rawByte == preByte) num++; } if (num > 1) { fout.write((char*) &delimiterPre, sizeof(char)); fout.write((char*) &delimiterPost, sizeof(char)); fout.write((char*) &num, sizeof(char)); } fout.write((char*) &preByte, sizeof(char)); fin.close(); fout.close(); } void UnCompress(string inputFileName, string outputFileName) { ifstream fin(inputFileName.c_str(), ios::in | ios::binary); ofstream fout(outputFileName.c_str(),ios::out | ios::binary); if (! fin || ! fout) return; int rawByte, preByte; preByte = fin.get(); while ((rawByte = fin.get()) != EOF) { if (preByte == 0 && rawByte == 0) { while ((preByte = fin.get()) == 0) fout.write((char*) &preByte, sizeof(char)); rawByte = fin.get(); for (int i = 0; i < preByte; i++) fout.write((char*) &rawByte, sizeof(char)); if ((preByte = fin.get()) == EOF) break; } else { fout.write((char*) &preByte, sizeof(char)); preByte = rawByte; } } if (preByte != EOF) fout.write((char*) &preByte, sizeof(char)); fin.close(); fout.close(); }
あと,世の中の高専生はもっと賢いです.こんなコードは書きません.
上のコードは1バイト単位でデータのランレングス圧縮を行うようなものです.
俺,実は学校でデータ圧縮に関して何かやらなきゃいけない立場なようで,
バイナリでデータを扱う練習がてら作ってみたものなんですが,
圧縮になるときと,ならないときが出てきちゃう,ダメダメなものになってしまいました.
ここで簡単にランレングス圧縮の説明ー.
文字列Zeeeeeeeingってなのを考えたとき,
文字列Zeeeeeeeingってなのを考えたとき,
連続した文字に注目して文字列中に数字が出てこないとして,
数字+文字で数字の数だけ文字があることを表すとすれば
文字列Zeeeeeeeingのeは7eと省略でき,最終的に
Z7eingとすることで文字列長を10から6に圧縮でき,
確かこんな操作を行う圧縮をランレングス圧縮と呼ばれる(たぶん).
で,上のコードも1バイト読み取ってそんな操作を行っているんですが,
デリミターで問題?が生じた結果, 圧縮にならないときが生じてしまいました.
ここでデリミターは繰り返しがある位置を表す区切り文字のこととします.
上のコードではデリミターを0x0000にしています.
で0x0000 (文字の繰り返し回数) (繰り返し文字) と 4バイトでランレングス圧縮を試みてるんですが,
これだと5回以上連続で文字が出現しない限り,圧縮にならないことになります.
そこで,4回以下はそのまま出力すればいいやと思っていたら,
0x0000 の表記があった場合それがデリミターか単なる?0x0000なのか
区別がつかなくなることに気がつき,どうしようもなく,そのままにしてあります.
おそらく2回連続は良くある?ので3回以上はランレングス圧縮するようにすれば良くなるかもですが・・・.
で,最終的に上のコードが言いたいことは,1バイト単位でやってもそんな圧縮にならんってことです(たぶん).
圧縮に関しては, 卒研より相当締切り日的なものが近く,
どうにかしなければならないので,今からで遅すぎますが,
どうにかして行きたいと思います.
ちなみに上のコードでC++で書いたHelloを出力する
実行ファイルを圧縮してみたところ,7783 bytesとから 6365 bytesと
見事に微妙に圧縮されました.
他のファイルではデリミッター問題?で元より増加してしまいました(ダメダメです).
ちなみに上のコードでC++で書いたHelloを出力する
実行ファイルを圧縮してみたところ,7783 bytesとから 6365 bytesと
見事に微妙に圧縮されました.
他のファイルではデリミッター問題?で元より増加してしまいました(ダメダメです).
使い方
コンパイル
g++ -o RunLength このコードファイル名.cpp
圧縮 (例としてhelloファイルの圧縮)
./RunLength nz hello
でhello.nzってな圧縮ファイルが作成される.
解凍(伸長)
./RunLength unz hello.nz
でUnzhelloってな解凍ファイルが作成される.
helloとUnzhelloが同じファイルであることを切に願います.
helloとUnzhelloが同じファイルであることを切に願います.