论坛首页 Java企业应用论坛

淘宝网的搜索关键字的编码与解码

浏览 77101 次
该帖已经被评为良好帖
作者 正文
   发表时间:2008-12-03   最后修改:2010-02-03
lichd 写道
刚刚看到你对淘宝编码的研究,我个人认为淘宝那个编码可能是Base32规范的演化版,只不过淘宝使用0来代替=做末位填充,RFC 3548 (The Base16, Base32, and Base64 Data Encodings)
对这些都有规范

经过证实,lichd 的观点正确,由于我在本文发表之前,除Base64 之外,其他的BaseXX 都没接触过,所以不知道这个正是Base32 的效果。

 

不过看官们请放心,本文的思路也和Base32 符合。当时的乱打乱撞,结果造了一个古人已经造好的轮子,实在惭愧。

 

因此,感谢lichd 的观点,从而稀释了我体内SB细胞的浓度。

 

回头看了一下本文的一个旧实现(codec.zip ),一年前写得代码太糟糕了,不推荐。

现在提供一个相对不错的实现,>>猛击获取<<

 

-------------------------- -------------------------- -------------------- ---------------------

------------------------------------------分割线 --------------------------------------------

-------------------------- -------------------------- -------------------- ---------------------

 

 

很久之前发现淘宝网的搜索关键字在编码后有点可爱,小写英文字母+数字的组合。

想去研究一下,但是后面好像没了回事似的。。记性不好

然后这两天不知道为什么,突然间抽起条脑筋,跑去研究。。。。

 

--------------------------开始分割线--------------------------

 

在淘宝网上用关键字“gembler”搜索一下商品,得出以下URL:

 

http://search1.taobao.com/browse/0/n-0-----------------g,m5sw2ytmmvza----------------40--commend-0-all-0.htm?at_topsearch=1

                                                                                           (留意上面这里:“m5sw2ytmmvza”

 

经过一轮 天昏地暗、沙尘滚滚 的分析、研究之后,得出以下结论:

 

  在Base64中,码表是由 [A-Z,a-z,0-9,+,/,=(pad)] 组成的。
  然后自己也弄个码表,由 [a-z,2-7(这个2-7是在
淘宝网上搜索了n次而得出的结论 ] 组成的:

 

a b c d e f g h i j k l m n o p q r
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17

 

-------------------------------愚蠢分割线---------------------------------

 

s t u v w x y z 2 3 4 5 6 7
18 19 20 21 22 23 24 25 26 27 28 29 30 31

 
  在Base64中,是将二进制连成一串,然后再按6位来分割,分割完后在前面补0,这个地球人都知道,不多说了。
  而我呢,哈哈,捣蛋一下,按5位来分割,如果刚好够分,那就爽了,

  但是,世事往往不如人意,位数不够,那咋办呢?看下面 :)
 
  在Base64中,是用"="来解决的吧。
  现在呢,就是在前面补0,然后在后面再补0,其实就是前后补0,

  但是, 按5位来分 ,前面3个0是补定的了,后面的0就看上帝怎么安排了。
 
  举个小例子:字符串 "aaa",(编码/加密)后是 "mfqwc"
 

二进制: 01100001 01100001 01100001

转换后: (000 )01100 (000 )00101 (000 )10000 (000 )10110 (000 )0001(0 )
十进制: 12 5 16 22 2
码表对应: m f q w c

 
  反过来,(解码/解密):
 

码表对应: m f q w c
十进制: 12 5 16 22 2
二进制: 00001100 00000101 00010000 00010110 00000010
去掉前3个0后: 01100 00101 10000 10110 00010
合并后: 0110000101100001011000010

 
  然后把合并后的串的长度除一下8,发现多了个0:
 

合并后的 二进制码: 01100001 01100001 01100001 0


  多了就算了,不要了(其实是在{编码/加密}的分割时候,在分剩的余数的后面补的0)。
  然后再将 byte[] 转回字符串

 

  OK!又见回"aaa"了。    :)
 
  *有一点值得注意的是:UTF-8、GBK、GB18030 一般都没什么问题,但是 GB2312 可能字符集不够丰富,繁体字在decode的时候成问号了。

 

