mirror of
				https://github.com/golang/go.git
				synced 2025-10-31 00:30:57 +00:00 
			
		
		
		
	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
This commit is contained in:
		
							parent
							
								
									fe14ee52cc
								
							
						
					
					
						commit
						e515d80d5d
					
				
					 21 changed files with 117 additions and 54 deletions
				
			
		|  | @ -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) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Brad Fitzpatrick
						Brad Fitzpatrick