この記事は、Yılmaz Yörü氏のhttps://blogs.embarcadero.com/encoding-and-decoding-files-in-c-builder-by-using-bit-shifting/の抄訳です
最速のデータ符号化(エンコード)および復号化(デコード)方式とは?データを保護するための最速の方法は何ですか?データやファイルのエンコードやデコードにシフトを使うことはできますか?文字列やバイナリデータにシフトを使うことはできるのか?
このブログでは、それらの疑問にお答えいたします。
Table of Contents
ビットとビットワイズ演算
ビットは、コンピュータやデジタル通信における情報の最も基本的な単位です。実際には、すべての演算子は主にビット演算に基づいており、「ビットワイズ演算子」とも呼ばれます。
コンピュータプログラミングでは、ビット文字列、ビット配列、または2進数(ビット文字列とみなす)を、個々のビット、1、0のレベルで操作することをビットワイズ演算と言います。ビット演算は、高レベルの算術演算の基本であり、プロセッサが直接サポートしているため、高速でシンプルな動作を実現しています。ほとんどのビット演算は、結果が入力オペランドの1つを置き換える 2オペランド命令として表されます。
このようなコンピュータのマイクロアーキテクチャの基本的な知識があるからこそ、ビット演算子を知っておくことは非常に重要なのです。C言語は最も古いプログラミング言語の一つで、他のプログラミング言語のオペランドや演算子の多くは、C言語からヒントを得ています。CとC++には同じ演算子があり、ほとんどの演算子は他のプログラミング言語でも同じです。一般的な演算子については、「C++の基礎: 演算子の使い方を学ぶ」をご覧ください。
それでは、ビットシフトとエンコード-デコードの例を見てみましょう。
データのビットシフト
ビット単位のオペランドの1つにビットシフトがあり、左シフトの’<<‘オペランドと右シフトの’>>‘オペランドがあります。ビット演算は、上述のようにコンピュータのマイクロアーキテクチャのため、マシンコードやC++では最も高速な演算です。多くのエンコードおよびデコード方法があり、ハッシュ符号化の方法もあります。最も簡単で高速な符号化(エンコード)方式の一つが「ビットシフトデータ」です。この方法は、データファイルのエンコードおよびデコードに利用できます。
左シフトと右シフト
例えば、cを文字とすると、ビットシフトを利用して以下のように文字のエンコードとデコードを行うことができます。
1 2 |
c = c << 1; // Encoding with Left Bit Shifting c = c >> 1; // Decoding with Right Bit Shifting |
このコード例は、127未満の文字数でうまく機能します。シフトする際に先頭のビットが失われます(左にシフトすると左のビットが、右にシフトすると右のビットが失われる)。これらのビットをすべてバイナリデータに保持するには、循環ビットシフト(Circular Bit Shifting)を行う必要があります。
循環左シフトと循環右シフト
循環ビットシフトを使用すれば、データのエンコードやデコードの際に、ビットを失うことはありません。最大8ビットのうち2ビットをシフトしたい場合は、以下のように左右の循環ビットシフトを行います。
1 2 |
c =(c << 2)|(c >> (8 - 2); // Encoding with Circular Left 2 Bits Shifting c =(c >> 2)|(c << (8 - 2); // Decoding with Circular Right 2 Bits Shifting |
複雑な循環左シフトと循環右シフト
例えば、(1+i%7)を加えることで、シフトするビット数に複雑性を与えることができます。
1 2 |
c =(c << (1+i%7))|(c >> (8 - (1+i%7)); // Encoding by Circular Left Bits Shifting with Complexity c =(c >> (1+i%7))|(c << (8 - (1+i%7)); // Decoding by Circular Right Bits Shifting with Complexity |
C++Builderでデータファイルをビットシフトする
例えば、C++Builderでデータファイルのエンコードとデコードを行うために、この「複雑な循環ビットシフト法」を使用することができます。C++Builderでこの例を実行するには、
1.C++Builder VCLプロジェクトを新規に作成し、すべてのファイルをプロジェクトフォルダに保存します。
2.TButtonコンポーネントを2つフォーム上に配置し、Button1->Caption = “Encode”、Button2->Caption = “Decode “に変更します。
3. TMemoとTOpenDialogコンポーネントをパレットからドラッグしてフォームに配置します。
4. F12を押してコードモードに切り替え、以下の例のようにコードにヘッダーを追加します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
//--------------------------------------------------------------------------- #include <vcl.h> #include <iostream> #include <fstream> #include <string> #pragma hdrstop #include "Encoding_and_Decoding_Data_File_VCL_Unit1.h" //--------------------------------------------------------------------------- #pragma package(smart_init) #pragma resource "*.dfm" TForm1 *Form1; //--------------------------------------------------------------------------- __fastcall TForm1::TForm1(TComponent* Owner) : TForm(Owner) { } |
5. Button1の「Encode」ボタンをダブルクリックし、OnClickイベントハンドラに以下のような「エンコードファイルを開き、エンコードし、エンコードされたファイルを保存する」コードを実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
//--------------------------------------------------------------------------- void __fastcall TForm1::Button1Click(TObject *Sender) { // ENCODING OpenDialog1->Title = "Choose a File to Encode"; OpenDialog1->DefaultExt = ""; OpenDialog1->FileName = ""; OpenDialog1->Execute(); if(OpenDialog1->FileName.Length()>0) { unsigned char c; // std::bitset<8> x; unsigned int i=0; std::ifstream indata; std::ofstream outdata; indata.open(OpenDialog1->FileName.w_str(), std::fstream::binary); if(!indata) { Memo1->Lines->Add("Error: Input file could not be opened"); exit(1); } outdata.open((OpenDialog1->FileName+".enc").w_str(), std::fstream::binary); if(!outdata) { Memo1->Lines->Add("Error: Output file could not be openedn"); indata.close(); exit(1); } Memo1->Lines->Add("nEncoding..n"); i=0; while ( !indata.eof() ) { c=indata.get(); // get the next char of data or indata.get(); c =(c << (1+i%7))|(c >> (8 - (1+i%7))); // Encoding by Circular Left Bit Shifting with Complexity outdata.put(c); // out the encoded char i++; } indata.close(); outdata.close(); Memo1->Lines->Add("Done! '"+OpenDialog1->FileName+".enc' saved!"); } } |
6. Button2の「Decode」ボタンをダブルクリックし、OnClickイベントハンドラに以下のような「エンコードファイルを開き、デコードし、デコードされたファイルを保存する」コードを実装します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
//--------------------------------------------------------------------------- void __fastcall TForm1::Button2Click(TObject *Sender) { OpenDialog1->Title = "Choose .enc File to Decode"; OpenDialog1->DefaultExt = ""; OpenDialog1->FileName ="*.enc"; OpenDialog1->Execute(); if(OpenDialog1->FileName.Length()>0) { unsigned char c; // std::bitset<8> x; unsigned int i=0; std::ifstream indata; std::ofstream outdata; // DECODING mytest.dat to mytest2.txt // --------------------------------------------------------- indata.open(OpenDialog1->FileName.w_str(), std::fstream::binary); if(!indata) { Memo1->Lines->Add("Error: Input file could not be opened"); exit(1); } outdata.open((OpenDialog1->FileName+".dec").w_str(), std::fstream::binary); if(!outdata) { Memo1->Lines->Add("Error: Output file could not be openedn"); indata.close(); exit(1); } i=0; Memo1->Lines->Add("nDecoding..n"); while ( !indata.eof() ) { c= indata.get(); // get the next char of data //c = c >> 1; // Decoding with Right Bit Shifting c =(c >> (1+i%7))|(c << (8 - (1+i%7))); // Decoding by Right Bit Shifting with Complexity outdata.put(c); // out the encoded char i++; } indata.close(); outdata.close(); Memo1->Lines->Add("Done! '"+OpenDialog1->FileName+".dec' saved!"); } } |
7. F9を押すと、プロジェクトをビルドし、デバッグ実行します。これでエンコードするファイルを選択し、*.encファイルをエンコードして、*.decファイルとしてデコードすることができます。
このビットシフト手法を使用して、データ、ユーザー名、パスワードなどの機密事項を保護することができます。また、このエンコードされたデータを暗号化ハッシュ関数(SHA、SHA2、MD5、BobJenkinsなど)を使ってハッシュ化することもできます。詳しくは「WindowsのモダンC++で強力な暗号化ハッシュ関数を学ぶ (SHA, SHA2, MD5, BobJenkins)」を参照ください。
Design. Code. Compile. Deploy.
Start Free Trial Upgrade Today
Free Delphi Community Edition Free C++Builder Community Edition