Strings – This package deals with UTF-8 encoded strings

Functions such as func Contains(S, substr String) bool are used quite often and we don’t want to go into that very much, so let’s take a look at this simple example

Package main import ("strings" "FMT" "OS" "IO /ioutil") fmt.Println(strings.Repeat(str, Println(string.Count("cheese"), string (string.Count("cheese"), string (string.Count("cheese"), string. "e")) //3 fmt.Println(strings.Count("five", "")) //5 fmt.Println(strings.EqualFold("Go", "go")) // true fmt.Println(strings.Contains(str, "China")) // true fmt.Println(strings.ContainsRune(str, Println(strings.Replace(STR, "China", "USA", -1)) //USA, FMT.Printf("Fields are: %q\n", strings.Fields(" foo bar baz ")) //Fields are: ["foo" "bar" "baz"] f := func(c rune) bool { return ! unicode.IsLetter(c) && ! unicode.IsNumber(c) } //Fields are: ["foo1" "bar2" "baz3"] fmt.Printf("Fields are: %q", strings.FieldsFunc(" foo1; bar2,baz3..." , f)) 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.Println(strings.Map(rot13, "'Twas brillig and the slithy gopher..." )) //'Gjnf oevyyvt naq gur fyvgul tbcure... reader := strings.NewReader("widuuv web") fmt.Printf("%#v\n",reader) fmt.Println(reader.Len())//10 n, err := reader.Read(make([]byte, 10)) if err ! Reader1 := strings.NewReader("China, hello, hello, B := make([]byte, 10) if n1, err := reader1.ReadAt(b, 2); err ! = nil {fmt.println (err)} else {fmt.println (string(b[:n1])) //ina, hello} reader2 := strings.newReader ("hello Shanghai China") b1 := make([]byte, 8) n2, _ := reader2.Read(b1) fmt.Println(string(b1[:n2])) //hello sh reader2.Seek(2, 1) n3,_ := reader2.Read(b1) fmt.Println(string(b1[:n3])) //ghai Chi reader3 := strings.NewReader("hello shanghai") b2 :=  make([]byte, 4) n4, _ := reader3.Read(b2) fmt.Println(string(b2[:n4])) //hell reader3.Seek(2, 1) reader3.UnreadByte() n5, _ := reader3.Read(b2) ftt. Println(string(b2[:n5]))// space sh reader4 := strings.NewReader(" Hello aniu") w, _ := os.Create("aniu.txt") defer w.Close() n6, err := reader4.WriteTo(w) if err ! = nil { fmt.Println(err) } fmt.Println(n6) //15 // ---------------Replacer-------------------- r := strings.NewReplacer("<", "&lt;" , ">", "&gt;" ) fmt.Println(r.Replace("This is <b>HTML</b>")) //This is &lt; b&gt; HTML&lt; /b&gt; n7,err := r.WriteString(w, "This is <b>Html</b>!" ) if err ! = nil { fmt.Println(err) } fmt.Println(n7)//32 d, _ := ioutil.ReadFile("aniu.txt") fmt.Println(string(d))//hello aniuThis is &lt; b&gt; Html&lt; /b&gt; ! }Copy the code

aniu.txt

hello aniuThis is &lt; b&gt; Html&lt; /b&gt; !Copy the code

Today, we’ll look at the Builder structure, which is used to efficiently build a string, using the Write method, which minimizes memory copy and uses zero values. But do not copy a Builder with zero values

type Builder struct {
	addr *Builder // of receiver, to detect copies by value
	buf  []byte
}

Copy the code

Why focus on this thing, because it is still more useful, you will meet the probability is relatively high, of course, eventually we have to learn to use it.

// String returns the accumulated string. func (b *Builder) String() string { return *(*string)(unsafe.Pointer(&b.buf)) } // Len returns the number of accumulated bytes; b.Len() == len(b.String()). func (b *Builder) Len() int { return len(b.buf) } // Cap returns the capacity of the builder's underlying byte slice. It is the // total space allocated for the string being built and includes any bytes //  already written. func (b *Builder) Cap() int { return cap(b.buf) } // Reset resets the Builder to be empty. func (b *Builder) Reset() { b.addr = nil b.buf = nil }Copy the code

The above methods are relatively simple ways to implement basic operations and properties,

The following special attention, is also the core function

// Grow grows b's capacity, if necessary, to guarantee space for // another n bytes. After Grow(n), at least n bytes can be written to b // without another allocation. If n is negative, Grow panics. func (b *Builder) Grow(n int) { b.copyCheck() if n < 0 { panic("strings.Builder.Grow: negative count") } if cap(b.buf)-len(b.buf) < n { b.grow(n) } } func (b *Builder) Write(p []byte) (int, error) { b.copyCheck() b.buf = append(b.buf, p...) return len(p), nil } func (b *Builder) WriteByte(c byte) error { b.copyCheck() b.buf = append(b.buf, c) return nil } // WriteRune appends the UTF-8 encoding of Unicode code point r to b's buffer. // It returns the length of r and a nil error. func (b *Builder) WriteRune(r rune) (int, error) { b.copyCheck() if r < utf8.RuneSelf { b.buf = append(b.buf, byte(r)) return 1, nil } l := len(b.buf) if cap(b.buf)-l < utf8.UTFMax { b.grow(utf8.UTFMax) } n := utf8.EncodeRune(b.buf[l:l+utf8.UTFMax],  r) b.buf = b.buf[:l+n] return n, nil } func (b *Builder) WriteString(s string) (int, error) { b.copyCheck() b.buf = append(b.buf, s...) return len(s), nil }Copy the code

Let’s start with the most commonly used, WriteString. It appends data to buffer, and appends the string in a direct way, by append. So, this illustrates one problem, the particular structure of strings. The return value of this method is much simpler, the length of the argument string, and nil.

Now look at the Write method, which does the same thing, except that the argument is a byte array. WriteByte is even simpler, writing a single byte.

The WriteRune method writes the UTF-8 encoded Unicode code point R to buffer. Returns the length of r and nil.

At this point you might think, oh my God, this is nothing, it’s all very simple, it’s all very clear, and it is.

As you’ve probably noticed, WriteString and WriteRune do this before appending data to buffer.

func (b *Builder) copyCheck() { if b.addr == nil { // This hack works around a failing of Go's escape analysis // that was causing b to escape and be heap allocated. // See issue 23382. // TODO: once issue 7921 is fixed, this should be reverted to // just "b.addr = b". b.addr = (*Builder)(noescape(unsafe.Pointer(b))) } else if b.addr ! = b { panic("strings: illegal use of non-zero Builder copied by value") } }Copy the code

Check to see if the Builder has been copied. If the address changes, panic occurs

Finally, if you know exactly how much memory you need before you use it, you can allocate it before you use it. If the current free space is smaller than the parameter n. The grow operation is performed

func (b *Builder) grow(n int) {
	buf := make([]byte, len(b.buf), 2*cap(b.buf)+n)
	copy(buf, b.buf)
	b.buf = buf
}

Copy the code

Apply for space twice the current capacity plus N, and copy data.

This is all about Builder in strings package. Do you notice that this is similar to Buffer in Bytes?

Take a look at the use of this structure in a practical example

// Do executes the request and returns response or error. // func (r CatCountRequest) Do(ctx context.Context, transport Transport) (*Response, error) { var ( method string path strings.Builder params map[string]string ) method = "GET" path.Grow(1 + len("_cat") + 1 + len("count") + 1 + len(strings.Join(r.Index, ","))) path.WriteString("/") path.WriteString("_cat") path.WriteString("/") path.WriteString("count") if len(r.Index) > 0 { path.WriteString("/") path.WriteString(strings.Join(r.Index, ",")) } params = make(map[string]string) if r.Format ! = "" { params["format"] = r.Format } if len(r.H) > 0 { params["h"] = strings.Join(r.H, ",") } if r.Help ! = nil { params["help"] = strconv.FormatBool(*r.Help) } if len(r.S) > 0 { params["s"] = strings.Join(r.S, ",") } if r.V ! = nil { params["v"] = strconv.FormatBool(*r.V) } if r.Pretty { params["pretty"] = "true" } if r.Human { params["human"] = "true" } if r.ErrorTrace { params["error_trace"] = "true" } if len(r.FilterPath) > 0 { params["filter_path"] = strings.Join(r.FilterPath, ",") } req, err := newRequest(method, path.String(), nil) if err ! = nil { return nil, err } if len(params) > 0 { q := req.URL.Query() for k, v := range params { q.Set(k, v) } req.URL.RawQuery = q.Encode() } if len(r.Header) > 0 { if len(req.Header) == 0 { req.Header = r.Header } else { for  k, vv := range r.Header { for _, v := range vv { req.Header.Add(k, v) } } } } if ctx ! = nil { req = req.WithContext(ctx) } res, err := transport.Perform(req) if err ! = nil { return nil, err } response := Response{ StatusCode: res.StatusCode, Body: res.Body, Header: res.Header, } return &response, nil }Copy the code

Again, this example is the Go-ElasticSearch package that builds data about the request parameters. The space is applied first, then the String is written, and finally path.string (), over. In one go,