]> Cypherpunks.ru repositories - gostls13.git/commitdiff
bytes, strings: add Cut
authorRuss Cox <rsc@golang.org>
Tue, 21 Sep 2021 14:59:16 +0000 (10:59 -0400)
committerRuss Cox <rsc@golang.org>
Wed, 6 Oct 2021 15:53:00 +0000 (15:53 +0000)
Using Cut is a clearer way to write the vast majority (>70%)
of existing code that calls Index, IndexByte, IndexRune, and SplitN.
There is more discussion on https://golang.org/issue/46336.

Fixes #46336.

Change-Id: Ia418ed7c3706c65bf61e1b2c5baf534cb783e4d3
Reviewed-on: https://go-review.googlesource.com/c/go/+/351710
Trust: Russ Cox <rsc@golang.org>
Run-TryBot: Russ Cox <rsc@golang.org>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
src/bytes/bytes.go
src/bytes/bytes_test.go
src/bytes/example_test.go
src/strings/example_test.go
src/strings/strings.go
src/strings/strings_test.go

index cd859d086db09228f201ae3a32cba7cb080f9738..a9f10031c4a4eb34bedc4be995daf548b2835256 100644 (file)
@@ -1192,3 +1192,16 @@ func Index(s, sep []byte) int {
        }
        return -1
 }
+
+// Cut slices s around the first 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, cut returns s, "", false.
+//
+// Cut returns slices of the original slice s, not copies.
+func Cut(s, sep []byte) (before, after []byte, found bool) {
+       if i := Index(s, sep); i >= 0 {
+               return s[:i], s[i+len(sep):], true
+       }
+       return s, nil, false
+}
index 850b2ed061d8b1540a2d166c30b073be6dfac4bc..3bece6adf0b905b63f580db349c5ee0cf007b89b 100644 (file)
@@ -1567,6 +1567,29 @@ func TestEqualFold(t *testing.T) {
        }
 }
 
