基数変換 - n進数をm進数に変換する
PHPにはbase_convert()という関数があり、2〜36進数文字列どうしの変換ができます。
Pythonは、「int("ABCD",base=16)」として2〜36進数表現からlong値へ変換はできますが、逆にlong値を組み込み関数またはフォーマット文字列を使って2、8、16進数表現にしか変換できません。
ここではbase_convertと似たようなものを作ってみます。ただし数値は文字列としてではなく、桁ごとのリスト(桁の大きい方から並べる、いわゆるbig-endian)として渡すことにします。
## (Python2.4用) #def any(iterable): # for element in iterable: # if element: # return True # return False def base_convert(nl, ibase, obase): o = [] while any(nl): c = 0 for i in xrange(len(nl)): c = c * ibase + nl[i] nl[i],c = divmod(c,obase) o.append(c) o.reverse() return o ## 1557(13) == 3114(10) print base_convert([1,5,5,7], 13, 10) ## 65535(10) == FFFF(16) print base_convert([6,5,5,3,5], 10, 16)
[3, 1, 1, 4] [15, 15, 15, 15]
基数変換とバイナリエンコーディングの関係
「[ord(c) for c in s]」とすれば任意のバイナリ列を256進数値とみなすことができます。メールの添付ファイルや日本語ヘッダのエンコーディングに使われているBASE64は、いわば256進数と64進数との間で変換を行っていることになります。
ただしPythonにはbase64モジュールというものが用意されていますので、BASE64のエンコード/デコードのためにわざわざ上記のようなコードを書く必要はありません。そもそもBASE64はバイナリ列を6ビットずつ切り分けて処理すればよいので、基数変換など使わずビット演算を使った変換の方が効率が良いです。国際化ドメイン名などに使われるBASE32も同様です。
しかしBASE85やBASE24といった、ビット演算では困難な方式もあり、こういったものには基数変換を使ったエンコード/デコードが有効です。BASE85はPostScriptのデータ列に、BASE24はWindows製品のプロダクトキーに使われています。
次の例では、Windowsレジストリの某所から取り出したプロダクトキー(15バイトのバイナリ列)のを25桁の24進数として表示しています。
(※このプロダクトキーは模擬的なもので、実際に使われているものではありません)
pkeystr = "\xA8\x61\xD6\xF5\xC4\xD9\x5E\x7C\x0F\x78\x8D\xBD\xFB\x02\x00" base24digit = "BCDFGHJKMPQRTVWXY2346789" pkeynum = base_convert([ord(c) for c in reversed(pkeystr)], 256, 24) ## 桁数が足りない場合0を左に詰める if len(pkeynum) < 25: pkeynum = [0]*(25-len(pkeynum)) + pkeynum ## 5桁ごとにハイフンを入れて表示 pkey = [base24digit[x] for x in pkeynum] for i in (20,15,10,5): pkey.insert(i,"-") print "".join(pkey)
BCDFG-HJKMP-QRTVW-XY234-6789B