This commit is contained in:
Looly 2024-03-07 16:03:57 +08:00
parent 27d67c4d84
commit 197e89e692
6 changed files with 75 additions and 25 deletions

View File

@ -2908,7 +2908,7 @@ public class CharSequenceUtil extends StrValidator {
if (INDEX_NOT_FOUND == startInclude) { if (INDEX_NOT_FOUND == startInclude) {
return str(str); return str(str);
} }
return replace(str, startInclude, startInclude + searchStr.length(), replacedStr); return replaceByCodePoint(str, startInclude, startInclude + searchStr.length(), replacedStr);
} }
/** /**
@ -3002,8 +3002,8 @@ public class CharSequenceUtil extends StrValidator {
* @return 替换后的字符串 * @return 替换后的字符串
* @since 3.2.1 * @since 3.2.1
*/ */
public static String replace(final CharSequence str, final int beginInclude, final int endExclude, final char replacedChar) { public static String replaceByCodePoint(final CharSequence str, final int beginInclude, final int endExclude, final char replacedChar) {
return new RangeReplacerByChar(beginInclude, endExclude, replacedChar).apply(str); return new RangeReplacerByChar(beginInclude, endExclude, replacedChar, true).apply(str);
} }
/** /**
@ -3017,8 +3017,8 @@ public class CharSequenceUtil extends StrValidator {
* @return 替换后的字符串 * @return 替换后的字符串
* @since 3.2.1 * @since 3.2.1
*/ */
public static String replace(final CharSequence str, final int beginInclude, final int endExclude, final CharSequence replacedStr) { public static String replaceByCodePoint(final CharSequence str, final int beginInclude, final int endExclude, final CharSequence replacedStr) {
return new RangeReplacerByStr(beginInclude, endExclude, replacedStr).apply(str); return new RangeReplacerByStr(beginInclude, endExclude, replacedStr, true).apply(str);
} }
/** /**
@ -3075,7 +3075,7 @@ public class CharSequenceUtil extends StrValidator {
* @since 4.1.14 * @since 4.1.14
*/ */
public static String hide(final CharSequence str, final int startInclude, final int endExclude) { public static String hide(final CharSequence str, final int startInclude, final int endExclude) {
return replace(str, startInclude, endExclude, '*'); return replaceByCodePoint(str, startInclude, endExclude, '*');
} }
/** /**

View File

@ -26,18 +26,21 @@ public class RangeReplacerByChar extends StrReplacer {
private final int beginInclude; private final int beginInclude;
private final int endExclude; private final int endExclude;
private final char replacedChar; private final char replacedChar;
private final boolean isCodePoint;
/** /**
* 构造 * 构造
* *
* @param beginInclude 开始位置包含 * @param beginInclude 开始位置包含
* @param endExclude 结束位置不包含 * @param endExclude 结束位置不包含
* @param replacedChar 被替换的字符串 * @param replacedChar 被替换的字符串
* @param isCodePoint 是否code point模式此模式下emoji等会被作为单独的字符
*/ */
public RangeReplacerByChar(final int beginInclude, final int endExclude, final char replacedChar) { public RangeReplacerByChar(final int beginInclude, final int endExclude, final char replacedChar, final boolean isCodePoint) {
this.beginInclude = beginInclude; this.beginInclude = beginInclude;
this.endExclude = endExclude; this.endExclude = endExclude;
this.replacedChar = replacedChar; this.replacedChar = replacedChar;
this.isCodePoint = isCodePoint;
} }
@Override @Override
@ -47,8 +50,8 @@ public class RangeReplacerByChar extends StrReplacer {
} }
final String originalStr = StrUtil.str(str); final String originalStr = StrUtil.str(str);
final int[] strCodePoints = originalStr.codePoints().toArray(); final int[] chars = (isCodePoint ? originalStr.codePoints() : originalStr.chars()).toArray();
final int strLength = strCodePoints.length; final int strLength = chars.length;
final int beginInclude = this.beginInclude; final int beginInclude = this.beginInclude;
if (beginInclude > strLength) { if (beginInclude > strLength) {
@ -71,7 +74,7 @@ public class RangeReplacerByChar extends StrReplacer {
replace(originalStr, i, stringBuilder); replace(originalStr, i, stringBuilder);
} else { } else {
// 其它字符保留 // 其它字符保留
stringBuilder.appendCodePoint(strCodePoints[i]); append(stringBuilder, chars[i]);
} }
} }
return stringBuilder.toString(); return stringBuilder.toString();
@ -82,4 +85,18 @@ public class RangeReplacerByChar extends StrReplacer {
out.appendCodePoint(replacedChar); out.appendCodePoint(replacedChar);
return pos; return pos;
} }
/**
* 追加字符
*
* @param stringBuilder {@link StringBuilder}
* @param c 字符
*/
private void append(final StringBuilder stringBuilder, final int c) {
if (isCodePoint) {
stringBuilder.appendCodePoint(c);
} else {
stringBuilder.append((char) c);
}
}
} }

