切分字符串

Updated: 2019-01-05

入门

一般的语言都会提供切分字符串的函数,比如把a,b,c切成ab,和c

Java

jshell> "a,b,c".split(",");
$1 ==> String[3] { "a", "b", "c" }

或 Python

>>> "a,b,c".split(",")
['a', 'b', 'c']

坑!

"a.b.c"

Python 没问题:

>>> "a.b.c".split(".")
['a', 'b', 'c']

Java 好像不太对:

jshell> "a.b.c".split(".");
$2 ==> String[0] {  }

"a|b|c"

Python 依然茁壮

>>> 'a|b|c'.split("|")
['a', 'b', 'c']

Java 还是……

jshell> "a|b|c".split("|")
$3 ==> String[5] { "a", "|", "b", "|", "c" }

",,"

Python 再次准确无误的给了我们三个空串:

>>> ",,".split(",")
['', '', '']

Java 却什么都没有

jshell> ",,".split(",")
$3 ==> String[0] {  }

"a\b\c"

再来一个 Java 的变态例子:将a\b\c保存到一个文本文件tmp.txt中,然后读取它

jshell> String s = Files.readAllLines(Paths.get("tmp.txt")).get(0);
s ==> "a\\b\\c"

文件中的一个\变成了两个,因为要 escape。那如何 split 这个字符串呢?

jshell> s.split("\")
|  Error:
|  unclosed string literal
|  s.split("\")
|

显然一个\是肯定不行的。那就两个:

jshell> s.split("\\")
|  Exception java.util.regex.PatternSyntaxException: Unexpected internal error near index 1
|  \
|        at Pattern.error (Pattern.java:2015)
|        at Pattern.compile (Pattern.java:1784)
|        at Pattern.<init> (Pattern.java:1427)
|        at Pattern.compile (Pattern.java:1068)
|        at String.split (String.java:2317)
|        at String.split (String.java:2364)
|        at (#6:1)

进阶

Java 中的 split 用的是 regex,所以需要转义,比如第一个例子

jshell> "a.b.c".split("\\.")
$4 ==> String[3] { "a", "b", "c" }

注意在转义的时候如果写作\.,那是在尝试转义.,必须要两个转义符才能保留下一个\在字符串里,然后再把\.放到正则表达式里去转义.,否则在 regex 里.表示任意字符。

第二个例子也是一样的:

jshell> "a|b|c".split("\\|")
$5 ==> String[3] { "a", "b", "c" }

第三个例子,是因为所有末尾的空串都被扔掉了。比如

jshell> ",a,,".split(",")
$6 ==> String[2] { "", "a" }

第一个空串被保留了,但a后面的空串都没了。如果要保留所有的空串,需要将第二个参数设为-1

jshell> ",a,,".split(",", -1)
$7 ==> String[4] { "", "a", "", "" }

最后一个例子,正确的解法是这样的:

jshell> s.split("\\\\")
$8 ==> String[3] { "a", "b", "c" }

没错,一个文件中的\,要用四个来分解。数数这里 escape 了几次。