javascript脚本压缩工具JSEncoder实现
1、算法原理:
从javascript脚本文件中提取单词,存入字典表中,这里使用|分割的字符串,然后将单词对应的序号(仿base64编码值)写入原来代码的地方,形成压缩后的js代码
2、压缩效果:
jquery-1.2.3.js原始文件大小95kb->[其他工具处理,去掉回车、注释等]jquery-1.2.3.min.js,大小53kb
=>本文工具压缩后:32kb
3、可选其他工具:
JSA 2.0 pre-alpha:http://sourceforge.net/project/showfiles.php?group_id=175776
packer:http://dean.edwards.name/packer/
4、下载(包含源代码在jar文件中)
2008.4 Ver:0.5 下载
5、源代码
java代码如下,写完代码之后才发现这是JSA(http://sourceforge.net/project/showfiles.php?group_id=175776)的压缩算法的再实现,不过好像作者没有开源,
本文算作是一种技术上的研究了。
通过对jquery-1.2.3.min.js http://code.google.com/p/jqueryjs/downloads/detail?name=jquery-1.2.3.min.js
进行压缩测试通过,压缩率>40%.
1
package com.cngd.jstool;
2
3
import java.io.FileReader;
4
import java.io.BufferedReader;
5
import java.io.FileOutputStream;
6
import java.io.IOException;
7
import java.util.regex.Matcher;
8
import java.util.regex.Pattern;
9
import java.util.Vector;
10
import java.text.DecimalFormat;
11
12
/**
13
* JSEncoder脚本压缩工具
14
* <p/>
15
* 写完代码之后才发现这是JSA(http://sourceforge.net/project/showfiles.php?group_id=175776)的压缩算法的再实现
16
* 针对jquery-1.2.3.min.js这个文件的压缩比率结果比较如下
17
* -------------------------------------------------------------------
18
* 原始大小 | JSEncoder | JSA-20071021(2.0 pre-alpha) | jquery packer算法
19
* -------------------------------------------------------------------
20
* 53kb | 32kb | 29kb | 29kb
21
* -------------------------------------------------------------------
22
* 因为JSA进一步将局部变量进行了压缩,因此相比较更小
23
* <p/>
24
* <p/>
25
* <p/>
26
* User: (在路上
http://www.cnblogs.com/midea0978)
27
* Date: 2008-4-18
28
* Version:0.5
29
*/
30
public class JSEncoder {
31
public static final String ENCODE_BASE64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789_$";
32
public boolean isDebug = false;
33
34
/**
35
* @param filename js filename
36
* @param offset offset>=0指定偏移变量,不同的offset可以实现代码表位置的变换,较小的offset可以获得更大的压缩率
37
* @return 压缩后的代码
38
*/
39
public String encode(String filename, int offset) throws Exception {
40
String jscript = readFileData(filename);
41
int size = jscript.length();
42
jscript = jscript.replaceAll("\n", " ");
43
//替换\->\\
44
jscript = jscript.replaceAll("\\\\", "\\\\\\\\");
45
//替换单引号\'=>\\'
46
jscript = jscript.replaceAll("\\\'", "\\\\\\\'");
47
48
Pattern p = Pattern.compile("([\\w\\$]+)");
49
Matcher m = p.matcher(jscript);
50
String element;
51
Vector<String> dict = new Vector<String>();
52
int index;
53
StringBuffer encscript = new StringBuffer();
54
StringBuffer dicttab = new StringBuffer();
55
56
debugInfo("=====编码字典对应表=====");
57
while (m.find()) {
58
element = m.group(1).trim();
59
if (!dict.contains(element)) {
60
dict.add(element);
61
index = dict.size() - 1;
62
} else {
63
index = dict.indexOf(element);
64
}
65
debugInfo(index + "==>" + element);
66
m.appendReplacement(encscript, Base64Encode(offset + index + 1));
67
}
68
for (String o : dict) dicttab.append(o + "|");
69
m.appendTail(encscript);
70
debugInfo("===== 编码字典结束 =====");
71
debugInfo("Offset=" + offset + ",字典大小=" + dict.size());
72
debugInfo("压缩后的代码:\n" + encscript.toString());
73
String dictstr = dicttab.substring(0, dicttab.length() - 1).toString();
74
debugInfo("字典字符串:\n" + dictstr);
75
String res = formatCode(encscript.toString(), dictstr, dict.size(), offset);
76
int packsize = res.length();
77
DecimalFormat df = new DecimalFormat("######.0");
78
System.out.println("\n原始文件大小:" + size + "\n压缩后文件大小:" + packsize);
79
System.out.println("=================\n压缩比率:" + df.format((size - packsize) * 100.0 / size) + "%");
80
return res;
81
}
82
83
private String readFileData(String filename) throws IOException {
84
BufferedReader in = new BufferedReader(new FileReader(filename));
85
StringBuffer sb = new StringBuffer();
86
while (in.ready()) {
87
sb.append(in.readLine() + "\n");
88
}
89
in.close();
90
return sb.toString();
91
}
92
93
private void debugInfo(String txt) {
94
if (isDebug) System.out.println(txt);
95
}
96
97
public static void main(String[] args) {
98
System.out.println("JSEncoder 0.5 by midea0978 2008.4");
99
System.out.println("=====================================");
100
System.out.println("http://www.cnblogs.com/midea0978\n");
101
if (args.length < 2) {
102
System.out.println("Usage:java JSEncoder.jar jsfile outputfile [offset].");
103
System.exit(0);
104
}
105
try {
106
System.out.println("输入文件: " + args[0]);
107
System.out.println("输出文件: " + args[1]);
108
JSEncoder util = new JSEncoder();
109
int offset = args.length >= 3 ? Integer.parseInt(args[2]) : 0;
110
String code = util.encode(args[0], offset);
111
FileOutputStream fs = new FileOutputStream(args[1]);
112
fs.write(code.getBytes());
113
fs.close();
114
} catch (Exception e) {
115
e.printStackTrace();
116
}
117
}
118
119
/**
120
* 仿Base64解码
121
*
122
* @param c 待编码的数字
123
* @return 编码值
124
*/
125
private String Base64Encode(int c) throws Exception {
126
String res;
127
if (c < 0) throw new Exception("Error:Offset必须>=0.");
128
if (c > 63)
129
res = Base64Encode(c >> 6) + Base64Encode(c & 63);
130
else {
131
//为了配合appendReplacement方法的使用,将$替换为\$
132
res = c == 63 ? "\\$" : String.valueOf(ENCODE_BASE64.charAt(c));
133
}
134
return res;
135
}
136
137
private String formatCode(String enc, String dict, int size, int offset) {
138
StringBuffer str = new StringBuffer();
139
str.append("/* Compressed by JSEncoder */\neval(function(E,I,A,D,J,K,L,H){function C(A){return A<62?String.fromCharCode(A+=A<26?65:A<52?71:-4):A<63?\'_\':A<64?\'$\':C(A>>6)+C(A&63)}while(A>0)K[C(D--)]=I[--A];function N(A){return K[A]==L[A]?A:K[A]}if(\'\'.replace(/^/,String)){var M=E.match(J),B=M[0],F=E.split(J),G=0;if(E.indexOf(F[0]))F=[\'\'].concat(F);do{H[A++]=F[G++];H[A++]=N(B)}while(B=M[G]);H[A++]=F[G]||\'\';return H.join(\'\')}return E.replace(J,N)}(");
140
str.append("\'" + enc + "\',");
141
str.append("\'" + dict + "\'.split(\'|\'),");
142
str.append(size + "," + (size + offset) + ",/[\\w\\$]+/g, {}, {}, []))");
143
return str.toString();
144
}
145
146
}
147

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26


27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147
