punycode编码

来源:互联网 发布:sql insert 换行符 编辑:程序博客网 时间:2024/06/14 00:53

枚举变量

base =36  tmin = 1 tmax = 26(阈值) skew = 38 damp = 70 initial_bias = 72 initial_n =128(0x80 ) delimiter= 0x2d(分隔符 -)

 

basic(cp)  判断是不是基本字符,即ASCII以内字符

 

delim(cp)  判断是不是 -”符号

 

decode_digitcp)将punycode 36进制数转换成整数

 

encode_digit(d,flag) 将字符转换成 0..25代表字面,26..35表示数字

 

maxint =-1

 

adapt (delta,numpoints,firsttime)   delta 是增量,numpoints是当前所有已编码的码位数量

 

可变长整数:

传统的可变长整数,如,100,2983等等等,但是,这些可变长整数一旦连接在一起,1002983,则不能知道两个整数的界限了,于是,要构造一个新的可变长整数格式:

每一位有一个阈值,t(j)

恰好只有一个权重最大的digit_j<t(j),从而可以区分各个数。

如,734251…..    对应的阈值为,2,3,5,5,5,5….

因为7>2,3=3,2<5,所以,2就是那个权重最大的digit,因此,734是一个数,类推,只,251是一个数。

 

这里,可以将734,251理解成punycode编码后的值,只需要将其解码:各位数,乘以各自权重求和,就可以得到对应的Unicode,从而得到对应的汉子。

 

权重公式如下:

 

W(0)  =  1

W(j)  = w( j-1 ) * (base  - t(j-1) ) j>0

 

阈值公式如下:

 

t(j)  =  base* ( j+1 ) – bias

 

如果计算出t(j) 小于tmin,则t(j) = tmin, 大于tmax t(j) = tmax

 

Bias 的值是根据贝叶斯调制而得:

 

1.  为了避免下步操作数据溢出,堆会按照比例缩小

第一次缩小,delta= delta/damp

第二次及以后,delta= delta/2

 

2.  堆的增加补偿了下个堆会被放入更长的字符串里:

Delta = delta + delta/numpoints

3.堆不断减小,直到落入界限值

for (k = 0; delta > ((base - tmin) * tmax)/ 2; k += base)

     {

           delta/= base - tmin;

     }

4.得到bias 的值

k + (base - tmin + 1) *delta / (delta + skew);

 

相反的过程,就是编码过程,编码过程,就是将Unicode差值,编码成可变长整数的过程,而这个可变长整数的取值范围是a~z,0~936进制。

 

编码步骤:

1.       基础码分离:先扫描全部输入,将ASCII部分,按照原来的次序排在前面的部分,插入‘-’用以分离

2.       把字按Unicode大小递增排序

3.       计算相邻Unicode码差值

4.       差值乘以初始码位置序号+排序后序号,得到增量

5.       对得到增量编码成base-36编码

6.       处理所有增量

 

 

如“中国互联网网络中心!”

 

变量初始值

n=initial_n= 128

delta = 0

bias =initial_bias = 72

h =out(out为输出字符串当前输出的位置,由于字符串中有!,所以,目前的h=out= 1)

 

对应的Unicode

20013  22269 20114 32852 32593 32593 32476 20013 24515 33

 

除去对‘!’处理,现在看对中文编码部分

 

 

参数m记录当前Unicode最小值,n代表上一个最小的Unicode值,因为‘ 国’的Unicode编码值大于‘中’字,所以,先对‘中’进行处理 (下面过程不考虑报错情况)

 

所以,当前

m=20013   n = 128

对应的delta = m-n*(h+1) = 39770

这个过程之后,n=m,为下一次计算差值做好准备。

Punycode所要编码的项目,则就是这个delta值,即,后一个字符与前一个字符的Unicode数值之差。

 

接下来是编码过程,编码过程是将39770由地位向高位一位一位的编码成一个可变长整数的过程。

 

先以十进制为例,如816这个数,要求出各位数值,则得从低位开始计算,816%10 = 6 则得出,低位的第一位是6,然后,816/10 =81, 81%10=1,则其次低位为1,再之,81/10=8, 8%10 = 8 ,则最高位为8

 

下面的代码则是针对base_36 做相同的操作

 

for (q =delta, k = base;; k += base)

                     {

                         if (out >= max_out)

                            return punycode_big_output;

              t = k <= bias /* + tmin */ ?tmin :         /* +tmin not needed */

                         k >= bias + tmax ? tmax : k - bias;

                         if (q < t)

                            break;

              output[out++] = encode_digit (t +(q - t) % (base - t), 0);

                         q = (q - t) / (base - t);

                   }

 

              output[out++] = encode_digit (q, case_flags && case_flags[j]);

                     bias = adapt (delta, h + 1, h == b);

 

首先计算低位的阈值,阈值的公式是

t =base*(j+1)-bias

而如果t大于tmax,则t=tmax,反之,t= tmin

j表示当前的位数,从0增长

bias,初始值为initial_bias = 72

 

第一次,t + (q - t) % (base - t) 可以得出最低位 10 经过编码成base_36 对应字符为 k

只要q>t ,则表明,仍然是 39770这个数在编码

继而得到,其他位为 16 32  0

对应编码为 q  6   ,a

所以,第一个字的编码 kq6a

 

以此类图,可以得到全部的编码信息。

 

可以验证,对于 39770这个数

 

权值

因为

W(0)  = 1

W(j)  = wj-1*(base – t (j-1) )

 

w0 =1

w 1= w (0) * (36 - 1) = 35

w  (2)  =w(1)*36-1 =352

w(3)  = w (2) * (36 - 1)  = 353

 

而,39770 = 0 * 353 + 32*352+ 16*35 +10 * 1

原创粉丝点击