fix escape bug

This commit is contained in:
Looly 2020-02-28 14:22:16 +08:00
parent 83d6428db8
commit fbfb124f11
5 changed files with 86 additions and 28 deletions

View File

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

View File

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

View File

@ -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++;

View File

@ -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<br>
* 该方法不会对 ASCII 字母和数字进行编码也不会对下面这些 ASCII 标点符号进行编码 * @ - _ + . / 其他所有的字符都会被转义序列替换
*
* Escape编码Unicode等同于JS的escape()方法<br>
* 该方法不会对 ASCII 字母和数字进行编码也不会对下面这些 ASCII 标点符号进行编码 * @ - _ + . / <br>
* 其他所有的字符都会被转义序列替换
*
* @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<br>
* 该方法不会对 ASCII 字母和数字进行编码其他所有的字符都会被转义序列替换
*
* @param content 被转义的内容
* @return 编码后的字符串
*/
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 解码后的字符串如果解码失败返回原字符串
*/

View File

@ -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);
}
}