View File

@ -26,6 +26,7 @@ public class RangeReplacerByStr extends StrReplacer {
private final int beginInclude; private final int beginInclude;
private final int endExclude; private final int endExclude;
private final CharSequence replacedStr; private final CharSequence replacedStr;
private final boolean isCodePoint;
/** /**
* 构造 * 构造
@ -33,11 +34,13 @@ public class RangeReplacerByStr extends StrReplacer {
* @param beginInclude 开始位置包含 * @param beginInclude 开始位置包含
* @param endExclude 结束位置不包含 * @param endExclude 结束位置不包含
* @param replacedStr 被替换的字符串 * @param replacedStr 被替换的字符串
* @param isCodePoint 是否code point模式此模式下emoji等会被作为单独的字符
*/ */
public RangeReplacerByStr(final int beginInclude, final int endExclude, final CharSequence replacedStr) { public RangeReplacerByStr(final int beginInclude, final int endExclude, final CharSequence replacedStr, final boolean isCodePoint) {
this.beginInclude = beginInclude; this.beginInclude = beginInclude;
this.endExclude = endExclude; this.endExclude = endExclude;
this.replacedStr = replacedStr; this.replacedStr = replacedStr;
this.isCodePoint = isCodePoint;
} }
@Override @Override
@ -47,8 +50,8 @@ public class RangeReplacerByStr extends StrReplacer {
} }
final String originalStr = StrUtil.str(str); final String originalStr = StrUtil.str(str);
final int[] strCodePoints = originalStr.codePoints().toArray(); final int[] chars = (isCodePoint ? originalStr.codePoints() : originalStr.chars()).toArray();
final int strLength = strCodePoints.length; final int strLength = chars.length;
final int beginInclude = this.beginInclude; final int beginInclude = this.beginInclude;
if (beginInclude > strLength) { if (beginInclude > strLength) {
@ -66,11 +69,11 @@ public class RangeReplacerByStr extends StrReplacer {
// 新字符串长度 <= 旧长度 - (被替换区间codePoints数量) + 替换字符串长度 // 新字符串长度 <= 旧长度 - (被替换区间codePoints数量) + 替换字符串长度
final StringBuilder stringBuilder = new StringBuilder(originalStr.length() - (endExclude - beginInclude) + replacedStr.length()); final StringBuilder stringBuilder = new StringBuilder(originalStr.length() - (endExclude - beginInclude) + replacedStr.length());
for (int i = 0; i < beginInclude; i++) { for (int i = 0; i < beginInclude; i++) {
stringBuilder.appendCodePoint(strCodePoints[i]); append(stringBuilder, chars[i]);
} }
replace(originalStr, beginInclude, stringBuilder); replace(originalStr, beginInclude, stringBuilder);
for (int i = endExclude; i < strLength; i++) { for (int i = endExclude; i < strLength; i++) {
stringBuilder.appendCodePoint(strCodePoints[i]); append(stringBuilder, chars[i]);
} }
return stringBuilder.toString(); return stringBuilder.toString();
} }
@ -83,4 +86,18 @@ public class RangeReplacerByStr extends StrReplacer {
// 无意义的返回 // 无意义的返回
return endExclude; return endExclude;
} }
/**
* 追加字符
*
* @param stringBuilder {@link StringBuilder}
* @param c 字符
*/
private void append(final StringBuilder stringBuilder, final int c) {
if (isCodePoint) {
stringBuilder.appendCodePoint(c);
} else {
stringBuilder.append((char) c);
}
}
} }

View File