+var cutTests = []struct {
+       s, sep        string
+       before, after string
+       found         bool
+}{
+       {"abc", "b", "a", "c", true},
+       {"abc", "a", "", "bc", true},
+       {"abc", "c", "ab", "", true},
+       {"abc", "abc", "", "", true},
+       {"abc", "", "", "abc", true},
+       {"abc", "d", "abc", "", false},
+       {"", "d", "", "", false},
+       {"", "", "", "", true},
+}
+
+func TestCut(t *testing.T) {
+       for _, tt := range cutTests {
+               if before, after, found := Cut([]byte(tt.s), []byte(tt.sep)); string(before) != tt.before || string(after) != tt.after || found != tt.found {
+                       t.Errorf("Cut(%q, %q) = %q, %q, %v, want %q, %q, %v", tt.s, tt.sep, before, after, found, tt.before, tt.after, tt.found)
+               }
+       }
+}
+
 func TestBufferGrowNegative(t *testing.T) {
        defer func() {
                if err := recover(); err == nil {
index d0d4dd2c2d43b9f3a47e2292dd296b22c266787c..d04b088fabbc4b683dd372d0acac9f3bacdf796d 100644 (file)
@@ -105,36 +105,6 @@ func ExampleCompare_search() {
        }
 }
 
-func ExampleTrimSuffix() {
-       var b = []byte("Hello, goodbye, etc!")
-       b = bytes.TrimSuffix(b, []byte("goodbye, etc!"))
-       b = bytes.TrimSuffix(b, []byte("gopher"))
-       b = append(b, bytes.TrimSuffix([]byte("world!"), []byte("x!"))...)
-       os.Stdout.Write(b)
-       // Output: Hello, world!
-}
-
-func ExampleTrimPrefix() {
-       var b = []byte("Goodbye,, world!")
-       b = bytes.TrimPrefix(b, []byte("Goodbye,"))
-       b = bytes.TrimPrefix(b, []byte("See ya,"))
-       fmt.Printf("Hello%s", b)
-       // Output: Hello, world!
-}
-
-func ExampleFields() {
-       fmt.Printf("Fields are: %q", bytes.Fields([]byte("  foo bar  baz   ")))
-       // Output: Fields are: ["foo" "bar" "baz"]
-}
-
-func ExampleFieldsFunc() {
-       f := func(c rune) bool {
-               return !unicode.IsLetter(c) && !unicode.IsNumber(c)
-       }
-       fmt.Printf("Fields are: %q", bytes.FieldsFunc([]byte("  foo1;bar2,baz3..."), f))
-       // Output: Fields are: ["foo1" "bar2" "baz3"]
-}
-
 func ExampleContains() {
        fmt.Println(bytes.Contains([]byte("seafood"), []byte("foo")))
        fmt.Println(bytes.Contains([]byte("seafood"), []byte("bar")))
@@ -181,6 +151,22 @@ func ExampleCount() {
        // 5
 }
 
+func ExampleCut() {
+       show := func(s, sep string) {
+               before, after, found := bytes.Cut([]byte(s), []byte(sep))
+               fmt.Printf("Cut(%q, %q) = %q, %q, %v\n", s, sep, before, after, found)
+       }
+       show("Gopher", "Go")
+       show("Gopher", "ph")
+       show("Gopher", "er")
+       show("Gopher", "Badger")
+       // Output:
+       // Cut("Gopher", "Go") = "", "pher", true
+       // Cut("Gopher", "ph") = "Go", "er", true
+       // Cut("Gopher", "er") = "Goph", "", true
+       // Cut("Gopher", "Badger") = "Gopher", "", false
+}
+
 func ExampleEqual() {
        fmt.Println(bytes.Equal([]byte("Go"), []byte("Go")))
        fmt.Println(bytes.Equal([]byte("Go"), []byte("C++")))
@@ -194,6 +180,19 @@ func ExampleEqualFold() {
        // Output: true
 }
 
+func ExampleFields() {
+       fmt.Printf("Fields are: %q", bytes.Fields([]byte("  foo bar  baz   ")))
+       // Output: Fields are: ["foo" "bar" "baz"]
+}
+
+func ExampleFieldsFunc() {
+       f := func(c rune) bool {
+               return !unicode.IsLetter(c) && !unicode.IsNumber(c)
+       }
+       fmt.Printf("Fields are: %q", bytes.FieldsFunc([]byte("  foo1;bar2,baz3..."), f))
+       // Output: Fields are: ["foo1" "bar2" "baz3"]
+}
+
 func ExampleHasPrefix() {
        fmt.Println(bytes.HasPrefix([]byte("Gopher"), []byte("Go")))
        fmt.Println(bytes.HasPrefix([]byte("Gopher"), []byte("C")))
@@ -259,6 +258,12 @@ func ExampleIndexRune() {
        // -1
 }
 
+func ExampleJoin() {
+       s := [][]byte{[]byte("foo"), []byte("bar"), []byte("baz")}
+       fmt.Printf("%s", bytes.Join(s, []byte(", ")))
+       // Output: foo, bar, baz
+}
+
 func ExampleLastIndex() {
        fmt.Println(bytes.Index([]byte("go gopher"), []byte("go")))
        fmt.Println(bytes.LastIndex([]byte("go gopher"), []byte("go")))
@@ -299,10 +304,12 @@ func ExampleLastIndexFunc() {
        // -1
 }
 
-func ExampleJoin() {
-       s := [][]byte{[]byte("foo"), []byte("bar"), []byte("baz")}
-       fmt.Printf("%s", bytes.Join(s, []byte(", ")))
-       // Output: foo, bar, baz
+func ExampleReader_Len() {
+       fmt.Println(bytes.NewReader([]byte("Hi!")).Len())
+       fmt.Println(bytes.NewReader([]byte("こんにちは!")).Len())
+       // Output:
+       // 3
+       // 16
 }
 
 func ExampleRepeat() {
@@ -412,20 +419,6 @@ func ExampleTrimFunc() {
        // go-gopher!
 }
 
-func ExampleMap() {
-       rot13 := func(r rune) rune {
-               switch {
-               case r >= 'A' && r <= 'Z':
-                       return 'A' + (r-'A'+13)%26
-               case r >= 'a' && r <= 'z':
-                       return 'a' + (r-'a'+13)%26
-               }
-               return r
-       }
-       fmt.Printf("%s", bytes.Map(rot13, []byte("'Twas brillig and the slithy gopher...")))
-       // Output: 'Gjnf oevyyvt naq gur fyvgul tbcure...
-}
-
 func ExampleTrimLeft() {
        fmt.Print(string(bytes.TrimLeft([]byte("453gopher8257"), "0123456789")))
        // Output:
@@ -442,11 +435,28 @@ func ExampleTrimLeftFunc() {
        // go-gopher!567
 }
 
+func ExampleTrimPrefix() {
+       var b = []byte("Goodbye,, world!")
+       b = bytes.TrimPrefix(b, []byte("Goodbye,"))
+       b = bytes.TrimPrefix(b, []byte("See ya,"))
+       fmt.Printf("Hello%s", b)
+       // Output: Hello, world!
+}
+
 func ExampleTrimSpace() {
        fmt.Printf("%s", bytes.TrimSpace([]byte(" \t\n a lone gopher \n\t\r\n")))
        // Output: a lone gopher
 }
 
+func ExampleTrimSuffix() {
+       var b = []byte("Hello, goodbye, etc!")
+       b = bytes.TrimSuffix(b, []byte("goodbye, etc!"))
+       b = bytes.TrimSuffix(b, []byte("gopher"))
+       b = append(b, bytes.TrimSuffix([]byte("world!"), []byte("x!"))...)
+       os.Stdout.Write(b)
+       // Output: Hello, world!
+}
+
 func ExampleTrimRight() {
        fmt.Print(string(bytes.TrimRight([]byte("453gopher8257"), "0123456789")))
        // Output:
@@ -463,21 +473,6 @@ func ExampleTrimRightFunc() {
        // 1234go-gopher!
 }
 
-func ExampleToUpper() {
-       fmt.Printf("%s", bytes.ToUpper([]byte("Gopher")))
-       // Output: GOPHER
-}
-
-func ExampleToUpperSpecial() {
-       str := []byte("ahoj vývojári golang")
-       totitle := bytes.ToUpperSpecial(unicode.AzeriCase, str)
-       fmt.Println("Original : " + string(str))
-       fmt.Println("ToUpper : " + string(totitle))
-       // Output:
-       // Original : ahoj vývojári golang
-       // ToUpper : AHOJ VÝVOJÁRİ GOLANG
-}
-
 func ExampleToLower() {
        fmt.Printf("%s", bytes.ToLower([]byte("Gopher")))
        // Output: gopher
@@ -493,10 +488,17 @@ func ExampleToLowerSpecial() {
        // ToLower : ahoj vývojári golang
 }
 
-func ExampleReader_Len() {
-       fmt.Println(bytes.NewReader([]byte("Hi!")).Len())
-       fmt.Println(bytes.NewReader([]byte("こんにちは!")).Len())
+func ExampleToUpper() {
+       fmt.Printf("%s", bytes.ToUpper([]byte("Gopher")))
+       // Output: GOPHER
+}
+
+func ExampleToUpperSpecial() {
+       str := []byte("ahoj vývojári golang")
+       totitle := bytes.ToUpperSpecial(unicode.AzeriCase, str)
+       fmt.Println("Original : " + string(str))
+       fmt.Println("ToUpper : " + string(totitle))
        // Output:
-       // 3
-       // 16
+       // Original : ahoj vývojári golang
+       // ToUpper : AHOJ VÝVOJÁRİ GOLANG
 }
index 375f9cac6502e89a0124315a04c1700b8cbd13ae..94aa167f90900c57be37a90d484455f88c7e5468 100644 (file)
@@ -10,17 +10,15 @@ import (
        "unicode"
 )
 
-func ExampleFields() {
-       fmt.Printf("Fields are: %q", strings.Fields("  foo bar  baz   "))
-       // Output: Fields are: ["foo" "bar" "baz"]
-}
-
-func ExampleFieldsFunc() {
-       f := func(c rune) bool {
-               return !unicode.IsLetter(c) && !unicode.IsNumber(c)
+func ExampleBuilder() {
+       var b strings.Builder
+       for i := 3; i >= 1; i-- {
+               fmt.Fprintf(&b, "%d...", i)
        }
-       fmt.Printf("Fields are: %q", strings.FieldsFunc("  foo1;bar2,baz3...", f))
-       // Output: Fields are: ["foo1" "bar2" "baz3"]
+       b.WriteString("ignition")
+       fmt.Println(b.String())
+
+       // Output: 3...2...1...ignition
 }
 
 func ExampleCompare() {
@@ -79,11 +77,40 @@ func ExampleCount() {
        // 5
 }
 
+func ExampleCut() {
+       show := func(s, sep string) {
+               before, after, found := strings.Cut(s, sep)
+               fmt.Printf("Cut(%q, %q) = %q, %q, %v\n", s, sep, before, after, found)
+       }
+       show("Gopher", "Go")
+       show("Gopher", "ph")
+       show("Gopher", "er")
+       show("Gopher", "Badger")
+       // Output:
+       // Cut("Gopher", "Go") = "", "pher", true
+       // Cut("Gopher", "ph") = "Go", "er", true
+       // Cut("Gopher", "er") = "Goph", "", true
+       // Cut("Gopher", "Badger") = "Gopher", "", false
+}
+
 func ExampleEqualFold() {
        fmt.Println(strings.EqualFold("Go", "go"))
        // Output: true
 }
 
+func ExampleFields() {
+       fmt.Printf("Fields are: %q", strings.Fields("  foo bar  baz   "))
+       // Output: Fields are: ["foo" "bar" "baz"]
+}
+
+func ExampleFieldsFunc() {
+       f := func(c rune) bool {
+               return !unicode.IsLetter(c) && !unicode.IsNumber(c)
+       }
+       fmt.Printf("Fields are: %q", strings.FieldsFunc("  foo1;bar2,baz3...", f))
+       // Output: Fields are: ["foo1" "bar2" "baz3"]
+}
+
 func ExampleHasPrefix() {
        fmt.Println(strings.HasPrefix("Gopher", "Go"))
        fmt.Println(strings.HasPrefix("Gopher", "C"))
@@ -370,14 +397,3 @@ func ExampleTrimRightFunc() {
        }))
        // Output: ¡¡¡Hello, Gophers
 }
-
-func ExampleBuilder() {
-       var b strings.Builder
-       for i := 3; i >= 1; i-- {
-               fmt.Fprintf(&b, "%d...", i)
-       }
-       b.WriteString("ignition")
-       fmt.Println(b.String())
-
-       // Output: 3...2...1...ignition
-}
index 0df8d2eb281dc45658fc0ebfcc10201e83c19ef4..4b543dcc1acaed619cb97282dedb5ae199e2316e 100644 (file)
@@ -1118,3 +1118,14 @@ func Index(s, substr string) int {
        }
        return -1
 }
+
+// Cut slices s around the first 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, cut returns s, "", false.
+func Cut(s, sep string) (before, after string, found bool) {
+       if i := Index(s, sep); i >= 0 {
+               return s[:i], s[i+len(sep):], true
+       }
+       return s, "", false
+}
index edc6c205907d3ab0e64f51a36b1499208564414f..0f30ca738e6754728fe28fe66e4ed046d08afe89 100644 (file)
@@ -1579,7 +1579,30 @@ var CountTests = []struct {
 func TestCount(t *testing.T) {
        for _, tt := range CountTests {
                if num := Count(tt.s, tt.sep); num != tt.num {
-                       t.Errorf("Count(\"%s\", \"%s\") = %d, want %d", tt.s, tt.sep, num, tt.num)
+                       t.Errorf("Count(%q, %q) = %d, want %d", tt.s, tt.sep, num, tt.num)
+               }
+       }
+}
+
+var cutTests = []struct {
+       s, sep        string
+       before, after string
+       found         bool
+}{
+       {"abc", "b", "a", "c", true},
+       {"abc", "a", "", "bc", true},
+       {"abc", "c", "ab", "", true},
+       {"abc", "abc", "", "", true},
+       {"abc", "", "", "abc", true},
+       {"abc", "d", "abc", "", false},
+       {"", "d", "", "", false},
+       {"", "", "", "", true},
+}
+
+func TestCut(t *testing.T) {
+       for _, tt := range cutTests {
+               if before, after, found := Cut(tt.s, tt.sep); before != tt.before || after != tt.after || found != tt.found {
+                       t.Errorf("Cut(%q, %q) = %q, %q, %v, want %q, %q, %v", tt.s, tt.sep, before, after, found, tt.before, tt.after, tt.found)
                }
        }
 }