Base64
仕事で作っているプログラムに、画像ファイルをBase64エンコードするロジックが必要になった。エンコードしてテキストファイルに埋め込むのである。
開発言語はDelphi(Object Pascal)、プラットホームはWindowsXPだ。
まずは、Indyのコンポーネントを試そうと思ったのだが――使い方が皆目わからない。マニュアルは、どこだ?
と、いうわけで、ネットを検索したら公開されているコードが見つかった(感謝)。シメシメとこれを利用して、取りあえず目標は達成できた。
が、別の機能を盛り込む段になって、やや面倒なことになった。エンコード部分に少し手を入れなければならなそうだ。しかし他人の書いたコードは判らんし、結構色々いじる必要がありそう。
まあ、そんなわけで、エンコードルーチンを一から書くことにした。Base64のロジック自体はそれ程難しいことはない。ファイルを頭から3バイト(24ビット)読んで、6ビットずつ4つに分割、それぞれの6ビットが表す数値(0?63)を予め決められた文字に割り当てる。6ビットで4文字だから、Base64らしい。
詳細は、ググッで貰えば当たるだろう。
とはいえ、イザ作るとなるとそれなりにボロボロにはなるわけで、特にバイトの並び、ビッグエンディアン、リトルエンディアンがどっちがどっちだか判らなくなって落とし穴に落ちまくりだった。
で、出来上がったのが以下のルーチンだ。
Base64.pasとして保存し、使いたいアプリでusesに追加すれば良い。
MIMEでは、エンコードした文字列は76文字で改行することになっている(らしい)。BRにはこの改行コードを指定する。例えば、
b64enc(FromStream, ToStream, brCRLF);
とすれば、FromStreamからデータを読み、Base64エンコードして結果をToStreamに書き出す。改行コードには$0D0Aが付けられる。
改行したくないときは、brNILを指定する。
てなことで、ここ数日、楽しい時を過ごしている。
デコードルーチンやらを付け加えたコードも公開しちゃいます。Base64.pas
最適でもないし、速くもないし、バグもありそうですが、突っ込むときはコッソリお願いします。
=================================================================
{ Base64 エンコード ルーチン 2008/03/06 flyman }
unit Base64;
interface
uses
Classes, SysUtils;
{ストリームをエンコードしてストリームへ}
procedure b64enc(FMStream, TOStream: TStream; BR: Word); Overload;
const
TABLE : array[0..63] of Char = ‘ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/’;
brCRLF : Word = $0A0D;
brCR : Word = $000D;
brLF : Word = $000A;
brNIL : Word = $0000;
implementation
// 3バイトデータの並び順を変える
function ReOrder(Src: LongWord): LongWord;
type
Tmp = array[0..3]of Byte;
begin
Result := Src;
Tmp(Result)[2] := Tmp(Src)[0];
Tmp(Result)[0] := Tmp(Src)[2];
end;
{ストリームをエンコードしてストリームへ}
procedure b64enc(FMStream, TOStream: TStream; BR: Word);
var
i,m,count : Integer;
Src : LongWord;
SSize : LongInt;
begin
FMStream.Position := 0;
TOStream.Position := 0;
count := 0;
SSize := FMStream.Size;
for i:=1 to (SSize div 3) do begin
if count=76 then begin
if (BR=brCRLF) then TOStream.Write(brCRLF, 2)
else if (BR=brCR) then TOStream.Write(brCR, 1)
else if (BR=brLF) then TOStream.Write(brLF, 1);
count := 0;
end;
FMStream.Read(Src, 3);
Src := ReOrder(Src);
TOStream.Write(TABLE[(Src shr 18 and $3F)], 1);
TOStream.Write(TABLE[(Src shr 12 and $3F)], 1);
TOStream.Write(TABLE[(Src shr 6 and $3F)], 1);
TOStream.Write(TABLE[(Src and $3F)], 1);
inc(count, 4);
end;
m := SSize mod 3;
if m>0 then begin
if count=76 then begin
if (BR=brCRLF) then TOStream.Write(brCRLF, 2)
else if (BR=brCR) then TOStream.Write(brCR, 1)
else if (BR=brLF) then TOStream.Write(brLF, 1);
end;
FillChar(Src, 3, 0);
FMStream.Read(Src, m);
Src := ReOrder(Src);
case m of
1: begin
TOStream.Write(TABLE[(Src shr 18 and $3F)], 1);
TOStream.Write(TABLE[(Src shr 12 and $3F)], 1);
end;
2: begin
TOStream.Write(TABLE[(Src shr 18 and $3F)], 1);
TOStream.Write(TABLE[(Src shr 12 and $3F)], 1);
TOStream.Write(TABLE[(Src shr 6 and $3F)], 1);
end;
end;
TOStream.Write(’==’, 3 - m);
end;
end;
end.
TrackBacks
TrackBack URL : http://www.kestrel.jp/modules/wordpress/wp-trackback.php/79
この投稿には、まだコメントが付いていません