この記事は、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を文字とすると、ビットシフトを利用して以下のように文字のエンコードとデコードを行うことができます。
c = c << 1; // Encoding with Left Bit Shifting c = c >> 1; // Decoding with Right Bit Shifting
このコード例は、127未満の文字数でうまく機能します。シフトする際に先頭のビットが失われます(左にシフトすると左のビットが、右にシフトすると右のビットが失われる)。これらのビットをすべてバイナリデータに保持するには、循環ビットシフト(Circular Bit Shifting)を行う必要があります。
循環左シフトと循環右シフト
循環ビットシフトを使用すれば、データのエンコードやデコードの際に、ビットを失うことはありません。最大8ビットのうち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)を加えることで、シフトするビット数に複雑性を与えることができます。
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を押してコードモードに切り替え、以下の例のようにコードにヘッダーを追加します。
//--------------------------------------------------------------------------- #include#include #include #include #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イベントハンドラに以下のような「エンコードファイルを開き、エンコードし、エンコードされたファイルを保存する」コードを実装します。
//---------------------------------------------------------------------------
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イベントハンドラに以下のような「エンコードファイルを開き、デコードし、デコードされたファイルを保存する」コードを実装します。
//---------------------------------------------------------------------------
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)」を参照ください。
Reduce development time and get to market faster with RAD Studio, Delphi, or C++Builder.
Design. Code. Compile. Deploy.
Free Delphi Community Edition Free C++Builder Community Edition







