aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--api/next/71151.txt2
-rw-r--r--doc/next/6-stdlib/99-minor/bytes/71151.md3
-rw-r--r--doc/next/6-stdlib/99-minor/strings/71151.md3
-rw-r--r--src/bytes/bytes.go13
-rw-r--r--src/bytes/bytes_test.go21
-rw-r--r--src/strings/strings.go11
-rw-r--r--src/strings/strings_test.go21
7 files changed, 74 insertions, 0 deletions
diff --git a/api/next/71151.txt b/api/next/71151.txt
new file mode 100644
index 0000000000..30ed124879
--- /dev/null
+++ b/api/next/71151.txt
@@ -0,0 +1,2 @@
+pkg bytes, func CutLast([]uint8, []uint8) ([]uint8, []uint8, bool) #71151
+pkg strings, func CutLast(string, string) (string, string, bool) #71151
diff --git a/doc/next/6-stdlib/99-minor/bytes/71151.md b/doc/next/6-stdlib/99-minor/bytes/71151.md
new file mode 100644
index 0000000000..a86361ef5a
--- /dev/null
+++ b/doc/next/6-stdlib/99-minor/bytes/71151.md
@@ -0,0 +1,3 @@
+The new [CutLast] function slices a []byte
+around the last occurrence of a separator.
+It can replace and simplify some common uses of LastIndex.
diff --git a/doc/next/6-stdlib/99-minor/strings/71151.md b/doc/next/6-stdlib/99-minor/strings/71151.md
new file mode 100644
index 0000000000..b979f84553
--- /dev/null
+++ b/doc/next/6-stdlib/99-minor/strings/71151.md
@@ -0,0 +1,3 @@
+The new [CutLast] function slices a string
+around the last occurrence of a separator.
+It can replace and simplify some common uses of LastIndex. \ No newline at end of file
diff --git a/src/bytes/bytes.go b/src/bytes/bytes.go
index 98f78f10c3..2bd1284296 100644
--- a/src/bytes/bytes.go
+++ b/src/bytes/bytes.go
@@ -1422,3 +1422,16 @@ func CutSuffix(s, suffix []byte) (before []byte, found bool) {
}
return s[:len(s)-len(suffix)], true
}
+
+// CutLast slices s around the last instance of sep,
+// returning the text before and after sep.
+// The found result reports whether sep appears in s.
+// If sep does not appear in s, CutLast returns s, nil, false.
+//
+// CutLast returns slices of the original slice s, not copies.
+func CutLast(s, sep []byte) (before, after []byte, found bool) {
+ if i := LastIndex(s, sep); i >= 0 {
+ return s[:i], s[i+len(sep):], true
+ }
+ return s, nil, false
+}
diff --git a/src/bytes/bytes_test.go b/src/bytes/bytes_test.go
index 07280464d2..20d333217e 100644
--- a/src/bytes/bytes_test.go
+++ b/src/bytes/bytes_test.go
@@ -1976,6 +1976,27 @@ func TestCut(t *testing.T) {
}
}
+func TestCutLast(t *testing.T) {
+ tests := []struct {
+ s, sep string
+ before, after string
+ found bool
+ }{
+ {"a/b/c", "/", "a/b", "c", true},
+ {"a//b//c", "//", "a//b", "c", true},
+ {"abc", "/", "abc", "", false},
+ {"abc", "", "abc", "", true},
+ {"", "", "", "", true},
+ {"/abc", "/", "", "abc", true},
+ {"abc/", "/", "abc", "", true},
+ }
+ for _, tt := range tests {
+ if before, after, found := CutLast([]byte(tt.s), []byte(tt.sep)); string(before) != tt.before || string(after) != tt.after || found != tt.found {
+ t.Errorf("CutLast(%q, %q) = %q, %q, %v; want %q, %q, %v", tt.s, tt.sep, before, after, found, tt.before, tt.after, tt.found)
+ }
+ }
+}
+
var cutPrefixTests = []struct {
s, sep string
after string
diff --git a/src/strings/strings.go b/src/strings/strings.go
index 367e0a8e24..70297f1e69 100644
--- a/src/strings/strings.go
+++ b/src/strings/strings.go
@@ -1289,3 +1289,14 @@ func CutPrefix(s, prefix string) (after string, found bool) {
func CutSuffix(s, suffix string) (before string, found bool) {
return stringslite.CutSuffix(s, suffix)
}
+
+// CutLast slices s around the last instance of sep,
+// returning the text before and after sep.
+// The found result reports whether sep appears in s.
+// If sep does not appear in s, CutLast returns s, "", false.
+func CutLast(s, sep string) (before, after string, found bool) {
+ if i := LastIndex(s, sep); i >= 0 {
+ return s[:i], s[i+len(sep):], true
+ }
+ return s, "", false
+}
diff --git a/src/strings/strings_test.go b/src/strings/strings_test.go
index edfeb0e813..4ff3a2a825 100644
--- a/src/strings/strings_test.go
+++ b/src/strings/strings_test.go
@@ -1826,6 +1826,27 @@ func TestCut(t *testing.T) {
}
}
+func TestCutLast(t *testing.T) {
+ tests := []struct {
+ s, sep string
+ before, after string
+ found bool
+ }{
+ {"a/b/c", "/", "a/b", "c", true},
+ {"a//b//c", "//", "a//b", "c", true},
+ {"abc", "/", "abc", "", false},
+ {"abc", "", "abc", "", true},
+ {"", "", "", "", true},
+ {"/abc", "/", "", "abc", true},
+ {"abc/", "/", "abc", "", true},
+ }
+ for _, tt := range tests {
+ if before, after, found := CutLast(tt.s, tt.sep); before != tt.before || after != tt.after || found != tt.found {
+ t.Errorf("CutLast(%q, %q) = %q, %q, %v; want %q, %q, %v", tt.s, tt.sep, before, after, found, tt.before, tt.after, tt.found)
+ }
+ }
+}
+
var cutPrefixTests = []struct {
s, sep string
after string