操作字符串離不開字符串的拼接,但是Go中string是只讀類型,大量字符串的拼接會造成性能問題。
讓客戶滿意是我們工作的目標,不斷超越客戶的期望值來自于我們對這個行業(yè)的熱愛。我們立志把好的技術通過有效、簡單的方式提供給客戶,將通過不懈努力成為客戶在信息化領域值得信任、有價值的長期合作伙伴,公司提供的服務項目有:主機域名、雅安服務器托管、營銷軟件、網站建設、和靜網站維護、網站推廣。
拼接字符串,無外乎四種方式,采用“+”,“fmt.Sprintf()”,"bytes.Buffer","strings.Builder"
上面我們創(chuàng)建10萬字符串拼接的測試,可以發(fā)現(xiàn)"bytes.Buffer","strings.Builder"的性能最好,約是“+”的1000倍級別。
這是由于string是不可修改的,所以在使用“+”進行拼接字符串,每次都會產生申請空間,拼接,復制等操作,數(shù)據(jù)量大的情況下非常消耗資源和性能。而采用Buffer等方式,都是預先計算拼接字符串數(shù)組的總長度(如果可以知道長度),申請空間,底層是slice數(shù)組,可以以append的形式向后進行追加。最后在轉換為字符串。這申請了不斷申請空間的操作,也減少了空間的使用和拷貝的次數(shù),自然性能也高不少。
bytes.buffer是一個緩沖byte類型的緩沖器存放著都是byte
是一個變長的 buffer,具有 Read 和Write 方法。 Buffer 的 零值 是一個 空的 buffer,但是可以使用,底層就是一個 []byte, 字節(jié)切片。
向Buffer中寫數(shù)據(jù),可以看出Buffer中有個Grow函數(shù)用于對切片進行擴容。
從Buffer中讀取數(shù)據(jù)
strings.Builder的方法和bytes.Buffer的方法的命名幾乎一致。
但實現(xiàn)并不一致,Builder的Write方法直接將字符拼接slice數(shù)組后。
其沒有提供read方法,但提供了strings.Reader方式
Reader 結構:
Buffer:
Builder:
可以看出Buffer和Builder底層都是采用[]byte數(shù)組進行裝載數(shù)據(jù)。
先來說說Buffer:
創(chuàng)建好Buffer是一個empty的,off 用于指向讀寫的尾部。
在寫的時候,先判斷當前寫入字符串長度是否大于Buffer的容量,如果大于就調用grow進行擴容,擴容申請的長度為當前寫入字符串的長度。如果當前寫入字符串長度小于最小字節(jié)長度64,直接創(chuàng)建64長度的[]byte數(shù)組。如果申請的長度小于二分之一總容量減去當前字符總長度,說明存在很大一部分被使用但已讀,可以將未讀的數(shù)據(jù)滑動到數(shù)組頭。如果容量不足,擴展2*c + n 。
其String()方法就是將字節(jié)數(shù)組強轉為string
Builder是如何實現(xiàn)的。
Builder采用append的方式向字節(jié)數(shù)組后添加字符串。
從上面可以看出,[]byte的內存大小也是以倍數(shù)進行申請的,初始大小為 0,第一次為大于當前申請的最大 2 的指數(shù),不夠進行翻倍.
可以看出如果舊容量小于1024進行翻倍,否則擴展四分之一。(2048 byte 后,申請策略的調整)。
其次String()方法與Buffer的string方法也有明顯區(qū)別。Buffer的string是一種強轉,我們知道在強轉的時候是需要進行申請空間,并拷貝的。而Builder只是指針的轉換。
這里我們解析一下 *(*string)(unsafe.Pointer(b.buf)) 這個語句的意思。
先來了解下unsafe.Pointer 的用法。
也就是說,unsafe.Pointer 可以轉換為任意類型,那么意味著,通過unsafe.Pointer媒介,程序繞過類型系統(tǒng),進行地址轉換而不是拷貝。
即*A = Pointer = *B
就像上面例子一樣,將字節(jié)數(shù)組轉為unsafe.Pointer類型,再轉為string類型,s和b中內容一樣,修改b,s也變了,說明b和s是同一個地址。但是對s重新賦值后,意味著s的地址指向了“WORLD”,它們所使用的內存空間不同了,所以s改變后,b并不會改變。
所以他們的區(qū)別就在于 bytes.Buffer 是重新申請了一塊空間,存放生成的string變量, 而strings.Builder直接將底層的[]byte轉換成了string類型返回了回來,去掉了申請空間的操作。
首先說一下go中的字符串類型:
字符串就是一串固定長度的字符連接起來的字符序列。Go的字符串是由單個字節(jié)連接起來的。Go語言的字符串的字節(jié)使用UTF-8編碼標識Unicode文本。
下面介紹字符串的三種遍歷方式,根據(jù)實際情況選擇即可。
該遍歷方式==缺點==:遍歷是按照字節(jié)遍歷,因此如果有中文等非英文字符,就會出現(xiàn)亂碼,比如要遍歷"abc北京"這個字符串,效果如下:
可見這不是我們想要的效果,根據(jù)utf-8中文編碼規(guī)則,我們要str[3]str[4]str[5]三個字節(jié)合起來組成“北”字及 str[6]str[7]str[8]合起來組成“京”字。由此引出下面第二種遍歷方法。
該方式是按照字符遍歷的,所以不會出現(xiàn)亂碼,如下:
運行結果:
從圖中可以看到第二個漢子“京”的開始下標是6,直接跳過了4和5,可見確實依照utf8編碼方式將三個字節(jié)組合成了一個漢字,str[3]-str[5]組合成“北”字,str[6]-str[8]組合成了“京”字。
由于下標的不確定性,所以引出了下面的遍歷方式。
1 可以先將字符串轉成 []rune 切片
2 再用常規(guī)方法進行遍歷
運行效果:
由此可見下標是按1遞增的,沒有產生跳躍現(xiàn)象。
按值傳遞函數(shù)參數(shù),是拷貝參數(shù)的實際值到函數(shù)的形式參數(shù)的方法調用。在這種情況下,參數(shù)在函數(shù)內變化對參數(shù)不會有影響。
默認情況下,Go編程語言使用調用通過值的方法來傳遞參數(shù)。在一般情況下,這意味著,在函數(shù)內碼不能改變用來調用所述函數(shù)的參數(shù)??紤]函數(shù)swap()的定義如下。
代碼如下:
/* function definition to swap the values */
func swap(int x, int y) int {
var temp int
temp = x /* save the value of x */
x = y /* put y into x */
y = temp /* put temp into y */
return temp;
}
現(xiàn)在,讓我們通過使實際值作為在以下示例調用函數(shù)swap():
代碼如下:
package main
import "fmt"
func main() {
/* local variable definition */
var a int = 100
var b int = 200
fmt.Printf("Before swap, value of a : %d\n", a )
fmt.Printf("Before swap, value of b : %d\n", b )
/* calling a function to swap the values */
swap(a, b)
fmt.Printf("After swap, value of a : %d\n", a )
fmt.Printf("After swap, value of b : %d\n", b )
}
func swap(x, y int) int {
var temp int
temp = x /* save the value of x */
x = y /* put y into x */
y = temp /* put temp into y */
return temp;
}
讓我們把上面的代碼放在一個C文件,編譯并執(zhí)行它,它會產生以下結果:
Before swap, value of a :100
Before swap, value of b :200
After swap, value of a :100
After swap, value of b :200
這表明,參數(shù)值沒有被改變,雖然它們已經在函數(shù)內部改變。
通過傳遞函數(shù)參數(shù),即是拷貝參數(shù)的地址到形式參數(shù)的參考方法調用。在函數(shù)內部,地址是訪問調用中使用的實際參數(shù)。這意味著,對參數(shù)的更改會影響傳遞的參數(shù)。
要通過引用傳遞的值,參數(shù)的指針被傳遞給函數(shù)就像任何其他的值。所以,相應的,需要聲明函數(shù)的參數(shù)為指針類型如下面的函數(shù)swap(),它的交換兩個整型變量的值指向它的參數(shù)。
代碼如下:
/* function definition to swap the values */
func swap(x *int, y *int) {
var temp int
temp = *x /* save the value at address x */
*x = *y /* put y into x */
*y = temp /* put temp into y */
}
現(xiàn)在,讓我們調用函數(shù)swap()通過引用作為在下面的示例中傳遞數(shù)值:
代碼如下:
package main
import "fmt"
func main() {
/* local variable definition */
var a int = 100
var b int= 200
fmt.Printf("Before swap, value of a : %d\n", a )
fmt.Printf("Before swap, value of b : %d\n", b )
/* calling a function to swap the values.
* a indicates pointer to a ie. address of variable a and
* b indicates pointer to b ie. address of variable b.
*/
swap(a, b)
fmt.Printf("After swap, value of a : %d\n", a )
fmt.Printf("After swap, value of b : %d\n", b )
}
func swap(x *int, y *int) {
var temp int
temp = *x /* save the value at address x */
*x = *y /* put y into x */
*y = temp /* put temp into y */
}
讓我們把上面的代碼放在一個C文件,編譯并執(zhí)行它,它會產生以下結果:
Before swap, value of a :100
Before swap, value of b :200
After swap, value of a :200
After swap, value of b :100
這表明變化的功能以及不同于通過值調用的外部體現(xiàn)的改變不能反映函數(shù)之外。
分享名稱:go語言組合字符串操作,golang 字符串操作
當前URL:http://jinyejixie.com/article40/dsecceo.html
成都網站建設公司_創(chuàng)新互聯(lián),為您提供外貿建站、自適應網站、品牌網站建設、App開發(fā)、移動網站建設、ChatGPT
聲明:本網站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)