手写String的split()方法,String的split()方法分三种情况:
- regex只有一位,且不为列出的特殊字符;
- regex有两位,第一位位转义字符且第二位不是数字和字母;
- 最后一种情况就是正则表达式去拆分字符串。
package com.dalingjia.algorithm.string;
import com.google.common.collect.Lists;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* 手动实现java的split()方法
*/
public class SpiltUtil {
private static final String contant = ".$|()[{^?*+\\";
public static String[] splitMethod(String string, String regex){
char ch = 0;
int off = 0;
int next = 0;
ArrayList<String> list = Lists.newArrayList();
if(regex.length() == 1 && contant.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)
{
while ((next = string.indexOf(ch,off)) > 0){
list.add(string.substring(off, next));
off = next + 1;
}
if(off == 0){
return new String[]{string};
}
list.add(string.substring(off, string.length()));
return interceptEmpty(list);
}
return regexSplit(string, regex);
}
private static String[] regexSplit(String string, String regex) {
int off = 0;
//将给定的正则表达式编译到模式中
Pattern pattern = Pattern.compile(regex);
//创建给定输入与此模式匹配的匹配器
Matcher m = pattern.matcher(string);
List<String> list = Lists.newArrayList();
while (m.find()){
//m.start(): 返回第一个匹配字符的索引
list.add(string.substring(off, m.start()));
//m.end(): 返回最后匹配字符之后的偏移量
off = m.end();
}
if(off == 0){
return new String[]{string};
}
list.add(string.substring(off, string.length()));
return interceptEmpty(list);
}
//截取空字符串
private static String[] interceptEmpty(List<String> list){
//截取空的字符串
int resultSize = list.size();
while (resultSize>0 && list.get(resultSize-1).length() == 0){
resultSize--;
}
String[] strings = new String[resultSize];
return list.subList(0, resultSize).toArray(strings);
}
//测试方法
@Test
public void test() {
//测试regex只有一位,且不为列出的特殊字符
String s1 = "gg,tge,hbfs,ijkd,,,";
String[] strings1 = splitMethod(s1, ",");
for (int i = 0; i < strings1.length; i++) {
System.out.println(strings1[i]);
}
//测试regex有两位,第一位位转义字符且第二位不是数字和字母
String s2 = "bb\'dn\'ags\'kl\'\'";
String[] strings2 = splitMethod(s2,"\\'");
for (int i = 0; i < strings2.length; i++) {
System.out.println(strings2[i]);
}
//测试正则表达式
String ss = "ac32dge533grhr139ljs343";
String[] strings = splitMethod(ss,"[\\d]+");
for (int i = 0; i < strings.length; i++) {
System.out.println(strings[i]);
}
}
}
String的split()方法源码如下:
public String[] split(String regex, int limit) {
char ch = 0;
if (
( //如果regex只有一位,且不为列出的特殊字符
(regex.length() == 1 && ".$|()[{^?*+\\".indexOf(ch = regex.charAt(0)) == -1)
||
//如果regex有2位,第一位为转义字符,且第二位不是数字或字母
/**
* “||”: 如果左边计算后的操作数为true,右边则不再执行,返回true;
*
* “|”:前后两个操作数都会进行计算。也就是说:“|”不存在短路。
*/
(regex.length() == 2 && regex.charAt(0) == '\\' && ( ((ch = regex.charAt(1))-'0')|('9'-ch) ) < 0 && ((ch-'a')|('z'-ch)) < 0 && ((ch-'A')|('Z'-ch)) < 0)
)
&&
/**
* UTF-16 编码中的 Unicode 高代理项代码单元的最小值, '\uD800'
* UTF-16 编码中的 Unicode 低代理项代码单元的最大值, '\uDFFF'
*/
(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 { // last one
//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);
}
//第三种情况,利用正则表达式去split字符串
return Pattern.compile(regex).split(this, limit);
}
String的split()方法最后一行调用的Pattern的split()方法,二则源码大同小异。
源码如下:
public String[] split(CharSequence input, int limit) {
int index = 0;
boolean matchLimited = limit > 0;
ArrayList<String> matchList = new ArrayList<>();
Matcher m = matcher(input);
while(m.find()) {
if (!matchLimited || matchList.size() < limit - 1) {
if (index == 0 && index == m.start() && m.start() == m.end()) {
continue;
}
String match = input.subSequence(index, m.start()).toString();
matchList.add(match);
index = m.end();
} else if (matchList.size() == limit - 1) { // last one
String match = input.subSequence(index, input.length()).toString();
matchList.add(match);
index = m.end();
}
}
// 如果没有匹配的,直接返回该字符串
if (index == 0)
return new String[] {input.toString()};
// 添加最后一个子序列
if (!matchLimited || matchList.size() < limit)
matchList.add(input.subSequence(index, input.length()).toString());
// 截取后面的空字符
int resultSize = matchList.size();
if (limit == 0)
while (resultSize > 0 && matchList.get(resultSize-1).equals(""))
resultSize--;
String[] result = new String[resultSize];
//集合截取
return matchList.subList(0, resultSize).toArray(result);
}
正则表达式的常用方法:
- Pattern.compile(String regex): 将给定的正则表达式编译到模式中;
- Pattern.split(CharSequence input):按照此模式拆分给定的输入序列;
- Pattern.matcher(CharSequence input): 创建给定输入与此模式匹配的匹配器;
- Matcher.find(): 查找与该模式匹配的输入序列的下一个子序列;
- Matcher.start(): 返回第一个匹配字符的索引;
- Matcher.end(): 返回最后匹配字符之后的偏移量。
注意:若split后字符串数组的尾部字符串为"",则需要舍弃空字符串