背景
在日志中发现存在着空指针报错,看报错行数,是属于在json对象中,没取到对应的值。顺着线索找到了对应的接口,作用是查询数据库数据,支持批量查询,多个id使用逗号分隔,例如:
改项目基础语言是golang,在strings包中存在Split方法,支持对字符串进行分隔,返回字符串切片数组,由此产生了一个意想不到的结果。
问题导引
项目中的代码不变展示,写了一段新代码,把主要受影响的代码段展示出
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
package main
import (
"fmt"
"strings"
)
func main() {
str := ""
strList := strings.Split(str, ",")
for _, s := range strList {
fmt.Println("asd:", s)
}
}
|
根据经验,大家觉得这段代码会打印出asd这一行吗?
结果如下,竟然执行了for循环
1
2
|
$ go run main.go
asd:
|
后面又打印出切片的容量和长度,事实上切片中包含了一个为空的字符串。试想一下,字符串为空,返回的切片长度应该是零才对吧。
strings.Split源码
接着去翻看对应的源码,最后都会调用到genSplit函数,当sep不等于空字符串时,切片长度必然是大于或等于1的。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
func Split(s, sep string) []string { return genSplit(s, sep, 0, -1) }
func genSplit(s, sep string, sepSave, n int) []string {
if n == 0 {
return nil
}
if sep == "" {
return explode(s, n)
}
if n < 0 {
n = Count(s, sep) + 1
}
a := make([]string, n)
n--
i := 0
for i < n {
m := Index(s, sep)
if m < 0 {
break
}
a[i] = s[:m+sepSave]
s = s[m+len(sep):]
i++
}
a[i] = s
return a[:i+1]
}
|
为什么golang这段代码会这么设计呢?好奇
其他语言分隔字符串
接着我查了下其他语言分隔字符串方法,是否都是这样的设计。
Java
1
2
3
4
5
6
7
8
9
|
public class HelloWorld {
public static void main(String[] args) {
String str = "";
String[] strList = str.split(",");
for (int i = 0; i < strList.length; i++) {
System.out.println("asd:"+strList[i]);
}
}
}
|
结果:
PHP
1
2
3
4
5
6
|
//php5支持
<?php
$str = "";
$strList = split('[/.-]', $str);
echo var_dump($strList);
?>
|
1
2
3
4
5
6
|
//php7支持
<?php
$str = "";
$strList2 = explode(',', $str);
echo var_dump($strList2);
?>
|
结果:
1
2
3
4
5
6
7
8
|
array(1) {
[0]=>
string(0) ""
}
array(1) {
[0]=>
string(0) ""
}
|
看来是我想多了,几种流行语言,字符串切割都是会包含空字符串。
结论
几种流行语言split函数返回结果是一样的,在日后开发中,需要对接口的边界条件做充分的测试。