ホーム 主筆 その他ソフト その他情報 Syuhitu.org English

Windows関連

スクリーンセーバー作成法

半透明ウインドウの性能

bootfont.bin

キャビネット形式

ウインドウスタイルをいじる

Java製ソフトをServiceに登録する

イベントログにメッセージを出力する

コントロールパネルにアイコンを追加する

スクリプトによる拡張1

スクリプトによる拡張2

ガジェットの作成

大容量メモリ

メモリ搭載量の下限に挑む

スパースファイルにする

表示されるアイコンの種類を調べてみた

メモリマップIOとエラー処理

ファイルを作る順番と速度の関係

Cryptography API: Next Generationを使う

Windows 10のアクセントカラー

iSCSIディスクにバックアップを取る

サーバプロセスを分離して実装する

サーバプロセスを分離して実装する - F#

レジストリに大量に書き込む

Solaris関連

OpenGL

Solaris設定

ディレクトリの読み込み

主筆プラグイン開発

マルチスレッドでの開発

door

音を出す

Blade100の正しい虐め方

パッケージの作成

画像入出力

BMPファイル

ICOファイル

ANIファイル

JPEGファイル

減色アルゴリズム

減色アルゴリズムの並列化

その他アルゴリズムなど

自由軸回転

Base64

文字列操作

CPU利用率の取得

正規表現ライブラリ

メタボールを作る

メタボールを作る2

正規表現とNFA・DFA

C言語の構文解析

液晶ディスプレイを解体してみた

iSCSIの理論と実装

単一フォルダにファイルを沢山作る

USB-HUBのカスケード接続

SafeIntの性能

VHDファイルのフォーマット

USBメモリに書き込み続けてみた

Base64エンコード

今更、ここでBase64エンコード方式を説明しても意味がないようなきもするが。

エンコード方式

Base64では8ビットバイトのデータを読み込み、6ビット単位に区切り、対応する文字を出力する、という作業を行います。

図示すると以下の通り。

読み込むデータ

0 1 0 0 0 0 0 1 バイトの区切り 0 1 0 0 0 0 1 0

6ビットごとに区切ったデータ(不足する分は0を格納)

0 1 0 0 0 0 区切り 0 1 0 1 0 0 区切り 0 0 1 0 0 0

でもって、6ビットごとに対応する文字を出力する。

Q U I

さらに、出力するデータが4バイト単位になるように「=」を付け加える。

Q U I =

なお、対応表は以下の通り

16進数 文字 16進数 文字   16進数 文字 16進数 文字
0x00 A 0x10 Q   0x20 g   0x30 w
0x01 B 0x11 R   0x21 h   0x31 x
0x02 C 0x12 S   0x22 i   0x32 y
0x03 D 0x13 T   0x23 j   0x33 z
0x04 E 0x14 U   0x24 k   0x34 0
0x05 F 0x15 V   0x25 l   0x35 1
0x06 G 0x16 W   0x26 m   0x36 2
0x07 H 0x17 X   0x27 n   0x37 3
0x08 I 0x18 Y   0x28 o   0x38 4
0x09 J 0x19 Z   0x29 p   0x39 5
0x0A K 0x1A a   0x2A q   0x3A 6
0x0B L 0x1B b   0x2B r   0x3B 7
0x0C M 0x1C c   0x2C s   0x3C 8
0x0D N 0x1D d   0x2D t   0x3D 9
0x0E O 0x1E e   0x2E u   0x3E +
0x0F P 0x1F f   0x2F v   0x3F /

データ量

上記で示したとおり、6ビットを1文字(8ビット)で表現するため、結局データ量は8/6に増えます。

狭い回線でメールをやりとりしてる時に、思ったより多くのデータが伝送されているのは、こんな理由があるからです。(もちろん、そのほかのプロトコルによるオーバーヘッドもあるのですが。)

プログラム

とりあえず、以下にプログラムを紹介しておきます。

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

// 作業用領域の構造体
struct tagTT
{
	unsigned char buf;	// バッファ
	FILE *infile;	// ファイル
	int nlen;		// バッファに保持しているビット長
};

int help();
int encode( FILE *infile, FILE *outfile );
int GetB64Num( struct tagTT *pWork );
int decode( FILE *infile, FILE *outfile );
unsigned char CtoNum( int c );
int GetNumB64( struct tagTT *pWork );

int main(int argc, char* argv[])
{
	int flg_e;	// 1つ目の引数が"e"ならば0
	int flg_d;	// 1つ目の引数が"d"ならば0
	FILE *infile = NULL;	// 入力ファイル
	FILE *outfile = NULL;	// 出力ファイル

	// 引数を確認
	if ( argc != 4 ) return help();	// 引数の数が不正
	flg_e = strcmp( argv[ 1 ], "e" );
	flg_d = strcmp( argv[ 1 ], "d" );
	if ( flg_e && flg_d ) return help();	// 指定されたフラグが不正

	// 入力ファイルを開く
	infile = fopen( argv[ 2 ], "rb" );
	if ( NULL == infile ) {
		printf( "入力ファイル %s のオープンに失敗\n", argv[ 2 ] );
		return -2;
	}

	// 出力用ファイルを開く
	outfile = fopen( argv[ 3 ], "wb" );
	if ( NULL == outfile ) {
		fclose( infile );	// すでに開いている入力用ファイルを閉じる
		printf( "出力ファイル %s のオープンに失敗\n", argv[ 3 ] );
		return -3;
	}

	if ( 0 == flg_e ) return encode( infile, outfile );	// エンコード
	if ( 0 == flg_d ) return decode( infile, outfile );	// デコード

	// ファイルを閉じる
	fclose( infile );
	fclose( outfile );

	return 0;
}

