diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/text/CharSequenceUtil.java b/hutool-core/src/main/java/org/dromara/hutool/core/text/CharSequenceUtil.java index 31b17caf9..2d87c7913 100644 --- a/hutool-core/src/main/java/org/dromara/hutool/core/text/CharSequenceUtil.java +++ b/hutool-core/src/main/java/org/dromara/hutool/core/text/CharSequenceUtil.java @@ -1419,7 +1419,7 @@ public class CharSequenceUtil extends StrValidator { * } * * - * @param str 被处理的字符串 + * @param str 被处理的字符串,{@code null}忽略 * @param prefixOrSuffix 前缀或后缀 * @return 处理后的字符串 * @since 3.1.2 @@ -1449,8 +1449,8 @@ public class CharSequenceUtil extends StrValidator { * * * @param str 被处理的字符串 - * @param prefix 前缀 - * @param suffix 后缀 + * @param prefix 前缀,{@code null}忽略 + * @param suffix 后缀,{@code null}忽略 * @return 处理后的字符串 * @since 3.1.2 */ @@ -1475,8 +1475,8 @@ public class CharSequenceUtil extends StrValidator { * * * @param str 被处理的字符串 - * @param prefix 前缀 - * @param suffix 后缀 + * @param prefix 前缀,{@code null}忽略 + * @param suffix 后缀,{@code null}忽略 * @param ignoreCase 是否忽略大小写 * @return 处理后的字符串 * @since 3.1.2 @@ -1486,29 +1486,8 @@ public class CharSequenceUtil extends StrValidator { return toStringOrNull(str); } - final String str2 = str.toString(); - int from = 0; - int to = str2.length(); - - if (startWith(str2, prefix, ignoreCase)) { - from = prefix.length(); - if (from == to) { - // "a", "a", "a" -> "" - return EMPTY; - } - } - if (endWith(str2, suffix, ignoreCase)) { - to -= suffix.length(); - if (from == to) { - // "a", "a", "a" -> "" - return EMPTY; - } else if (to < from) { - // pre去除后和suffix有重叠,如 ("aba", "ab", "ba") -> "a" - to += suffix.length(); - } - } - - return str2.substring(from, to); + return new StrStripper(prefix, suffix, ignoreCase, false) + .apply(str); } /** @@ -1522,7 +1501,7 @@ public class CharSequenceUtil extends StrValidator { * * * @param str 被处理的字符串 - * @param prefixOrSuffix 前缀或后缀 + * @param prefixOrSuffix 前缀或后缀,{@code null}忽略 * @return 处理后的字符串 * @since 5.8.30 */ @@ -1558,44 +1537,47 @@ public class CharSequenceUtil extends StrValidator { * @param prefix 前缀 * @param suffix 后缀 * @return 处理后的字符串 - * @since 5.8.30 + * @since 6.0.0 */ public static String stripAll(final CharSequence str, final CharSequence prefix, final CharSequence suffix) { + return stripAll(str, prefix, suffix, false); + } + + /** + * 去除两边所有的指定字符串 + * + *
{@code
+ * "aaa_STRIPPED_bbb", "a", "b" -> "_STRIPPED_"
+ * "aaa_STRIPPED_bbb", null, null -> "aaa_STRIPPED_bbb"
+ * "aaa_STRIPPED_bbb", "", "" -> "aaa_STRIPPED_bbb"
+ * "aaa_STRIPPED_bbb", "", "b" -> "aaa_STRIPPED_"
+ * "aaa_STRIPPED_bbb", null, "b" -> "aaa_STRIPPED_"
+ * "aaa_STRIPPED_bbb", "a", "" -> "_STRIPPED_bbb"
+ * "aaa_STRIPPED_bbb", "a", null -> "_STRIPPED_bbb"
+ *
+ * // special test
+ * "aaaaaabbb", "aaa", null -> "bbb"
+ * "aaaaaaabbb", "aa", null -> "abbb"
+ *
+ * "aaaaaaaaa", "aaa", "aa" -> ""
+ * "a", "a", "a" -> ""
+ * }
+ *
+ *
+ * @param str 被处理的字符串
+ * @param prefix 前缀
+ * @param suffix 后缀
+ * @param ignoreCase 是否忽略大小写
+ * @return 处理后的字符串
+ * @since 6.0.0
+ */
+ public static String stripAll(final CharSequence str, final CharSequence prefix, final CharSequence suffix, final boolean ignoreCase) {
if (isEmpty(str)) {
return toStringOrNull(str);
}
- final String prefixStr = toStringOrEmpty(prefix);
- final String suffixStr = toStringOrEmpty(suffix);
-
- final String str2 = str.toString();
- int from = 0;
- int to = str2.length();
-
- if (!prefixStr.isEmpty()) {
- while (str2.startsWith(prefixStr, from)) {
- from += prefix.length();
- if (from == to) {
- // "a", "a", "a" -> ""
- return EMPTY;
- }
- }
- }
- if (!suffixStr.isEmpty()) {
- while (str2.startsWith(suffixStr, to - suffixStr.length())) {
- to -= suffixStr.length();
- if (from == to) {
- // "a", "a", "a" -> ""
- return EMPTY;
- } else if (to < from) {
- // pre去除后和suffix有重叠,如 ("aba", "ab", "ba") -> "a"
- to += suffixStr.length();
- break;
- }
- }
- }
-
- return str2.substring(from, to);
+ return new StrStripper(prefix, suffix, ignoreCase, true)
+ .apply(str);
}
// endregion
diff --git a/hutool-core/src/main/java/org/dromara/hutool/core/text/StrStripper.java b/hutool-core/src/main/java/org/dromara/hutool/core/text/StrStripper.java
new file mode 100644
index 000000000..13ab1a6ec
--- /dev/null
+++ b/hutool-core/src/main/java/org/dromara/hutool/core/text/StrStripper.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2024 Hutool Team and hutool.cn
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.dromara.hutool.core.text;
+
+import java.io.Serializable;
+import java.util.function.UnaryOperator;
+
+/**
+ * 字符串裁剪器,用于裁剪字符串前后缀{@code
+ * "aaa_STRIPPED_bbb", "a", "b" -> "aa_STRIPPED_bb"
+ * "aaa_STRIPPED_bbb", null, null -> "aaa_STRIPPED_bbb"
+ * "aaa_STRIPPED_bbb", "", "" -> "aaa_STRIPPED_bbb"
+ * "aaa_STRIPPED_bbb", "", "b" -> "aaa_STRIPPED_bb"
+ * "aaa_STRIPPED_bbb", null, "b" -> "aaa_STRIPPED_bb"
+ * "aaa_STRIPPED_bbb", "a", "" -> "aa_STRIPPED_bbb"
+ * "aaa_STRIPPED_bbb", "a", null -> "aa_STRIPPED_bbb"
+ *
+ * "a", "a", "a" -> ""
+ * }
+ *
+ * @param charSequence 被处理的字符串
+ * @return 处理后的字符串
+ */
+ private String stripOnce(final CharSequence charSequence) {
+ if (StrUtil.isEmpty(charSequence)) {
+ return StrUtil.toStringOrNull(charSequence);
+ }
+
+ final String str = charSequence.toString();
+ int from = 0;
+ int to = str.length();
+
+ if (StrUtil.isNotEmpty(this.prefix) && startWith(str, this.prefix, 0)) {
+ from = this.prefix.length();
+ if (from == to) {
+ // "a", "a", "a" -> ""
+ return StrUtil.EMPTY;
+ }
+ }
+ if (endWithSuffix(str)) {
+ to -= this.suffix.length();
+ if (from == to) {
+ // "a", "a", "a" -> ""
+ return StrUtil.EMPTY;
+ } else if (to < from) {
+ // pre去除后和suffix有重叠,如 ("aba", "ab", "ba") -> "a"
+ to += this.suffix.length();
+ }
+ }
+
+ return str.substring(from, to);
+ }
+
+ /**
+ * 去除两边所有的指定字符串
+ *
+ * {@code
+ * "aaa_STRIPPED_bbb", "a", "b" -> "_STRIPPED_"
+ * "aaa_STRIPPED_bbb", null, null -> "aaa_STRIPPED_bbb"
+ * "aaa_STRIPPED_bbb", "", "" -> "aaa_STRIPPED_bbb"
+ * "aaa_STRIPPED_bbb", "", "b" -> "aaa_STRIPPED_"
+ * "aaa_STRIPPED_bbb", null, "b" -> "aaa_STRIPPED_"
+ * "aaa_STRIPPED_bbb", "a", "" -> "_STRIPPED_bbb"
+ * "aaa_STRIPPED_bbb", "a", null -> "_STRIPPED_bbb"
+ *
+ * // special test
+ * "aaaaaabbb", "aaa", null -> "bbb"
+ * "aaaaaaabbb", "aa", null -> "abbb"
+ *
+ * "aaaaaaaaa", "aaa", "aa" -> ""
+ * "a", "a", "a" -> ""
+ * }
+ *
+ * @param charSequence 被处理的字符串
+ * @return 处理后的字符串
+ * @since 5.8.30
+ */
+ private String stripAll(final CharSequence charSequence) {
+ if (StrUtil.isEmpty(charSequence)) {
+ return StrUtil.toStringOrNull(charSequence);
+ }
+
+ final String str = charSequence.toString();
+ int from = 0;
+ int to = str.length();
+
+ if (StrUtil.isNotEmpty(this.prefix)) {
+ while (startWith(str, this.prefix, from)) {
+ from += this.prefix.length();
+ if (from == to) {
+ // "a", "a", "a" -> ""
+ return StrUtil.EMPTY;
+ }
+ }
+ }
+ if (StrUtil.isNotEmpty(suffix)) {
+ final int suffixLength = this.suffix.length();
+ while (startWith(str, suffix, to - suffixLength)) {
+ to -= suffixLength;
+ if (from == to) {
+ // "a", "a", "a" -> ""
+ return StrUtil.EMPTY;
+ } else if (to < from) {
+ // pre去除后和suffix有重叠,如 ("aba", "ab", "ba") -> "a"
+ to += suffixLength;
+ break;
+ }
+ }
+ }
+
+ return str.substring(from, to);
+ }
+
+ /**
+ * 判断是否以指定前缀开头
+ *
+ * @param charSequence 被检查的字符串
+ * @param from 开始位置
+ * @return 是否以指定前缀开头
+ */
+ private boolean startWith(final CharSequence charSequence, final CharSequence strToCheck, final int from) {
+ return new StrRegionMatcher(this.ignoreCase, false, from)
+ .test(charSequence, strToCheck);
+ }
+
+ /**
+ * 判断是否以指定后缀结尾
+ *
+ * @param charSequence 被检查的字符串
+ * @return 是否以指定后缀结尾
+ */
+ private boolean endWithSuffix(final CharSequence charSequence) {
+ return StrUtil.isNotEmpty(suffix) && StrUtil.endWith(charSequence, suffix, ignoreCase);
+ }
+}
diff --git a/hutool-core/src/test/java/org/dromara/hutool/core/text/StrStripperTest.java b/hutool-core/src/test/java/org/dromara/hutool/core/text/StrStripperTest.java
new file mode 100644
index 000000000..42c848a63
--- /dev/null
+++ b/hutool-core/src/test/java/org/dromara/hutool/core/text/StrStripperTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2024 Hutool Team and hutool.cn
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.dromara.hutool.core.text;
+
+import org.junit.jupiter.api.Test;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+public class StrStripperTest {
+
+ @Test
+ void stripAllPrefixTest() {
+ final StrStripper prefixStripper = new StrStripper("a", null, false, true);
+ assertEquals("_STRIPPED_bbb", prefixStripper.apply("aaa_STRIPPED_bbb"));
+ // 不忽略大小写则不替换
+ assertEquals("AAA_STRIPPED_bbb", prefixStripper.apply("AAA_STRIPPED_bbb"));
+ }
+
+ @Test
+ void stripAllSuffixTest() {
+ final StrStripper suffixStripper = new StrStripper(null, "b", false, true);
+ assertEquals("aaa_STRIPPED_", suffixStripper.apply("aaa_STRIPPED_bbb"));
+ // 不忽略大小写则不替换
+ assertEquals("aaa_STRIPPED_BBB", suffixStripper.apply("aaa_STRIPPED_BBB"));
+ }
+
+ @Test
+ void stripAllPrefixIgnoreCaseTest() {
+ final StrStripper prefixStripper = new StrStripper("A", null, true, true);
+ assertEquals("_STRIPPED_bbb", prefixStripper.apply("aaa_STRIPPED_bbb"));
+ assertEquals("_STRIPPED_bbb", prefixStripper.apply("Aaa_STRIPPED_bbb"));
+ assertEquals("_STRIPPED_bbb", prefixStripper.apply("AAA_STRIPPED_bbb"));
+ assertEquals("_STRIPPED_bbb", prefixStripper.apply("aaA_STRIPPED_bbb"));
+ }
+
+ @Test
+ void stripAllSuffixIgnoreCaseTest() {
+ final StrStripper prefixStripper = new StrStripper(null, "B", true, true);
+ assertEquals("aaa_STRIPPED_", prefixStripper.apply("aaa_STRIPPED_bbb"));
+ assertEquals("aaa_STRIPPED_", prefixStripper.apply("aaa_STRIPPED_Bbb"));
+ assertEquals("aaa_STRIPPED_", prefixStripper.apply("aaa_STRIPPED_BBB"));
+ assertEquals("aaa_STRIPPED_", prefixStripper.apply("aaa_STRIPPED_bbB"));
+ }
+}
+