From 891b2762ee13fd77c19c36c5c5b93647f7ecdf34 Mon Sep 17 00:00:00 2001 From: Huan Du Date: Fri, 9 Jan 2015 12:41:17 +0800 Subject: [PATCH] refs #18, #9. insert str by rune index; slice supports negative end. --- convert.go | 9 ++++++--- manipulate.go | 26 +++++++++++++++++++++++--- manipulate_test.go | 28 ++++++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 6 deletions(-) diff --git a/convert.go b/convert.go index 2cb2d17..0f22955 100644 --- a/convert.go +++ b/convert.go @@ -12,9 +12,12 @@ import ( // ToCamelCase can convert all lower case characters behind underscores // to upper case character. // Underscore character will be removed in result except following cases. -// * More than 1 underscore, e.g. "a__b" => "A_B" -// * At the beginning of string, e.g. "_a" => "_A" -// * At the end of string, e.g. "ab_" => "Ab_" +// * More than 1 underscore. +// "a__b" => "A_B" +// * At the beginning of string. +// "_a" => "_A" +// * At the end of string. +// "ab_" => "Ab_" func ToCamelCase(str string) string { if len(str) == 0 { return "" diff --git a/manipulate.go b/manipulate.go index c4cf25e..5e6bdf3 100644 --- a/manipulate.go +++ b/manipulate.go @@ -27,18 +27,26 @@ func Reverse(str string) string { } // Slice a string by rune. -// Start and end must satisfy 0 <= start <= end <= rune length. +// +// Start must satisfy 0 <= start <= rune length. +// +// End can be positive, zero or negative. +// If end >= 0, start and end must satisfy start <= end <= rune length. +// If end < 0, it means slice to the end of string. +// // Otherwise, Slice will panic as out of range. func Slice(str string, start, end int) string { var size, startPos, endPos int origin := str - if start < 0 || end > len(str) || start > end { + if start < 0 || end > len(str) || (end >= 0 && start > end) { panic("out of range") } - end -= start + if end >= 0 { + end -= start + } for start > 0 && len(str) > 0 { _, size = utf8.DecodeRuneInString(str) @@ -47,6 +55,10 @@ func Slice(str string, start, end int) string { str = str[size:] } + if end < 0 { + return origin[startPos:] + } + endPos = startPos for end > 0 && len(str) > 0 { @@ -98,3 +110,11 @@ func LastPartition(str, sep string) []string { return []string{str[:index], sep, str[index+len(sep):]} } + +// Insert src into dst at given rune index. +// Index is counted by runes instead of bytes. +// +// If index is out of range of dst, panic with out of range. +func Insert(dst, src string, index int) string { + return Slice(dst, 0, index) + src + Slice(dst, index, -1) +} diff --git a/manipulate_test.go b/manipulate_test.go index 3ff9595..7412070 100644 --- a/manipulate_test.go +++ b/manipulate_test.go @@ -39,6 +39,8 @@ func TestSlice(t *testing.T) { sep("中en文混~排总是少不了的a", "2", "8"): "n文混~排总", sep("中en文混~排总是少不了的a", "0", "0"): "", sep("中en文混~排总是少不了的a", "14", "14"): "", + sep("中en文混~排总是少不了的a", "5", "-1"): "~排总是少不了的a", + sep("中en文混~排总是少不了的a", "14", "-1"): "", sep("let us slice out of range", "-3", "3"): "out of range", sep("超出范围哦", "2", "6"): "out of range", @@ -86,3 +88,29 @@ func TestLastPartition(t *testing.T) { sep("来ge混排ba", "e 混"): sep("", "", "来ge混排ba"), }) } + +func TestInsert(t *testing.T) { + InsertRunner := func(str string) (result string) { + defer func() { + if e := recover(); e != nil { + result = e.(string) + } + }() + + strs := split(str) + index, _ := strconv.ParseInt(strs[2], 10, 0) + result = Insert(strs[0], strs[1], int(index)) + return + } + + runTestCases(t, InsertRunner, _M{ + sep("abcdefg", "hi", "3"): "abchidefg", + sep("少量中文是必须的", "混pai", "4"): "少量中文混pai是必须的", + sep("zh英文hun排", "~!", "5"): "zh英文h~!un排", + sep("插在begining", "我", "0"): "我插在begining", + sep("插在ending", "我", "8"): "插在ending我", + + sep("超tian出yuan边tu界po", "foo", "-1"): "out of range", + sep("超tian出yuan边tu界po", "foo", "17"): "out of range", + }) +}