String类的split方法原理用法示例源码详解
文章目录
原理
String类的split方法的源码实现主要有两个部分:快速通道和正则表达式的split方法。
1.快速通道:
在快速通道中,首先判断给定的正则表达式是否满足特定条件,可以使用快速通道进行分割。这些条件包括:
- 正则表达式是单字符字符串,并且该字符不是正则表达式的元字符 “.$|()[{^?*+\”
- 正则表达式是双字符字符串,并且第一个字符是反斜杠,第二个字符不是ASCII数字或字母
如果正则表达式满足上述条件,则通过遍历字符串,找到与分隔符匹配的位置,并将相应的子串添加到一个ArrayList中。
最后,根据limit的值构造最终的结果数组。
2.正则表达式的split方法:
如果给定的正则表达式不满足快速通道的条件,或者需要更复杂的分割规则,就会调用Pattern.compile(regex).split(this, limit)方法进行分割。
这个方法会创建一个Pattern对象,编译正则表达式,并使用它来对当前字符串进行分割操作。最终的结果由该方法返回。
总的来说,String类的split方法在实现上结合了快速通道和正则表达式的split方法,以实现字符串的分割功能。通过快速通道,可以处理简单的分割需求,而正则表达式的split方法则提供了更灵活和强大的分割功能。
用法
String类的split方法用于将一个字符串按照给定的正则表达式进行分割,并返回一个字符串数组。
使用该方法时,需要传入两个参数:
1.regex:分隔符的正则表达式。
它指定了用于分割字符串的规则。
2.limit:结果阈值。
它控制模式的应用次数,从而影响结果数组的长度。如果limit大于0,则最多应用limit-1次模式匹配,数组的长度不会超过limit,最后一个元素将包含所有超出最后一个匹配的分隔符的输入。如果limit非正数,则模式将尽可能多地应用,数组可以具有任意长度。如果limit为0,则模式将尽可能多地应用,数组可以具有任意长度,并且尾随的空字符串将被丢弃。
示例用法:
1.基本分割:
String str = "apple,banana,orange";
String[] result = str.split(",");
// 结果为 ["apple", "banana", "orange"]
2.分割并限制结果数组长度:
String str = "apple,banana,orange";
String[] result = str.split(",", 2);
// 结果为 ["apple", "banana,orange"]
3.使用正则表达式作为分隔符:
String str = "apple123banana456orange";
String[] result = str.split("\\d+"); // 匹配连续的数字
// 结果为 ["apple", "banana", "orange"]
4.处理包含空字符串的情况:
String str = "apple,,banana,orange";
String[] result = str.split(",");
// 结果为 ["apple", "", "banana", "orange"]
5.处理以分隔符开头的情况:
String str = ",apple,banana,orange";
String[] result = str.split(",");
// 结果为 ["", "apple", "banana", "orange"]
6.处理以分隔符结尾的情况:
String str = "apple,banana,orange,";
String[] result = str.split(",");
// 结果为 ["apple", "banana", "orange", ""]
7.处理多个连续分隔符的情况:
String str = "apple,,banana,,,orange";
String[] result = str.split(",");
// 结果为 ["apple", "", "banana", "", "", "orange"]
8.使用正则表达式中的元字符作为分隔符:
String str = "apple|banana+orange";
String[] result = str.split("[|+]");
// 结果为 ["apple", "banana", "orange"]
9.处理包含特殊字符的分隔符:
String str = "apple$banana&orange";
String[] result = str.split("\\$|&");
// 结果为 ["apple", "banana", "orange"]
10.分割空字符串:
String str = "";
String[] result = str.split(",");
// 结果为 [""]
这些用例展示了String类的split方法在不同情况下的使用,可以根据具体需求选择合适的分割方式。
11.零宽度正向预查和零宽度正向回顾后发断言。
正则表达式 (?=[+*/-])|(?<=[+*/-])
使用了,用于在运算符前后进行拆分。
这个正则表达式的含义如下:
-
(?=[+*/-])
:零宽度正向预查,匹配一个位置,该位置后面紧跟着+
、*
、/
或-
中的任意一个字符。这个部分用于在运算符之前进行拆分。 -
|
:表示逻辑或,用于将两个部分连接起来。 -
(?<=[+*/-])
:零宽度正向回顾后发断言,匹配一个位置,该位置前面紧跟着+
、*
、/
或-
中的任意一个字符。这个部分用于在运算符之后进行拆分。
使用这个正则表达式作为 split()
方法的参数,可以实现在运算符前后进行字符串的拆分。
示例代码如下所示:
String equation = "x1+x2*x3-x4/x5";
String[] tokens = equation.split("(?=[+*/-])|(?<=[+*/-])");
for (String token : tokens) {
System.out.println(token);
}
输出结果:
x1
+
x2
*
x3
-
x4
/
x5
这样,我们就成功地将字符串 “x1+x2*x3-x4/x5” 按照运算符进行了拆分,并得到了预期的结果。
常见元字符
正则表达式中的元字符是具有特殊含义的字符,它们在正则表达式中有特殊的用途。以下是正则表达式中的一些常见元字符:
- . (点):匹配除换行符以外的任意单个字符。
- $:匹配输入字符串的结尾位置。
- |:表示“或”的关系,匹配左侧或右侧的表达式。
- ( ):分组,可以将多个表达式组合为一个整体,并且可以通过后续操作对整个分组进行处理。
- [ ]:字符类,匹配方括号内的任意一个字符。
- { }:限定符,指定前面的内容出现的次数。
- ^:匹配输入字符串的开始位置。
- ?:限定符,表示前面的内容可选(出现零次或一次)。
- *:限定符,表示前面的内容可以重复任意次(包括零次)。
- +:限定符,表示前面的内容可以重复至少一次。
这些元字符在正则表达式中具有特殊的含义,如果想要匹配它们本身的字符,需要使用转义字符 \ 进行转义,例如 $ 表示匹配 $ 字符本身。
源码
/**
* 使用给定的正则表达式分割此字符串。
*
* 该方法返回的数组包含该字符串的每个子字符串,这些子字符串由另一个与给定表达式匹配的子字符串终止,
* 或者由字符串的结尾终止。数组中的子字符串按照它们在此字符串中出现的顺序排列。如果表达式不匹配输入的任何部分,
* 则结果数组只有一个元素,即该字符串本身。
*
* 当字符串的开头存在正向匹配时,结果数组的开头将包括一个空的子字符串。但是,当字符串的开头存在零宽度匹配时,
* 不会产生空的子字符串。
*
* limit 参数控制模式的应用次数,从而影响结果数组的长度。如果 limit <i>n</i> 大于零,则模式最多应用 <i>n</i>-1 次,
* 数组的长度不会超过 <i>n</i>,并且数组的最后一个条目将包含所有超出最后一个匹配的分隔符的输入。如果 <i>n</i> 非正,
* 则模式将应用尽可能多的次数,并且数组可以具有任意长度。如果 <i>n</i> 为零,则模式将应用尽可能多的次数,数组可以具有任意长度,
* 并且尾随的空字符串将被丢弃。
*
* 例如,字符串 "boo:and:foo" 使用以下参数产生以下结果:
*
* Regex Limit Result
* ":" 2 { "boo", "and:foo" }
* ":" 5 { "boo", "and", "foo" }
* ":" -2 { "boo", "and", "foo" }
* "o" 5 { "b", "", ":and:f", "", "" }
* "o" -2 { "b", "", ":and:f", "", "" }
* "o" 0 { "b", "", ":and:f" }
*
* 使用形如 str.split(regex, n) 的方法调用将产生与表达式 Pattern.compile(regex).split(str, n) 相同的结果。
*
* @param regex 分隔符正则表达式
* @param limit 结果阈值,如上所述
* @return 由分割此字符串而计算得到的字符串数组
* @throws PatternSyntaxException 如果正则表达式的语法无效
* @see java.util.regex.Pattern
* @since 1.4
* @spec JSR-51
*/
public String[] split(String regex, int limit) {
/* 快速通道,如果正则表达式是
(1) 单字符字符串,并且该字符不是正则表达式的元字符 ".$|()[{^?*+\\",或
(2) 双字符字符串,并且第一个字符是反斜杠,第二个字符不是ASCII数字或ASCII字母。*/
char ch = 0;
if (((regex.value.length == 1 &&
".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1) ||
(regex.length() == 2 &&
regex.charAt(0) == '\\' &&
(((ch = regex.charAt(1))-'0')|('9'-ch)) < 0 &&
((ch-'a')|('z'-ch)) < 0 &&
((ch-'A')|('Z'-ch)) < 0)) &&
(ch < Character.MIN_HIGH_SURROGATE ||
ch > Character.MAX_LOW_SURROGATE))
{
int off = 0;
int next = 0;
boolean limited = limit > 0;
ArrayList<String> list = new ArrayList<>();
while ((next = indexOf(ch, off)) != -1) {
if (!limited || list.size() < limit - 1) {
list.add(substring(off, next));
off = next + 1;
} else { // 最后一个
//assert (list.size() == limit - 1);
list.add(substring(off, value.length));
off = value.length;
break;
}
}
// 如果找不到匹配项,则返回这个字符串
if (off == 0)
return new String[]{this};
// 添加剩余的段
if (!limited || list.size() < limit)
list.add(substring(off, value.length));
// 构造结果
int resultSize = list.size();
if (limit == 0) {
while (resultSize > 0 && list.get(resultSize - 1).length() == 0) {
resultSize--;
}
}
String[] result = new String[resultSize];
return list.subList(0, resultSize).toArray(result);
}
return Pattern.compile(regex).split(this, limit);
}
/**
* 使用给定的正则表达式分割此字符串。
*
* 该方法的工作方式就像使用给定的表达式和限制参数为零调用两个参数的 split 方法一样。
* 因此,结果数组不包含尾随的空字符串。
*
* 例如,字符串 "boo:and:foo" 使用以下表达式产生以下结果:
*
* Regex Result
* ":" { "boo", "and", "foo" }
* "o" { "b", "", ":and:f" }
*
* @param regex 分隔符正则表达式
* @return 由分割此字符串而计算得到的字符串数组
* @throws PatternSyntaxException 如果正则表达式的语法无效
* @see java.util.regex.Pattern
* @since 1.4
* @spec JSR-51
*/
public String[] split(String regex) {
return split(regex, 0);
}