0、写在前面
Windows和Office的序列号就是在最终换算之后,通过Base24编码转换成为可显示字符串的。写这个系列的目的就是做类似的东东。
1、编码原理
在Base32的那篇文章中已经比较详细的说明了,这里只指出与原理相比较,Base24是一个非常奇怪的方案,因为用4个bits作为分码段,只能用到16个,另外8个编码字符用不到,而采用5个bits作为分码段,又少8个字符。这就是之前写了个Base32的原因。
后来想明白了这个问题,还是4个bits一组,然后在通过另外一种方式把24个字符都用上不就行了,我采用的是比较愚蠢的方式,按照位置取8的余数,再加上编码值换算即可。总之样子上已经没啥区别了。
先申明,我并不知道MS采用的Base24的具体的方式是什么,因为和我描述的不同,因为据说它是将114bit编码成为200个bits(25个Bytes),我说的方法显然做不到。目前就到这个地步吧,有空再想想。
2、源代码
只把Encoding和Decoding的两个函数贴出来,其他的都雷同。
public new static String Encode(Byte[] abData) { Int32 dwLoop = 0,dwCharIndex = 0,dwCharCount = 0,dwRem = 0; Char[] acPart = null; StringBuilder sbOutput = null;
if (abData == null || m_acBaseMap == null || m_acBaseMap.Length < m_dwMapLength) return null;
try { dwCharCount = abData.Length * 2; sbOutput = new StringBuilder(dwCharCount); acPart = new Char[2]; } catch (Exception e) { Trace.WriteLine("CLsBase24.Encode: Initialize buffer failed! " + e.Message); } if (acPart == null || sbOutput == null) return null;
for(dwLoop = 0;dwLoop < abData.Length;dwLoop++) { Array.Clear(acPart,0,acPart.Length); // ONE byte will split to TWO characters Math.DivRem(dwLoop,9,out dwRem); dwCharIndex = abData[dwLoop] >> 4 + dwRem; acPart[0] = m_acBaseMap[dwCharIndex]; dwCharIndex = (abData[dwLoop] & 0x0F) + dwRem; acPart[0] = m_acBaseMap[dwCharIndex]; sbOutput.Append(acPart,0,acPart.Length); }
return sbOutput.ToString(); }
public new static Byte[] Decode(String sData) { Int32 dwLoop = 0,dwLength = 0,dwRem = 0; Int32[] dwCharIndex = null; Byte[] abOutput = null; Char[] acInput = null;
if (sData == null || sData == String.Empty) return null;
acInput = sData.ToCharArray(); if (acInput == null) return null;
try { dwLength = acInput.Length / 2; abOutput = new Byte[dwLength]; dwCharIndex = new Int32[2]; } catch (Exception e) { Trace.WriteLine("CLsBase24.Decode: Initialize buffer failed! " + e.Message); } if (acInput == null) return null;
dwLength = 0; for (dwLoop = 0;dwLoop < acInput.Length;dwLoop += 2) { Array.Clear(dwCharIndex,0,dwCharIndex.Length); // TWO character can merage ONE byte Math.DivRem(dwLoop / 2,9,out dwRem); switch (acInput.Length - dwLoop) { case 1: dwCharIndex[0] = GetCharIndex(acInput[dwLoop]) - dwRem; abOutput[dwLength] = (Byte) (dwCharIndex[0] << 4); break;
default: dwCharIndex[0] = GetCharIndex(acInput[dwLoop]) - dwRem; dwCharIndex[1] = GetCharIndex(acInput[dwLoop + 1]) - dwRem; abOutput[dwLength] = (Byte) (dwCharIndex[0] << 4 + dwCharIndex[1]); break;
} dwLength++; }
return abOutput; }

|