一、简介

程序员经常遇到涉及分割字符串的算法。在特殊场景中,可能需要根据单个或多个不同的分隔符拆分字符串,并 在拆分操作中返回分隔符

让我们详细讨论此 字符串 拆分问题的不同可用解决方案。

2. 基本原理

Java 世界提供了相当多的库( java.lang.String 、 Guava 和 Apache Commons 等)来促进简单和相当复杂情况下的字符串分割。此外,功能丰富的正则表达式在拆分围绕特定模式匹配的问题时提供了额外的灵活性。

3. 环视断言

在正则表达式中,环视断言表明 通过在源字符串的当前位置向前查找 (lookahead) 或向后查找 (lookbehind) 来查找另一个模式,可以进行匹配 。让我们通过一个例子更好地理解这一点。

仅当后跟 “Baeldung” 前瞻断言 Java(?=Baeldung) 才匹配“Java”。

同样,负后向断言 (?<!#)\d+ 仅匹配前面没有“#”的数字。

让我们使用这种环视断言正则表达式并为我们的问题设计一个解决方案。

在本文解释的所有示例中,我们将使用两个简单的 String

String text = "Hello@World@This@Is@A@Java@Program";
String textMixed = "@HelloWorld@This:Is@A#Java#Program";

4. 使用 String.split()

让我们首先使用核心 Java 库的 String 类中的 split() 方法。

此外,我们将评估适当的先行断言、后行断言以及它们的组合,以根据需要分割字符串。

4.1.正向前瞻

首先,让我们使用前瞻断言 “((?=@))” 并将字符串 文本 围绕其匹配项进行分割:

String[] splits = text.split("((?=@))");

前瞻正则表达式通过 “@” 符号的前向匹配来分割字符串。结果数组的内容是:

[Hello, @World, @This, @Is, @A, @Java, @Program]

使用此正则表达式不会在 splits 数组中单独返回分隔符。让我们尝试另一种方法。

4.2.积极的后瞻

我们还可以使用正向回顾断言 “((?<=@))” 来分割字符串 文本

String[] splits = text.split("((?<=@))");

但是,生成的输出仍然不包含分隔符作为数组的单个元素:

[Hello@, World@, This@, Is@, A@, Java@, Program]

4.3.正向前瞻或后向展望

我们可以将上述两种解释的环视与逻辑或结合使用,并查看其实际效果。

得到的正则表达式 “((?=@)|(?<=@))” 肯定会给我们想要的结果。 下面的代码片段演示了这一点:

String[] splits = text.split("((?=@)|(?<=@))");

上面的正则表达式分割字符串,结果数组包含分隔符:

[Hello, @, World, @, This, @, Is, @, A, @, Java, @, Program]

现在我们了解了所需的环视断言正则表达式,我们可以根据输入字符串中存在的不同类型的分隔符对其进行修改。

让我们尝试使用合适的正则表达式来拆分前面定义的 textMixed

String[] splitsMixed = textMixed.split("((?=:|#|@)|(?<=:|#|@))");

执行上面这行代码后,看到以下结果就不足为奇了:

[@, HelloWorld, @, This, :, Is, @, A, #, Java, #, Program]

5.使用番石榴 分离器

考虑到现在我们已经清楚了上一节中讨论的正则表达式断言,让我们深入研究一下 Google 提供的 Java 库。

GuavaSplitter 类提供了 on()onPattern() 方法,使用正则表达式模式作为分隔符来分割字符串。

首先,让我们看看它们在包含单个分隔符 “@” 的字符串 文本 上的作用:

List<String> splits = Splitter.onPattern("((?=@)|(?<=@))").splitToList(text);
List<String> splits2 = Splitter.on(Pattern.compile("((?=@)|(?<=@))")).splitToList(text);

执行上述代码行的结果与 split 方法生成的结果非常相似,只是我们现在使用的是 List 而不是数组。

同样,我们也可以使用这些方法来分割包含多个不同分隔符的字符串:

List<String> splitsMixed = Splitter.onPattern("((?=:|#|@)|(?<=:|#|@))").splitToList(textMixed);
List<String> splitsMixed2 = Splitter.on(Pattern.compile("((?=:|#|@)|(?<=:|#|@))")).splitToList(textMixed);

我们可以看到,上述两种方法之间的差异非常明显。

on() 方法接受 java.util.regex.Pattern 参数,而 onPattern() 方法仅接受分隔符正则表达式作为 String

6. 使用 Apache Commons StringUtils

我们还可以利用Apache Commons Lang项目的 StringUtils 方法 splitByCharacterType()。

值得注意的是,这个 * 方法的工作原理是按 java.lang.Character.getType(char) 返回的字符类型分割输入字符串。在这里,我们无法选择或提取我们选择的分隔符。 *

此外,当源字符串始终具有恒定的大小写(大写或小写)时,它会提供最佳结果:

String[] splits = StringUtils.splitByCharacterType("pg@no;10@hello;world@this;is@a#10words;Java#Program");

在上面的字符串中看到的不同字符类型是大写和小写字母、数字和特殊字符(@;#)。

因此,正如预期的那样,生成的数组 拆分 如下所示:

[pg, @, no, ;, 10, @, hello, ;, world, @, this, ;, is, @, a, #, 10, words, ;, J, ava, #, P, rogram]

七、结论

在本文中,我们了解了如何以分隔符在结果数组中也可用的方式拆分字符串。

首先,我们讨论了环视断言并使用它们来获得所需的结果。后来我们使用Guava库提供的方法也达到了类似的效果。

最后,我们总结了 Apache Commons Lang 库,它提供了一种更用户友好的方法来解决分割字符串的相关问题,并返回分隔符。

与往常一样,本文中使用的代码可以在 GitHub 上找到。