@ -40,10 +40,10 @@ public class CharSequenceUtilTest {
@Test @Test
public void replaceByStrTest() { public void replaceByStrTest() {
final String replace = "SSM15930297701BeryAllen"; final String replace = "SSM15930297701BeryAllen";
final String result = CharSequenceUtil.replace(replace, 5, 12, "***"); final String result = CharSequenceUtil.replaceByCodePoint(replace, 5, 12, "***");
Assertions.assertEquals("SSM15***01BeryAllen", result); Assertions.assertEquals("SSM15***01BeryAllen", result);
final String emoji = StrUtil.replace("\uD83D\uDE00aabb\uD83D\uDE00ccdd", 2, 6, "***"); final String emoji = StrUtil.replaceByCodePoint("\uD83D\uDE00aabb\uD83D\uDE00ccdd", 2, 6, "***");
Assertions.assertEquals("\uD83D\uDE00a***ccdd", emoji); Assertions.assertEquals("\uD83D\uDE00a***ccdd", emoji);
} }

View File

@ -0,0 +1,16 @@
package org.dromara.hutool.core.text;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;
public class IssueI96LWHTest {
@Test
public void replaceByCodePointTest() {
final String str = "\uD83D\uDC46最上方点击蓝字";
// 这个方法里\uD83D\uDC46表示一个emoji表情使用codePoint之后一个表情表示一个字符因此按照一个字符对
Assertions.assertEquals("\uD83D\uDC46最上下点击蓝字", StrUtil.replaceByCodePoint(str, 3, 4, ""));
Assertions.assertEquals("\uD83D\uDC46最下方点击蓝字", new StringBuilder(str).replace(3, 4, "").toString());
}
}

View File

@ -181,12 +181,12 @@ public class StrUtilTest {
@Test @Test
public void replaceTest() { public void replaceTest() {
String string = StrUtil.replace("aabbccdd", 2, 6, '*'); String string = StrUtil.replaceByCodePoint("aabbccdd", 2, 6, '*');
Assertions.assertEquals("aa****dd", string); Assertions.assertEquals("aa****dd", string);
string = StrUtil.replace("aabbccdd", 2, 12, '*'); string = StrUtil.replaceByCodePoint("aabbccdd", 2, 12, '*');
Assertions.assertEquals("aa******", string); Assertions.assertEquals("aa******", string);
final String emoji = StrUtil.replace("\uD83D\uDE00aabb\uD83D\uDE00ccdd", 2, 6, '*'); final String emoji = StrUtil.replaceByCodePoint("\uD83D\uDE00aabb\uD83D\uDE00ccdd", 2, 6, '*');
Assertions.assertEquals("\uD83D\uDE00a****ccdd", emoji); Assertions.assertEquals("\uD83D\uDE00a****ccdd", emoji);
} }
@ -210,11 +210,11 @@ public class StrUtilTest {
@Test @Test
public void replaceTest5() { public void replaceTest5() {
final String a = "\uD853\uDC09秀秀"; final String a = "\uD853\uDC09秀秀";
final String result = StrUtil.replace(a, 1, a.length(), '*'); final String result = StrUtil.replaceByCodePoint(a, 1, a.length(), '*');
Assertions.assertEquals("\uD853\uDC09**", result); Assertions.assertEquals("\uD853\uDC09**", result);
final String aa = "规划大师"; final String aa = "规划大师";
final String result1 = StrUtil.replace(aa, 2, a.length(), '*'); final String result1 = StrUtil.replaceByCodePoint(aa, 2, a.length(), '*');
Assertions.assertEquals("规划**", result1); Assertions.assertEquals("规划**", result1);
} }
@ -602,10 +602,10 @@ public class StrUtilTest {
@Test @Test
public void testReplaceByStr() { public void testReplaceByStr() {
final String replace = "SSM15930297701BeryAllen"; final String replace = "SSM15930297701BeryAllen";
final String result = StrUtil.replace(replace, 5, 12, "***"); final String result = StrUtil.replaceByCodePoint(replace, 5, 12, "***");
Assertions.assertEquals("SSM15***01BeryAllen", result); Assertions.assertEquals("SSM15***01BeryAllen", result);
final String emoji = StrUtil.replace("\uD83D\uDE00aabb\uD83D\uDE00ccdd", 2, 6, "***"); final String emoji = StrUtil.replaceByCodePoint("\uD83D\uDE00aabb\uD83D\uDE00ccdd", 2, 6, "***");
Assertions.assertEquals("\uD83D\uDE00a***ccdd", emoji); Assertions.assertEquals("\uD83D\uDE00a***ccdd", emoji);
} }