Commit e515d80d authored by Brad Fitzpatrick's avatar Brad Fitzpatrick

bytes, strings: add TrimPrefix and TrimSuffix

Everybody either gets confused and thinks this is
TrimLeft/TrimRight or does this by hand which gets
repetitive looking.

R=rsc, kevlar
CC=golang-dev
https://golang.org/cl/7239044
parent fe14ee52
......@@ -778,8 +778,7 @@ func (w *Walker) walkConst(vs *ast.ValueSpec) {
}
}
}
if strings.HasPrefix(litType, constDepPrefix) {
dep := litType[len(constDepPrefix):]
if dep := strings.TrimPrefix(litType, constDepPrefix); dep != litType {
w.constDep[ident.Name] = dep
continue
}
......
......@@ -1542,8 +1542,8 @@ func godefsFields(fld []*ast.Field) {
npad := 0
for _, f := range fld {
for _, n := range f.Names {
if strings.HasPrefix(n.Name, prefix) && n.Name != prefix {
n.Name = n.Name[len(prefix):]
if n.Name != prefix {
n.Name = strings.TrimPrefix(n.Name, prefix)
}
if n.Name == "_" {
// Use exported name instead.
......
......@@ -180,7 +180,7 @@ func (p *Package) cdefs(f *File, srcfile string) string {
for _, line := range lines {
line = strings.TrimSpace(line)
if strings.HasPrefix(line, "type ") && strings.HasSuffix(line, " struct {") {
s := line[len("type ") : len(line)-len(" struct {")]
s := strings.TrimSuffix(strings.TrimPrefix(line, "type "), " struct {")
printf("typedef struct %s %s;\n", s, s)
}
}
......
......@@ -395,9 +395,7 @@ func typecheck1(cfg *TypeConfig, f interface{}, typeof map[interface{}]string, a
// Field or method.
name := n.Sel.Name
if t := typeof[n.X]; t != "" {
if strings.HasPrefix(t, "*") {
t = t[1:] // implicit *
}
t = strings.TrimPrefix(t, "*") // implicit *
if typ := cfg.Type[t]; typ != nil {
if t := typ.dot(cfg, name); t != "" {
typeof[n] = t
......
......@@ -195,9 +195,7 @@ func testFlag(args []string, i int) (f *testFlagSpec, value string, extra bool)
}
name := arg[1:]
// If there's already "test.", drop it for now.
if strings.HasPrefix(name, "test.") {
name = name[5:]
}
name = strings.TrimPrefix(name, "test.")
equals := strings.Index(name, "=")
if equals >= 0 {
value = name[equals+1:]
......
......@@ -229,9 +229,7 @@ func (dir *Directory) lookupLocal(name string) *Directory {
}
func splitPath(p string) []string {
if strings.HasPrefix(p, "/") {
p = p[1:]
}
p = strings.TrimPrefix(p, "/")
if p == "" {
return nil
}
......@@ -310,14 +308,9 @@ func (root *Directory) listing(skipRoot bool) *DirList {
// the path is relative to root.Path - remove the root.Path
// prefix (the prefix should always be present but avoid
// crashes and check)
path := d.Path
if strings.HasPrefix(d.Path, root.Path) {
path = d.Path[len(root.Path):]
}
path := strings.TrimPrefix(d.Path, root.Path)
// remove leading separator if any - path must be relative
if len(path) > 0 && path[0] == '/' {
path = path[1:]
}
path = strings.TrimPrefix(path, "/")
p.Path = path
p.Name = d.Name
p.HasPkg = d.HasPkg
......
......@@ -459,9 +459,7 @@ func (ns nameSpace) ReadDir(path string) ([]os.FileInfo, error) {
if hasPathPrefix(old, path) && old != path {
// Find next element after path in old.
elem := old[len(path):]
if strings.HasPrefix(elem, "/") {
elem = elem[1:]
}
elem = strings.TrimPrefix(elem, "/")
if i := strings.Index(elem, "/"); i >= 0 {
elem = elem[:i]
}
......
......@@ -419,9 +419,7 @@ func pkgLinkFunc(path string) string {
relpath := path[1:]
// because of the irregular mapping under goroot
// we need to correct certain relative paths
if strings.HasPrefix(relpath, "src/pkg/") {
relpath = relpath[len("src/pkg/"):]
}
relpath = strings.TrimPrefix(relpath, "src/pkg/")
return pkgHandler.pattern[1:] + relpath // remove trailing '/' for relative URL
}
......
......@@ -347,7 +347,7 @@ func main() {
fs.Bind(target, OS(path), "/", bindReplace)
abspath = target
} else if strings.HasPrefix(path, cmdPrefix) {
path = path[len(cmdPrefix):]
path = strings.TrimPrefix(path, cmdPrefix)
forceCmd = true
} else if bp, _ := build.Import(path, "", build.FindOnly); bp.Dir != "" && bp.ImportPath != "" {
fs.Bind(target, OS(bp.Dir), "/", bindReplace)
......
......@@ -90,9 +90,7 @@ func (f *File) checkCanonicalMethod(id *ast.Ident, t *ast.FuncType) {
fmt.Fprintf(&f.b, "<%s>", err)
}
actual := f.b.String()
if strings.HasPrefix(actual, "func(") {
actual = actual[4:]
}
actual = strings.TrimPrefix(actual, "func(")
actual = id.Name + actual
f.Warnf(id.Pos(), "method %s should have signature %s", actual, expectFmt)
......
......@@ -515,6 +515,24 @@ func TrimFunc(s []byte, f func(r rune) bool) []byte {
return TrimRightFunc(TrimLeftFunc(s, f), f)
}
// TrimPrefix returns s without the provided leading prefix string.
// If s doesn't start with prefix, s is returned unchanged.
func TrimPrefix(s, prefix []byte) []byte {
if HasPrefix(s, prefix) {
return s[len(prefix):]
}
return s
}
// TrimSuffix returns s without the provided trailing suffix string.
// If s doesn't end with suffix, s is returned unchanged.
func TrimSuffix(s, suffix []byte) []byte {
if HasSuffix(s, suffix) {
return s[:len(s)-len(suffix)]
}
return s
}
// IndexFunc interprets s as a sequence of UTF-8-encoded Unicode code points.
// It returns the byte index in s of the first Unicode
// code point satisfying f(c), or -1 if none do.
......
......@@ -794,8 +794,8 @@ func TestRunes(t *testing.T) {
}
type TrimTest struct {
f string
in, cutset, out string
f string
in, arg, out string
}
var trimTests = []TrimTest{
......@@ -820,12 +820,17 @@ var trimTests = []TrimTest{
{"TrimRight", "", "123", ""},
{"TrimRight", "", "", ""},
{"TrimRight", "☺\xc0", "☺", "☺\xc0"},
{"TrimPrefix", "aabb", "a", "abb"},
{"TrimPrefix", "aabb", "b", "aabb"},
{"TrimSuffix", "aabb", "a", "aabb"},
{"TrimSuffix", "aabb", "b", "aab"},
}
func TestTrim(t *testing.T) {
for _, tc := range trimTests {
name := tc.f
var f func([]byte, string) []byte
var fb func([]byte, []byte) []byte
switch name {
case "Trim":
f = Trim
......@@ -833,12 +838,21 @@ func TestTrim(t *testing.T) {
f = TrimLeft
case "TrimRight":
f = TrimRight
case "TrimPrefix":
fb = TrimPrefix
case "TrimSuffix":
fb = TrimSuffix
default:
t.Errorf("Undefined trim function %s", name)
}
actual := string(f([]byte(tc.in), tc.cutset))
var actual string
if f != nil {
actual = string(f([]byte(tc.in), tc.arg))
} else {
actual = string(fb([]byte(tc.in), []byte(tc.arg)))
}
if actual != tc.out {
t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.cutset, actual, tc.out)
t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.arg, actual, tc.out)
}
}
}
......
......@@ -66,3 +66,20 @@ func ExampleCompare_search() {
// Found it!
}
}
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!
}
......@@ -42,10 +42,7 @@ func readParseTest(r *bufio.Reader) (text, want, context string, err error) {
}
b = append(b, line...)
}
text = string(b)
if strings.HasSuffix(text, "\n") {
text = text[:len(text)-1]
}
text = strings.TrimSuffix(string(b), "\n")
b = b[:0]
// Skip the error list.
......
......@@ -551,9 +551,7 @@ func stripCommonPrefix(lines []string) {
}
// Shorten the computed common prefix by the length of
// suffix, if it is found as suffix of the prefix.
if strings.HasSuffix(prefix, string(suffix)) {
prefix = prefix[0 : len(prefix)-len(suffix)]
}
prefix = strings.TrimSuffix(prefix, string(suffix))
}
}
......
......@@ -44,10 +44,7 @@ func FindPkg(path, srcDir string) (filename, id string) {
if bp.PkgObj == "" {
return
}
noext = bp.PkgObj
if strings.HasSuffix(noext, ".a") {
noext = noext[:len(noext)-len(".a")]
}
noext = strings.TrimSuffix(bp.PkgObj, ".a")
case build.IsLocalImport(path):
// "./x" -> "/this/directory/x.ext", "/this/directory/x"
......
......@@ -172,7 +172,7 @@ func (name handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// listing the available profiles.
func Index(w http.ResponseWriter, r *http.Request) {
if strings.HasPrefix(r.URL.Path, "/debug/pprof/") {
name := r.URL.Path[len("/debug/pprof/"):]
name := strings.TrimPrefix(r.URL.Path, "/debug/pprof/")
if name != "" {
handler(name).ServeHTTP(w, r)
return
......
......@@ -198,9 +198,7 @@ func (r *Response) Write(w io.Writer) error {
}
protoMajor, protoMinor := strconv.Itoa(r.ProtoMajor), strconv.Itoa(r.ProtoMinor)
statusCode := strconv.Itoa(r.StatusCode) + " "
if strings.HasPrefix(text, statusCode) {
text = text[len(statusCode):]
}
text = strings.TrimPrefix(text, statusCode)
io.WriteString(w, "HTTP/"+protoMajor+"."+protoMinor+" "+statusCode+text+"\r\n")
// Process Body,ContentLength,Close,Trailer
......
......@@ -179,3 +179,19 @@ func ExampleToLower() {
fmt.Println(strings.ToLower("Gopher"))
// Output: gopher
}
func ExampleTrimSuffix() {
var s = "Hello, goodbye, etc!"
s = strings.TrimSuffix(s, "goodbye, etc!")
s = strings.TrimSuffix(s, "planet")
fmt.Print(s, "world!")
// Output: Hello, world!
}
func ExampleTrimPrefix() {
var s = "Goodbye,, world!"
s = strings.TrimPrefix(s, "Goodbye,")
s = strings.TrimPrefix(s, "Howdy,")
fmt.Print("Hello" + s)
// Output: Hello, world!
}
......@@ -558,6 +558,24 @@ func TrimSpace(s string) string {
return TrimFunc(s, unicode.IsSpace)
}
// TrimPrefix returns s without the provided leading prefix string.
// If s doesn't start with prefix, s is returned unchanged.
func TrimPrefix(s, prefix string) string {
if HasPrefix(s, prefix) {
return s[len(prefix):]
}
return s
}
// TrimSuffix returns s without the provided trailing suffix string.
// If s doesn't end with suffix, s is returned unchanged.
func TrimSuffix(s, suffix string) string {
if HasSuffix(s, suffix) {
return s[:len(s)-len(suffix)]
}
return s
}
// Replace returns a copy of the string s with the first n
// non-overlapping instances of old replaced by new.
// If n < 0, there is no limit on the number of replacements.
......
......@@ -496,8 +496,8 @@ func TestSpecialCase(t *testing.T) {
func TestTrimSpace(t *testing.T) { runStringTests(t, TrimSpace, "TrimSpace", trimSpaceTests) }
var trimTests = []struct {
f string
in, cutset, out string
f string
in, arg, out string
}{
{"Trim", "abba", "a", "bb"},
{"Trim", "abba", "ab", ""},
......@@ -520,6 +520,10 @@ var trimTests = []struct {
{"TrimRight", "", "123", ""},
{"TrimRight", "", "", ""},
{"TrimRight", "☺\xc0", "☺", "☺\xc0"},
{"TrimPrefix", "aabb", "a", "abb"},
{"TrimPrefix", "aabb", "b", "aabb"},
{"TrimSuffix", "aabb", "a", "aabb"},
{"TrimSuffix", "aabb", "b", "aab"},
}
func TestTrim(t *testing.T) {
......@@ -533,12 +537,16 @@ func TestTrim(t *testing.T) {
f = TrimLeft
case "TrimRight":
f = TrimRight
case "TrimPrefix":
f = TrimPrefix
case "TrimSuffix":
f = TrimSuffix
default:
t.Errorf("Undefined trim function %s", name)
}
actual := f(tc.in, tc.cutset)
actual := f(tc.in, tc.arg)
if actual != tc.out {
t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.cutset, actual, tc.out)
t.Errorf("%s(%q, %q) = %q; want %q", name, tc.in, tc.arg, actual, tc.out)
}
}
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment