From fbfb124f11ead699adcc2c59b0aebe70147d931a Mon Sep 17 00:00:00 2001 From: Looly Date: Fri, 28 Feb 2020 14:22:16 +0800 Subject: [PATCH] fix escape bug --- CHANGELOG.md | 2 + .../core/text/replacer/ReplacerChain.java | 1 + .../core/text/replacer/StrReplacer.java | 13 ++-- .../java/cn/hutool/core/util/EscapeUtil.java | 73 +++++++++++++------ .../cn/hutool/core/util/EscapeUtilTest.java | 25 ++++++- 5 files changed, 86 insertions(+), 28 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 68eb83149..f86ddd411 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,8 +7,10 @@ ### 新特性 * 【poi 】 Excel合并单元格读取同一个值,不再为空 +* 【core 】 增加EscapeUtil.escapeAll(issue#758@Github) ### Bug修复 +* 【core 】 修复EscapeUtil.escape转义错误(issue#758@Github) ------------------------------------------------------------------------------------------------------------- diff --git a/hutool-core/src/main/java/cn/hutool/core/text/replacer/ReplacerChain.java b/hutool-core/src/main/java/cn/hutool/core/text/replacer/ReplacerChain.java index d80eb2a9c..9cac7ee26 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/replacer/ReplacerChain.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/replacer/ReplacerChain.java @@ -29,6 +29,7 @@ public class ReplacerChain extends StrReplacer implements Chain iterator() { return replacers.iterator(); diff --git a/hutool-core/src/main/java/cn/hutool/core/text/replacer/StrReplacer.java b/hutool-core/src/main/java/cn/hutool/core/text/replacer/StrReplacer.java index 2683a87d1..76ca06cfd 100644 --- a/hutool-core/src/main/java/cn/hutool/core/text/replacer/StrReplacer.java +++ b/hutool-core/src/main/java/cn/hutool/core/text/replacer/StrReplacer.java @@ -8,31 +8,32 @@ import cn.hutool.core.text.StrBuilder; /** * 抽象字符串替换类
* 通过实现replace方法实现局部替换逻辑 - * + * * @author looly * @since 4.1.5 */ -public abstract class StrReplacer implements Replacer, Serializable{ +public abstract class StrReplacer implements Replacer, 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++; diff --git a/hutool-core/src/main/java/cn/hutool/core/util/EscapeUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/EscapeUtil.java index 4187e70da..f822b7a7c 100644 --- a/hutool-core/src/main/java/cn/hutool/core/util/EscapeUtil.java +++ b/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
* escape采用ISO Latin字符集对指定的字符串进行编码。
* 所有的空格符、标点符号、特殊字符以及其他非ASCII字符都将被转化成%xx格式的字符编码(xx等于该字符在字符集表里面的编码的16进制数字)。 - * + * * @author xiaoleilu */ public class EscapeUtil { + /** + * 不转义的符号编码 + */ + private static final String NOT_ESCAPE_CHARS = "*@-_+./"; + private static final Filter 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)
- * 该方法不会对 ASCII 字母和数字进行编码,也不会对下面这些 ASCII 标点符号进行编码: * @ - _ + . / 。其他所有的字符都会被转义序列替换。 - * + * Escape编码(Unicode)(等同于JS的escape()方法)
+ * 该方法不会对 ASCII 字母和数字进行编码,也不会对下面这些 ASCII 标点符号进行编码: * @ - _ + . /
+ * 其他所有的字符都会被转义序列替换。 + * * @param content 被转义的内容 * @return 编码后的字符串 */ - public static String escape(String content) { - if (StrUtil.isBlank(content)) { - return content; + public static String escape(CharSequence content) { + return escape(content, JS_ESCAPE_FILTER); + } + + /** + * Escape编码(Unicode)
+ * 该方法不会对 ASCII 字母和数字进行编码。其他所有的字符都会被转义序列替换。 + * + * @param content 被转义的内容 + * @return 编码后的字符串 + */ + public static String escapeAll(CharSequence content) { + return escape(content, c -> true); + } + + /** + * Escape编码(Unicode)
+ * 该方法不会对 ASCII 字母和数字进行编码。其他所有的字符都会被转义序列替换。 + * + * @param content 被转义的内容 + * @param filter 编码过滤器,对于过滤器中accept为false的字符不做编码 + * @return 编码后的字符串 + */ + public static String escape(CharSequence content, Filter 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 解码后的字符串,如果解码失败返回原字符串 */ diff --git a/hutool-core/src/test/java/cn/hutool/core/util/EscapeUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/EscapeUtilTest.java index 72fb5b381..238667630 100644 --- a/hutool-core/src/test/java/cn/hutool/core/util/EscapeUtilTest.java +++ b/hutool-core/src/test/java/cn/hutool/core/util/EscapeUtilTest.java @@ -12,6 +12,29 @@ public class EscapeUtilTest { String result = EscapeUtil.unescapeHtml4("振荡器类型"); 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); } }