Browse Source

fix escape bug

Looly 5 years ago
parent
commit
fbfb124f11

+ 2 - 0
CHANGELOG.md

@@ -7,8 +7,10 @@
 
 ### 新特性
 * 【poi  】     Excel合并单元格读取同一个值,不再为空
+* 【core 】     增加EscapeUtil.escapeAll(issue#758@Github)
 
 ### Bug修复
+* 【core 】     修复EscapeUtil.escape转义错误(issue#758@Github)
 
 -------------------------------------------------------------------------------------------------------------
 

+ 1 - 0
hutool-core/src/main/java/cn/hutool/core/text/replacer/ReplacerChain.java

@@ -29,6 +29,7 @@ public class ReplacerChain extends StrReplacer implements Chain<StrReplacer, Rep
 		}
 	}
 
+	@SuppressWarnings("NullableProblems")
 	@Override
 	public Iterator<StrReplacer> iterator() {
 		return replacers.iterator();

+ 7 - 6
hutool-core/src/main/java/cn/hutool/core/text/replacer/StrReplacer.java

@@ -8,31 +8,32 @@ import cn.hutool.core.text.StrBuilder;
 /**
  * 抽象字符串替换类<br>
  * 通过实现replace方法实现局部替换逻辑
- * 
+ *
  * @author looly
  * @since 4.1.5
  */
-public abstract class StrReplacer implements Replacer<CharSequence>, Serializable{
+public abstract class StrReplacer implements Replacer<CharSequence>, Serializable {
 	private static final long serialVersionUID = 1L;
-	
+
 	/**
 	 * 抽象的字符串替换方法,通过传入原字符串和当前位置,执行替换逻辑,返回处理或替换的字符串长度部分。
+	 *
 	 * @param str 被处理的字符串
 	 * @param pos 当前位置
 	 * @param out 输出
 	 * @return 处理的原字符串长度,0表示跳过此字符
 	 */
 	protected abstract int replace(CharSequence str, int pos, StrBuilder out);
-	
+
 	@Override
 	public CharSequence replace(CharSequence t) {
 		final int len = t.length();
 		final StrBuilder strBuillder = StrBuilder.create(len);
 		int pos = 0;//当前位置
 		int consumed;//处理过的字符数
-		while(pos < len) {
+		while (pos < len) {
 			consumed = replace(t, pos, strBuillder);
-			if(0 == consumed) {
+			if (0 == consumed) {
 				//0表示未处理或替换任何字符,原样输出本字符并从下一个字符继续
 				strBuillder.append(t.charAt(pos));
 				pos++;

+ 51 - 20
hutool-core/src/main/java/cn/hutool/core/util/EscapeUtil.java

@@ -1,5 +1,6 @@
 package cn.hutool.core.util;
 
+import cn.hutool.core.lang.Filter;
 import cn.hutool.core.text.escape.Html4Escape;
 import cn.hutool.core.text.escape.Html4Unescape;
 
@@ -7,57 +8,87 @@ import cn.hutool.core.text.escape.Html4Unescape;
  * 转义和反转义工具类Escape / Unescape<br>
  * escape采用ISO Latin字符集对指定的字符串进行编码。<br>
  * 所有的空格符、标点符号、特殊字符以及其他非ASCII字符都将被转化成%xx格式的字符编码(xx等于该字符在字符集表里面的编码的16进制数字)。
- * 
+ *
  * @author xiaoleilu
  */
 public class EscapeUtil {
 
 	/**
+	 * 不转义的符号编码
+	 */
+	private static final String NOT_ESCAPE_CHARS = "*@-_+./";
+	private static final Filter<Character> JS_ESCAPE_FILTER = c -> false == (
+			Character.isDigit(c)
+					|| Character.isLowerCase(c)
+					|| Character.isUpperCase(c)
+					|| StrUtil.contains(NOT_ESCAPE_CHARS, c)
+	);
+
+	/**
 	 * 转义HTML4中的特殊字符
-	 * 
+	 *
 	 * @param html HTML文本
 	 * @return 转义后的文本
 	 * @since 4.1.5
 	 */
-	public static String escapeHtml4(String html) {
+	public static String escapeHtml4(CharSequence html) {
 		Html4Escape escape = new Html4Escape();
 		return escape.replace(html).toString();
 	}
 
 	/**
 	 * 反转义HTML4中的特殊字符
-	 * 
+	 *
 	 * @param html HTML文本
 	 * @return 转义后的文本
 	 * @since 4.1.5
 	 */
-	public static String unescapeHtml4(String html) {
+	public static String unescapeHtml4(CharSequence html) {
 		Html4Unescape unescape = new Html4Unescape();
 		return unescape.replace(html).toString();
 	}
 
 	/**
+	 * Escape编码(Unicode)(等同于JS的escape()方法)<br>
+	 * 该方法不会对 ASCII 字母和数字进行编码,也不会对下面这些 ASCII 标点符号进行编码: * @ - _ + . / <br>
+	 * 其他所有的字符都会被转义序列替换。
+	 *
+	 * @param content 被转义的内容
+	 * @return 编码后的字符串
+	 */
+	public static String escape(CharSequence content) {
+		return escape(content, JS_ESCAPE_FILTER);
+	}
+
+	/**
 	 * Escape编码(Unicode)<br>
-	 * 该方法不会对 ASCII 字母和数字进行编码,也不会对下面这些 ASCII 标点符号进行编码: * @ - _ + . / 。其他所有的字符都会被转义序列替换。
-	 * 
+	 * 该方法不会对 ASCII 字母和数字进行编码。其他所有的字符都会被转义序列替换。
+	 *
 	 * @param content 被转义的内容
 	 * @return 编码后的字符串
 	 */
-	public static String escape(String content) {
-		if (StrUtil.isBlank(content)) {
-			return content;
+	public static String escapeAll(CharSequence content) {
+		return escape(content, c -> true);
+	}
+
+	/**
+	 * Escape编码(Unicode)<br>
+	 * 该方法不会对 ASCII 字母和数字进行编码。其他所有的字符都会被转义序列替换。
+	 *
+	 * @param content 被转义的内容
+	 * @param filter  编码过滤器,对于过滤器中accept为false的字符不做编码
+	 * @return 编码后的字符串
+	 */
+	public static String escape(CharSequence content, Filter<Character> filter) {
+		if (StrUtil.isEmpty(content)) {
+			return StrUtil.str(content);
 		}
 
-		int i;
+		final StringBuilder tmp = new StringBuilder(content.length() * 6);
 		char j;
-		StringBuilder tmp = new StringBuilder();
-		tmp.ensureCapacity(content.length() * 6);
-
-		for (i = 0; i < content.length(); i++) {
-
+		for (int i = 0; i < content.length(); i++) {
 			j = content.charAt(i);
-
-			if (Character.isDigit(j) || Character.isLowerCase(j) || Character.isUpperCase(j)) {
+			if (false == filter.accept(j)) {
 				tmp.append(j);
 			} else if (j < 256) {
 				tmp.append("%");
@@ -75,7 +106,7 @@ public class EscapeUtil {
 
 	/**
 	 * Escape解码
-	 * 
+	 *
 	 * @param content 被转义的内容
 	 * @return 解码后的字符串
 	 */
@@ -115,7 +146,7 @@ public class EscapeUtil {
 
 	/**
 	 * 安全的unescape文本,当文本不是被escape的时候,返回原文。
-	 * 
+	 *
 	 * @param content 内容
 	 * @return 解码后的字符串,如果解码失败返回原字符串
 	 */

+ 24 - 1
hutool-core/src/test/java/cn/hutool/core/util/EscapeUtilTest.java

@@ -12,6 +12,29 @@ public class EscapeUtilTest {
 		
 		String result = EscapeUtil.unescapeHtml4("&#25391;&#33633;&#22120;&#31867;&#22411;");
 		Assert.assertEquals("振荡器类型", result);
-		
+
+		String escape = EscapeUtil.escapeHtml4("*@-_+./(123你好)");
+		Assert.assertEquals("*@-_+./(123你好)", escape);
+	}
+
+	@Test
+	public void escapeTest(){
+		String str = "*@-_+./(123你好)ABCabc";
+		String escape = EscapeUtil.escape(str);
+		Assert.assertEquals("*@-_+./%28123%u4f60%u597d%29ABCabc", escape);
+
+		String unescape = EscapeUtil.unescape(escape);
+		Assert.assertEquals(str, unescape);
+	}
+
+	@Test
+	public void escapeAllTest(){
+		String str = "*@-_+./(123你好)ABCabc";
+
+		String escape = EscapeUtil.escapeAll(str);
+		Assert.assertEquals("%2a%40%2d%5f%2b%2e%2f%28%31%32%33%u4f60%u597d%29%41%42%43%61%62%63", escape);
+
+		String unescape = EscapeUtil.unescape(escape);
+		Assert.assertEquals(str, unescape);
 	}
 }