前段时间碰到一个代码review,其中一个bug是关于java的substring的。java的substring作为一个String类下的函数,对于Java的熟练工来说,应当是了如指掌的,可是在我的记忆中生生地搞错了substring原来是不允许越界的。
我必须要为满脑袋浆糊的原因做出一个解释,那就是我发现substring函数,也就是取子字符串函数,虽是一个几乎所有语言都会提供的基本得不能再基本的库函数,但有趣的是,各家对于这个函数的设计差别很大,在一些语言中,利用一些基本类型的差别,很巧妙地解决了这个问题。
在java中,取子字符串的方法是
"abc".substring(1,2); //输出b,通过给定区间的方法,而且是左闭右开的区间,也就是区间的末端的字符是不取的。 "abc".substring(1); //输出bc,可以省略第二个参数,这通常意味着将从begin参数开始之后的字符全部输出 "abc".substring(1,4); //error,越界了!,显然"abc"没有第3(从0开始标号)个字符
而在传统的C++中,取子字符串函数的第二个参数就变成了要去的字符串的长度
string("abc").substr(1,2); //输出bc,你看不一样了吧 string("abc").substr(1); //输出bc,这个一样 string("abc").substr(1,4); //error,越界了!,字符'b'之后没有4个字符。
在很多新兴的语言(新吗?小众吗?非主流吗?)眼中,前面两种语言无疑是太老土了。在脚本和函数式语言中,经常会有向量、列表之类的概念,字符串就变成了一种特殊的向量或列表,这时候可以通过使用向量和列表的方法来获取子字符串,以下举两个例子
%Matlab str='pumpkin'; s1=str(2:3) %second and third element s2=str(3:6) % 3rd-6th element s3=str(1:2:end) % odd elements s4=str(end:-1:1) %reverse order s5=str(randperm(3)) % three random elements s5=str(kron([1 1],[1:7])) % get two pumpkins out of one
//Groovy String s = “CU soon!” assert s[-1..0] == “!noos UC”
下面对各种语言的取字符串方法做一个总结,鉴于偶水品极为有限,错误在所难免,谢谢指正。
语言 | 函数原型 | 取值方法 | 越界 | 类似函数 |
C | char *strncpy(char *dest, const char *src, int length) | 长度 | dest多余尾部填0,会非法访问。 length为负则不复制。 |
strcpy memcpy |
C++ | string string::substr(size_t pos =0, size_t length=npos) const | 长度 | pos: out_of_range异常 n:只到结尾 |
|
Java | String String.substring(int beginIndex [, int endIndex]) | 左闭右开 | IndexOutOfBoundsException | subSequence |
C#/.net | string string.Substring(int startIndex [, int length]) | 长度 | ArgumentOutOfRangeException | |
VB | mid(string,start [,length]) | 长度 | 越界后输出到结尾 但Start <= 0 or Length < 0 时ArgumentException(错误5) |
left、right |
PHP | string substr(string $str,int $start [,int $length]) | 长度 | 正负均可,尽量输出 | mb_substr |
Python | MyStr[a:b](a、b可省略) | 左闭右开 | 正负均可,尽量输出 | |
Ruby | MyStr[begin, length] | 长度 | length负时为nil越界后输出到结尾 | |
MyStr[begin..end] | 双闭区间 | begin>length为nil begin不越界时最多到结尾 都越界时输出nil |
||
JavaScript | String.substr(start [, length ]) | 长度 | length负时为空 越界后输出到结尾 |
|
String.substring(start, end) | 左闭右开 | |||
Pascal | Copy(S: String; Index: Integer; Count: Integer): String | 长度 | 越界后输出到结尾 | |
T-SQL | SUBSTRING ( expression ,start , length ) | 长度 | length负时报错越界后 输出到结尾 |
|
PL/SQL | SUBSTR( source_string, start_position, [ length ] ) | 长度 | length负时为NULL 越界后输出到结尾 |
|
Lua | strsub (s, i, [j]) string.sub (s, i [, j]) |
双闭区间 | 越界后输出到结尾 | |
Matlab | MyStr(MatlabRange) | Matlab区间或向量 | 不合法的Range或Range中元素越界则报错 | |
SAS | SUBSTR(SOURCE, POSITION, LENGTH) | 长度 | 报错Invalid argument | |
Bash | ${string:position[:length]} | 长度 | (不知道,待补充) | |
R | substr(x, start, stop) substring(text, first, last = 1000000L) |
双闭区间 | (不知道,待补充) | |
Erlang | substr(String, Start[, Length]) | 长度 | (不知道,待补充) | |
Common Lisp | subseq STRING begin end | 左闭右开 | (不知道,待补充) | |
Groovy | MyStr[begin..end]MyStr[begin] | 双闭区间 | 支持正负和逆区间段 总能得到一个输出 |
[1] 各大语言的官方文档,oh,这个太多了,不写了
[2] http://www.mathworks.com/matlabcentral/newsreader/view_thread/100236/
[3] http://grooovygeorge.wordpress.com/2012/03/20/groovy-no-need-to-use-substring/
fuzzytalker兄好久不见了,这次是一篇很棒的工具文章。
嘿嘿,不过貌似排版问题很严重,有办法吗?
我这边看上去没什么问题。就是第一段程序注释太长换行了有点奇怪。