|
16番会議室「玉石混淆みんなで作るSample蔵」に寄せられたサンプル
"base64、エンコード&デコード"
base64形式でのエンコードとデコードのサンプルです。
エンコードは、バイナリーデータを6ビットずつに切り分けて、
64種類の文字('A'〜'Z'、'a'〜'z'、'0'〜'9'、'+'、'/')に
割り当てます。6ビット毎の変換なので、元データ3バイトにつき
4バイトの出力になります。元データが3の倍数でない場合には
出力の不足分を‘=’で埋めます。
デコードはエンコードの逆です...(手抜きの説明で失礼)
下記のサンプルコードは、フォームに以下の5個のオブジェクト
を置き、
RichEdit1: TRichEdit;
OpenDialog1: TOpenDialog;
SaveDialog1: TSaveDialog;
Button1: TButton;
Button2: TButton;
Button1.OnClick でファイルをエンコードしてRichEdit1に表示、
Button2.OnClick でデコードしてファイルに保存します。
注意すべき点は6ビットずつ読み書きする際に、バイトデータ
の並び順の調整が必要になることです。
詳しくは「エンディアン変換」をキーにしてWeb等を検索し
てみてください。
// 3バイトデータの並び順を変える
function exchange_0_2(Src: DWORD): DWORD;
type
TTemp = array[0..3]of BYTE;
begin
Result := Src;
TTemp(Result)[2] := TTemp(Src)[0];
TTemp(Result)[0] := TTemp(Src)[2];
end;
// base64形式でエンコード
function base64encode(Src: Pointer; Len: Integer): string;
const
Table: PChar =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
var
I: Integer;
function doNbyte(N: Integer): string;
var
Value: DWORD;
Dst: array[0..3]of Char;
I: Integer;
begin
if not(N in [1..3]) then Exit;
FillChar(Dst, sizeof(Dst), 0);
// Nバイト読み込んで、並び変える
Move(PBYTE(Src)^, Value, N);
Inc(Integer(Src), N);
Value := exchange_0_2(Value);
// 6ビット毎に変換
for I := 3 downto 0 do
begin
if I > N then
Dst[I] := '=' // 変換対象外は‘=’でパディングする
else Dst[I] :=
Table[Value and $3f];
Value := Value shr 6;
end;
// 変換したデータ(4文字)を返す
SetString(Result, Dst, 4);
end;
begin
Result := '';
for I := 0 to Len div 3 - 1 do
Result := Result + doNbyte(3);
// 3の倍数バイトから余るデータを処理する
if (Len mod 3) > 0 then
Result := Result + doNbyte(Len mod 3);
end;
// base64形式のデータをデコード
procedure base64decode(ms: TStream; S: string);
var
P: PChar;
I: Integer;
function decode(code: BYTE): BYTE;
begin
case Char(code) of
'A'..'Z': Result := code - BYTE('A');
'a'..'z': Result := code - BYTE('a') + 26;
'0'..'9': Result := code - BYTE('0') + 52;
'+': Result := 62;
'/': Result := 63;
else Result := 0;
end;
end;
procedure doNbyte;
var
I, N: Integer;
Dst: DWORD;
begin
// パディング文字‘=’の有無をチェック
case Pos('=', P) of
3: N := 1;
4: N := 2;
else N := 3;
end;
Dst := 0;
for I := 3 downto 0 do
begin
// 6ビット毎に変換する
Inc(Dst, decode(PBYTE(P)^) shl (I * 6));
Inc(P);
end;
// 変換したデータの並び順を変える
Dst := exchange_0_2(Dst);
ms.Write(Dst, N);
end;
begin
P := PChar(S);
// 4文字ずつ変換
for I := 0 to (Length(S) div 4) - 1 do
doNbyte;
end;
procedure TForm1.SaveToFile(FileName: string);
var
Stream: TFileStream;
I: Integer;
begin
Stream := TFileStream.Create(FileName, fmCreate);
try
for I := 0 to RichEdit1.Lines.Count - 1 do
base64decode(Stream, RichEdit1.Lines[I]);
finally
Stream.Free;
end;
end;
procedure TForm1.LoadFromFile(FileName: string);
const
LineSize = 76; // 1行76文字まで
var
Stream: TFileStream;
Buffer: Pointer;
ReadSize, BufferSize: Integer;
begin
Stream := TFileStream.Create(FileName, fmOpenRead);
try
BufferSize := LineSize div 4 * 3;
GetMem(Buffer, BufferSize);
try
RichEdit1.Clear;
while true do
begin
ReadSize := Stream.Read(PBYTE(Buffer)^, BufferSize);
if ReadSize <= 0 then break;
RichEdit1.Lines.Add(base64encode(Buffer, ReadSize));
end;
finally
FreeMem(Buffer);
end;
finally
Stream.Free;
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
// base64でエンコードしてRichEdit1に表示する
if OpenDialog1.Execute then
LoadFromFile(OpenDialog1.FileName);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
// RichEdit1のテキストをデコードして保存する
RichEdit1.WordWrap := FALSE;
if SaveDialog1.Execute then
SaveToFile(SaveDialog1.FileName);
end;
P.S.
上記のサンプルコードは、あまりきれいなコードとは言えません。
少しブラッシュアップした方が良いかもしれません。
2001/04/23、河邦 正(GCC02240@nifty.com)
(http://homepage2.nifty.com/kht0000/、NIFTY外へ私作Componentの
公開用)
Original document by 河邦 正 氏 ID:(GCC02240)
ここにあるドキュメントは NIFTY SERVEの Delphi Users' Forum の16番会議室「玉石混淆みんなで作るSample蔵」に投稿されたサンプルです。これらのサンプルはボーランド株式会社がサポートする公式のものではありません。また、必ずしも動作が検証されているものではありません。これらのサンプルを使用したことに起因するいかなる損害も投稿者、およびフォーラムスタッフはその責めを負いません。使用者のリスクの範疇でご使用下さい。
Copyright 1996-2002 Delphi Users' Forum
|