// 通称:使用方法を表示する
int help()
{
	printf( "使用法\n" );
	printf( "b64 e|d 入力ファイル名 出力ファイル名\n" );
	printf( " e : 入力ファイルをBASE64でエンコードし、出力する。\n" );
	printf( " d : BASE64形式の入力ファイルをデコードし、出力する。\n" );
	return -1;
}

// 通称:エンコード
// 機能:標準入力から読み込み、BASE64でエンコードを行い標準出力に出力する
// 引数:infile 入力ファイル
//    outfile 出力ファイル
int encode( FILE *infile, FILE *outfile )
{
	int cnt = 0;	// 文字数のカウント
	int w = 0;
	struct tagTT work;	// 作業用領域
	const char *pTable = // 変換表
		"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

	work.buf = 0;	// 作業用領域に初期値を設定
	work.nlen = 0;
	work.infile = infile;

	w = GetB64Num( &work );	// 最初の1文字を取得
	while ( w != -1 ){
		putc( pTable[ w ], outfile );	// 取得した数値に対応した文字を出力
		w = GetB64Num( &work );	// 1文字ずつ取得
		cnt++;
	}
	// 4バイト単位になるよう=を出力
	while ( cnt % 4 != 0 ){
		putc( '=', outfile );
		cnt++;
	}
	return 0;
}

// 通称:文字を入力し、変換した数値を返す
// 引数:作業用領域の構造体のアドレス
int GetB64Num( struct tagTT *pWork )
{
	unsigned int rw;	// 読み込み時の作業
	unsigned char w;	// 演算用
	unsigned char r;	// 戻り値を保持するため

	// バッファに6ビット残っている場合は、
	// ファイルから読み込まずに、バッファの値を返す
	if ( 6 == pWork->nlen ) {
		r = pWork->buf >> 2;
		pWork->nlen = 0;	// 残りビット長をクリア
		pWork->buf = 0;	// バッファをクリア
		return r;
	}

	rw = getc( pWork->infile );	// 1バイト取得
	if ( EOF == rw ){
		// ファイルの末尾に達した場合
		if ( pWork->nlen ){
			// ビットが残っている場合
			pWork->nlen = 0;
			return pWork->buf >> 2;
		}
		else
			return -1;
	}
	w = rw;	// unsigned int から unsigned charへ変換

	// バッファの値と今回読み込んだ値の内、頭6ビットを返す
	r = ( pWork->buf | ( w >> pWork->nlen ) ) >> 2;
	// バッファには毎回2ビットずつ溜まっていく
	pWork->nlen += 2;
	// 余ったビットをバッファへ格納
	pWork->buf = w << ( 8 - pWork->nlen );
	return r;
}

// 通称:デコード
// 機能:BASE64でエンコードされたファイルを標準入力から読み込み、
//    デコードを行い標準出力に出力する。
// 引数:infile 入力ファイル
//    outfile 出力ファイル
int decode( FILE *infile, FILE *outfile )
{
	unsigned int rw;	// 読み込み時の作業用
	struct tagTT work;	// 作業用領域

	work.buf = 0;	// 作業用領域に初期値を設定
	work.nlen = 0;
	work.infile = infile;

	rw = GetNumB64( &work );	// 1文字取得
	while ( rw != -1 ) {
		putc( rw, outfile );
		rw = GetNumB64( &work );
	}
	return 0;
}

// 通称:1文字ずつ取得する
int GetNumB64( struct tagTT *pWork )
{
	int wr;	// 読み込み時の作業用
	unsigned char w;
	unsigned char r;

	wr = getc( pWork->infile );
	if ( wr == EOF || wr == '=' )
		return -1;	// ファイルの末尾に達したら-1を返す

	w = CtoNum( wr );	// 読み込んだ文字を6ビットの数値に変換
	if ( pWork->nlen == 0 ){
		unsigned char w2;
		// バッファにビットが残っていない場合
		wr = getc( pWork->infile );	// 次の文字を読む
		if ( wr != EOF && wr != '=' )
			w2 = CtoNum( wr );
		else
			w2 = 0;
		// wの値をバッファに移動、wには次の値を格納しておく
		pWork->buf = w << 2;
		w = w2;
		pWork->nlen = 6;
	}

	// バッファの値は毎回2ビットずつ減少してゆく
	pWork->nlen -= 2;	
	// バッファの値と今回読み込んだ値の内、頭8ビットを返す
	r = pWork->buf | ( w >> pWork->nlen );
	// 余ったビットをバッファに格納
	pWork->buf = ( w << ( 8 - pWork->nlen ) );
	return r;
}

// 通称:文字を番号に変換する
unsigned char CtoNum( int c )
{
	if ( c >= 'A' && c <= 'Z' ) return c - 'A';
	if ( c >= 'a' && c <= 'z' ) return ( c - 'a' ) + 26;
	if ( c >= '0' && c <= '9' ) return ( c - '0' ) + 52;
	if ( c == '+' ) return 62;
	if ( c == '/' ) return 63;
	return -1;
}

どうも、あまり美しくない処理方法ですが、まぁ、それは人それぞれということで。


連絡先 - サイトマップ - 更新履歴
Copyright (C)  2000 - 2016 nabiki_t All Rights Reserved.