発熱するマイナー魂

隠れた名作の発掘が生きがい。サブカル作品の感想とIT技術メモ中心のブログです。

文字列をBase64でエンコードするjavaプログラムに挑戦


スポンサードリンク

勉強会で文字列をBase64エンコードするjavaのプログラムを作成する機会があったので,その時に書いたコードを修正して載せています.単純にエンコードするだけれあれば,sun.misc.BASE64Encoderを利用する方法がありますが,ここではエンコードする部分を1から作っています.


Base64変換手順
WikkipediaでBase64の変換例を参照すると,変換は次のように行うようです.


1.元データ
 文字列: "ABCDEFG"
 16進表現: 41, 42, 43, 44, 45, 46, 47
 2進表現: 0100 0001, 0100 0010, 0100 0011, 0100 0100, 0100 0101, 0100 0110, 0100 0111
2.6bitずつに分割
 010000 010100 001001 000011 010001 000100 010101 000110 010001 11
3.2bit余るので、4bit分0を追加して6bitにする
 010000 010100 001001 000011 010001 000100 010101 000110 010001 110000
4.変換表により、4文字ずつ変換
 "QUJD","REVG","Rw"
5.2文字余るので、2文字分 = 記号を追加して4文字にする
 "QUJD","REVG","Rw=="
6.Base64文字列
 "QUJDREVGRw=="


プログラム
上記のアルゴリズムを元に,”ABCDEF”という文字列を" QUJDREVGRw=="へと変換することを目的としてプログラムを作成しました.

import java.io.UnsupportedEncodingException;

public class Base64 {

     private static String indexTable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";

     public static void main(String[] args) {
          System.out.println(Base64.encode("ABCDEFG"));
     }

     public static String encode(String stringData) {
          //String型の文字列をUTF-8形式のbyte型配列に格納
          byte[] byteData;
          try {
               byteData = stringData.getBytes("UTF-8");
          } catch (UnsupportedEncodingException e) {
               return "UnsuppotedEncodingException";
          }

          //8bitから4bitごとに区切って格納した配列を生成
          byte expandedData[] = new byte[byteData.length * 2];
          for (int i = 0; i < byteData.length; i++) {
               expandedData[i * 2] = (byte) ((byteData[i] & 0xF0) >> 4);
               expandedData[i * 2 + 1] = (byte) (byteData[i] & 0x0F);
          }

          //3つの4bitのデータを一度に処理
          //まず,expandDataの要素数-3まで処理し,要素数-2,要素数-1は場合分けして処理
          byte dividedBy6BitUpper = 0;
          byte dividedBy6BitLower = 0;
          StringBuilder result = new StringBuilder();
          for (int i = 0; i < expandedData.length -2; i += 3) {
               dividedBy6BitUpper = (byte) (((expandedData[i]) << 2) | ((expandedData[i + 1] & 0x0C) >> 2));
               dividedBy6BitLower = (byte) (((expandedData[i + 1] & 0x03) << 4) | (expandedData[i + 2]));
               result.append(indexTable.charAt(dividedBy6BitUpper));
               result.append(indexTable.charAt(dividedBy6BitLower));
          }
          if (expandedData.length % 3 == 2) {
               dividedBy6BitUpper = (byte) (((expandedData[expandedData.length -2]) << 2) | ((expandedData[expandedData.length - 1] & 0x0C) >> 2));
               dividedBy6BitLower = (byte) (((expandedData[expandedData.length -1] & 0x03) << 4));
               result.append(indexTable.charAt(dividedBy6BitUpper));
               result.append(indexTable.charAt(dividedBy6BitLower));
          } else if (expandedData.length % 3 == 1) {
               dividedBy6BitUpper = (byte) (((expandedData[expandedData.length -2]) << 2));
               result.append(indexTable.charAt(dividedBy6BitUpper));
          }

          //余った文字を=で埋める
          if (result.length() % 4 != 0) {
               for (int i = 0; i < result.length() % 4; i++) {
                    result.append("=");
               }
          }

          return result.toString();
     }
}


反省
8bitのbyteデータを4bit形式で扱いやすくするために,受け取ったデータの2倍のサイズを持つ配列を作っているところがいただけないです.配列を作成せずに操作することもできたと思いますが,いかんせん,ビット操作に慣れていないため,個人的にやりやすい方法を重視して作りました.実用には耐えられないけれど,Base64のアルゴリズムが理解できた!