搞了半天,“可视化编辑器”的 Java Code不好使,骨干代码在回帖里贴出................(完整的代码,有兴趣的同志们请见附件!

 

--------------------------结束分割线--------------------------

 

。。。整理好了,整个人都铺满了灰尘.......

 

PS:不知道淘宝网的思路是不是这样

 

(补:另外一篇讨论 - “ 拍拍网的搜索关键字的编码与解码 ”)

   发表时间:2008-12-03   最后修改:2009-03-28
贴上部分代码片段

编码:

	/**
	 * (编码/加密)字节数组
	 * 
	 * @author gembler
	 * @version 2008-12-3 下午03:14:43
	 * 
	 * @param keyBytes
	 *            需要(编码/加密)的字节数组
	 * 
	 * @return (编码/加密)后的字符串
	 */
	private static String encode(byte[] keyBytes) {

		if (keyBytes == null || keyBytes.length < 1) {

			return "";

		}

		/*
		 * 合并二进制码, 
		 * 如: 
		 *     00101010 11010011 00101101 10100011 
		 *   to
		 *     00101010110100110010110110100011
		 */

		StringBuilder mergrd = new StringBuilder();

		for (int i = 0; i < keyBytes.length; i++) {

			FormatUtil.formatBinary(keyBytes[i], mergrd);

		}

		/*
		 * 以5个bit为单位,计算能分多少组,
		 * 如:
      *     00101010110100110010110110100011
		 *   to
		 *     00101 01011 01001 10010 11011 01000 11
		 *                                          |
		 *                                   (这个11为余下的位)
		 */

		int groupCount = mergrd.length() / FIVE_BIT;

		// 计算余下的位数
		int lastCount = mergrd.length() % FIVE_BIT;

		// 类似数据分页的算法,有余数的情况下需要加 1。
		if (lastCount > 0) {

			groupCount += 1;

		}

		/*
		 * (编码/加密)
		 */

		StringBuilder sbEncoded = new StringBuilder();

		// 循环所需的条件
		int forMax = groupCount * FIVE_BIT;

		// 每次递增5位来截取
		for (int i = 0; i < forMax; i += FIVE_BIT) {

			// 结束点
			int end = i + FIVE_BIT;

			/*
			 * 如果结束点比已合并的二进制码串的长度要大,
			 * 相当于有余数,
          * 并且表示当前循环到了(已合并的二进制码串的长度 % FIVE_BIT)的那一截。
			 */

			// 标记是否到了余数的那一截
			boolean flag = false;

			if (end > mergrd.length()) {

				/*
				 * 如果结束点比已合并的二进制码串的长度要大,
				 * 结束点需要被重设为: 
				 * 已合并的二进制码串的长度,等价于(i + lastCount). 并且重设标记。
				 */

				end = (i + lastCount);

				flag = true;

			}

			// 截取
			String strFiveBit = mergrd.substring(i, end);

			// 截取后从二进制转为十进制
			int intFiveBit = Integer.parseInt(strFiveBit, BINARY);

			if (flag) {

				/*
				 * 如果结束点比已合并的二进制码串的长度要大,
				 * 或者是到了余数的那一截: 
				 * 需要左移操作,假设余下的二进制位为:11,
				 * 那么需要从后面补0,左移操作后为 (000)11(000)
				 */

				intFiveBit <<= (FIVE_BIT - lastCount);

			}

			// 利用该十进制数作为码表的索引获取对应的字符,并追加到sbEncoded
			sbEncoded.append(CODEC_TABLE.charAt(intFiveBit));

		}

		return sbEncoded.toString();

	}


解码:

	/**
	 * (解码/解密)字符串
	 * 
	 * @author gembler
	 * @version 2008-12-3 下午03:15:00
	 * 
	 * @param code
	 *            需要(解码/解密)的字符串
	 * @param characterSet
	 *            字符集
	 * 
	 * @return (解码/解密)后的字符串
	 */
	public static String decode(String code, String characterSet) {

		if (code == null || code.length() < 1) {

			return "";

		}

		/*
		 * 拆除每一个字符,从码表里获取相应的索引。
		 */

		StringBuilder sbBinarys = new StringBuilder();

		for (int i = 0; i < code.length(); i++) {

			// 从码表里获取相应的索引
			int index = getCodecTableIndex(code.charAt(i));

			// 将十进制的索引转换为二进制串
			String indexBinary = Integer.toBinaryString(index);

			// 去掉前3个0,并且追加到sbBinarys
			FormatUtil.formatBinary(indexBinary, sbBinarys, FIVE_BIT);

		}

		/*
		 * 按8个bit拆分,剩下的余数扔掉。
		 * 扔掉的余数是在(编码/加密)的分割时候,在分剩的余数的后面补的0
		 */

		byte[] binarys = new byte[sbBinarys.length() / EIGHT_BIT];

		for (int i = 0, j = 0; i < binarys.length; i++) {

			// 每8个bit截取一份
			String sub = sbBinarys.substring(j, j += EIGHT_BIT);

			// 将截取下来的二进制串转换为十进制
			Integer intBinary = Integer.valueOf(sub, BINARY);

			binarys[i] = intBinary.byteValue();

		}

		String decoded = null;

		if (characterSet == null || characterSet.length() < 1) {

			// 采用默认语言环境的 character set。
			decoded = new String(binarys);

		} else {

			try {

				// 采用指定的 character set。
				decoded = new String(binarys, characterSet);

			} catch (UnsupportedEncodingException e) {

				decoded = new String(binarys);

			}
		}

		return decoded;

	}


测试:

			BufferedReader br = new BufferedReader(new InputStreamReader(
					System.in));

			while (true) {

				System.out.print("输入字符号串:");

				String in = br.readLine();

				if ("exit".equalsIgnoreCase(in)) {
					break;
				}

				String enCode = Codec.encode(in);

				String deCode = Codec.decode(enCode);
				System.out.println();
				System.out.println("original: " + in);
				System.out.println("encode: " + enCode);
				System.out.println("decode: " + deCode);
				System.out.println();
			}


测试结果:

      输入:JavaEye论坛频道
      输出:

        original: JavaEye论坛频道
        encode: jjqxmykfpfs4fw6mwpdllnoa
        decode: JavaEye论坛频道


      然后跑去淘宝网敲入"JavaEye论坛频道"搜索。


      得出:


      http://search1.taobao.com/browse/0/n-g,jjqxmykfpfs4fw6mwpdllnoa----------------40--commend-0-all-0.htm?at_topsearch=1

      比较一下:

        jjqxmykfpfs4fw6mwpdllnoa - console输出
        jjqxmykfpfs4fw6mwpdllnoa - 淘宝网URL
0 请登录后投票
   发表时间:2008-12-03  
以前也对这个问题有点兴趣,不过没细想过。
0 请登录后投票
   发表时间:2008-12-03  
真是够强悍的!这都能分析出来!

少见多怪的问一句:淘宝为什么要对url进行Base64编码啊?
0 请登录后投票
   发表时间:2008-12-03  
netfork 写道

少见多怪的问一句:淘宝为什么要对url进行Base64编码啊?


哈···其实我也不知道。。。

不过经过Base64后,可能会带有 '/'
0 请登录后投票
   发表时间:2008-12-03  
gembler 写道
netfork 写道

少见多怪的问一句:淘宝为什么要对url进行Base64编码啊?


哈···其实我也不知道。。。

不过经过Base64后,可能会带有 '/'

嘿嘿,就因为这个?
0 请登录后投票
   发表时间:2008-12-03   最后修改:2008-12-03
netfork 写道
gembler 写道
netfork 写道

少见多怪的问一句:淘宝为什么要对url进行Base64编码啊?


哈···其实我也不知道。。。

不过经过Base64后,可能会带有 '/'

嘿嘿,就因为这个?


哈·这还是请淘宝网的研发人员回答吧···我纯粹兴趣研究

不过 '/' 这东西拼上URL就有点点问题,如果处理不当的话。
0 请登录后投票
   发表时间:2008-12-03  
gembler 写道
netfork 写道
gembler 写道
netfork 写道

少见多怪的问一句:淘宝为什么要对url进行Base64编码啊?


哈···其实我也不知道。。。

不过经过Base64后,可能会带有 '/'

嘿嘿,就因为这个?


哈·这还是请淘宝网的研发人员回答吧···我纯粹兴趣研究

不过 '/' 这东西拼上URL就有点点问题,如果处理不当的话。

使用URLEncoder转一下不成吗?
0 请登录后投票
   发表时间:2008-12-03  
netfork 写道
gembler 写道
netfork 写道
gembler 写道
netfork 写道

少见多怪的问一句:淘宝为什么要对url进行Base64编码啊?


哈···其实我也不知道。。。

不过经过Base64后,可能会带有 '/'

嘿嘿,就因为这个?


哈·这还是请淘宝网的研发人员回答吧···我纯粹兴趣研究

不过 '/' 这东西拼上URL就有点点问题,如果处理不当的话。

使用URLEncoder转一下不成吗?


如果关键字多一点,转出来就又长又臭了,不好看
0 请登录后投票
   发表时间:2008-12-03  
gembler 写道
netfork 写道
gembler 写道
netfork 写道
gembler 写道
netfork 写道

少见多怪的问一句:淘宝为什么要对url进行Base64编码啊?


哈···其实我也不知道。。。

不过经过Base64后,可能会带有 '/'

嘿嘿,就因为这个?


哈·这还是请淘宝网的研发人员回答吧···我纯粹兴趣研究

不过 '/' 这东西拼上URL就有点点问题,如果处理不当的话。

使用URLEncoder转一下不成吗?


如果关键字多一点,转出来就又长又臭了,不好看

有道理!还是Base64转出来的看着顺眼些。
0 请登录后投票
论坛首页 Java企业应用版

跳转论坛:
Global site tag (gtag.js) - Google Analytics