1
0
mirror of https://github.com/golang/go synced 2024-11-25 09:57:57 -07:00

strings: add LastIndexAny

The need for a LastIndexAny function has come up in the discussion
for https://golang.org/cl/3008041/. This function is
implemented analogously to lastIndexFunc, using functions from
the utf8 package.

R=r, rsc, PeterGo
CC=golang-dev
https://golang.org/cl/3057041
This commit is contained in:
Benny Siegert 2010-11-12 12:47:50 -08:00 committed by Rob Pike
parent f0d174b776
commit 8530e8ef65
4 changed files with 79 additions and 9 deletions

View File

@ -165,6 +165,25 @@ func IndexAny(s []byte, chars string) int {
return -1 return -1
} }
// LastIndexAny interprets s as a sequence of UTF-8-encoded Unicode code
// points. It returns the byte index of the last occurrence in s of any of
// the Unicode code points in chars. It returns -1 if chars is empty or if
// there is no code point in common.
func LastIndexAny(s []byte, chars string) int {
if len(chars) > 0 {
for i := len(s); i > 0; {
rune, size := utf8.DecodeLastRune(s[0:i])
i -= size
for _, m := range chars {
if rune == m {
return i
}
}
}
}
return -1
}
// Generic split: splits after each instance of sep, // Generic split: splits after each instance of sep,
// including sepSave bytes of sep in the subarrays. // including sepSave bytes of sep in the subarrays.
func genSplit(s, sep []byte, sepSave, n int) [][]byte { func genSplit(s, sep []byte, sepSave, n int) [][]byte {

View File

@ -128,6 +128,20 @@ var indexAnyTests = []BinOpTest{
{dots + dots + dots, " ", -1}, {dots + dots + dots, " ", -1},
} }
var lastIndexAnyTests = []BinOpTest{
{"", "", -1},
{"", "a", -1},
{"", "abc", -1},
{"a", "", -1},
{"a", "a", 0},
{"aaa", "a", 2},
{"abc", "xyz", -1},
{"abc", "ab", 1},
{"a☺b☻c☹d", "uvw☻xyz", 2 + len("☺")},
{"a.RegExp*", ".(|)*+?^$[]", 8},
{dots + dots + dots, " ", -1},
}
var indexRuneTests = []BinOpTest{ var indexRuneTests = []BinOpTest{
{"", "a", -1}, {"", "a", -1},
{"", "☺", -1}, {"", "☺", -1},
@ -150,16 +164,21 @@ func runIndexTests(t *testing.T, f func(s, sep []byte) int, funcName string, tes
} }
} }
func runIndexAnyTests(t *testing.T, f func(s []byte, chars string) int, funcName string, testCases []BinOpTest) {
for _, test := range testCases {
a := []byte(test.a)
actual := f(a, test.b)
if actual != test.i {
t.Errorf("%s(%q,%q) = %v; want %v", funcName, a, test.b, actual, test.i)
}
}
}
func TestIndex(t *testing.T) { runIndexTests(t, Index, "Index", indexTests) } func TestIndex(t *testing.T) { runIndexTests(t, Index, "Index", indexTests) }
func TestLastIndex(t *testing.T) { runIndexTests(t, LastIndex, "LastIndex", lastIndexTests) } func TestLastIndex(t *testing.T) { runIndexTests(t, LastIndex, "LastIndex", lastIndexTests) }
func TestIndexAny(t *testing.T) { func TestIndexAny(t *testing.T) { runIndexAnyTests(t, IndexAny, "IndexAny", indexAnyTests) }
for _, test := range indexAnyTests { func TestLastIndexAny(t *testing.T) {
a := []byte(test.a) runIndexAnyTests(t, LastIndexAny, "LastIndexAny", lastIndexAnyTests)
actual := IndexAny(a, test.b)
if actual != test.i {
t.Errorf("IndexAny(%q,%q) = %v; want %v", a, test.b, actual, test.i)
}
}
} }
func TestIndexByte(t *testing.T) { func TestIndexByte(t *testing.T) {

View File

@ -142,6 +142,24 @@ func IndexAny(s, chars string) int {
return -1 return -1
} }
// LastIndexAny returns the index of the last instance of any Unicode code
// point from chars in s, or -1 if no Unicode code point from chars is
// present in s.
func LastIndexAny(s, chars string) int {
if len(chars) > 0 {
for i := len(s); i > 0; {
rune, size := utf8.DecodeLastRuneInString(s[0:i])
i -= size
for _, m := range chars {
if rune == m {
return i
}
}
}
}
return -1
}
// Generic split: splits after each instance of sep, // Generic split: splits after each instance of sep,
// including sepSave bytes of sep in the subarrays. // including sepSave bytes of sep in the subarrays.
func genSplit(s, sep string, sepSave, n int) []string { func genSplit(s, sep string, sepSave, n int) []string {

View File

@ -86,6 +86,19 @@ var indexAnyTests = []IndexTest{
{"aRegExp*", ".(|)*+?^$[]", 7}, {"aRegExp*", ".(|)*+?^$[]", 7},
{dots + dots + dots, " ", -1}, {dots + dots + dots, " ", -1},
} }
var lastIndexAnyTests = []IndexTest{
{"", "", -1},
{"", "a", -1},
{"", "abc", -1},
{"a", "", -1},
{"a", "a", 0},
{"aaa", "a", 2},
{"abc", "xyz", -1},
{"abc", "ab", 1},
{"a☺b☻c☹d", "uvw☻xyz", 2 + len("☺")},
{"a.RegExp*", ".(|)*+?^$[]", 8},
{dots + dots + dots, " ", -1},
}
// Execute f on each test case. funcName should be the name of f; it's used // Execute f on each test case. funcName should be the name of f; it's used
// in failure reports. // in failure reports.
@ -101,6 +114,7 @@ func runIndexTests(t *testing.T, f func(s, sep string) int, funcName string, tes
func TestIndex(t *testing.T) { runIndexTests(t, Index, "Index", indexTests) } func TestIndex(t *testing.T) { runIndexTests(t, Index, "Index", indexTests) }
func TestLastIndex(t *testing.T) { runIndexTests(t, LastIndex, "LastIndex", lastIndexTests) } func TestLastIndex(t *testing.T) { runIndexTests(t, LastIndex, "LastIndex", lastIndexTests) }
func TestIndexAny(t *testing.T) { runIndexTests(t, IndexAny, "IndexAny", indexAnyTests) } func TestIndexAny(t *testing.T) { runIndexTests(t, IndexAny, "IndexAny", indexAnyTests) }
func TestLastIndexAny(t *testing.T) { runIndexTests(t, LastIndexAny, "LastIndexAny", lastIndexAnyTests) }
type ExplodeTest struct { type ExplodeTest struct {
s string s string