這些是死知識(shí),把常用的記住,不常用的直接查表就行了
創(chuàng)新互聯(lián)建站 - 溫江服務(wù)器租用,四川服務(wù)器租用,成都服務(wù)器租用,四川網(wǎng)通托管,綿陽服務(wù)器托管,德陽服務(wù)器托管,遂寧服務(wù)器托管,綿陽服務(wù)器托管,四川云主機(jī),成都云主機(jī),西南云主機(jī),溫江服務(wù)器租用,西南服務(wù)器托管,四川/成都大帶寬,機(jī)柜大帶寬租用·托管,四川老牌IDC服務(wù)商
golang 的fmt 包實(shí)現(xiàn)了格式化I/O函數(shù),類似于C的 printf 和 scanf。
type Human struct {
Name string
}
var people = Human{Name:"zhangsan"}
golang沒有 '%u' 點(diǎn)位符,若整數(shù)為無符號(hào)類型,默認(rèn)就會(huì)被打印成無符號(hào)的。
寬度與精度的控制格式以Unicode碼點(diǎn)為單位。寬度為該數(shù)值占用區(qū)域的最小寬度;精度為小數(shù)點(diǎn)之后的位數(shù)。
操作數(shù)的類型為int時(shí),寬度與精度都可用字符 '*' 表示。
對(duì)于 %g/%G 而言,精度為所有數(shù)字的總數(shù),例如:123.45,%.4g 會(huì)打印123.5,(而 %6.2f 會(huì)打印123.45)。
%e 和 %f 的默認(rèn)精度為6
對(duì)大多數(shù)的數(shù)值類型而言,寬度為輸出的最小字符數(shù),如果必要的話會(huì)為已格式化的形式填充空格。
而以字符串類型,精度為輸出的最大字符數(shù),如果必要的話會(huì)直接截?cái)唷?/p>
使用起來很簡單,一般配合fmt.Printf()使用,因?yàn)閒mt的Printf()是有格式的輸出,切忌使用Println(),否則將會(huì)以字符串的形式輸出。
查看原文: golang fmt格式“占位符”
首先說一下go中的字符串類型:
字符串就是一串固定長度的字符連接起來的字符序列。Go的字符串是由單個(gè)字節(jié)連接起來的。Go語言的字符串的字節(jié)使用UTF-8編碼標(biāo)識(shí)Unicode文本。
下面介紹字符串的三種遍歷方式,根據(jù)實(shí)際情況選擇即可。
該遍歷方式==缺點(diǎn)==:遍歷是按照字節(jié)遍歷,因此如果有中文等非英文字符,就會(huì)出現(xiàn)亂碼,比如要遍歷"abc北京"這個(gè)字符串,效果如下:
可見這不是我們想要的效果,根據(jù)utf-8中文編碼規(guī)則,我們要str[3]str[4]str[5]三個(gè)字節(jié)合起來組成“北”字及 str[6]str[7]str[8]合起來組成“京”字。由此引出下面第二種遍歷方法。
該方式是按照字符遍歷的,所以不會(huì)出現(xiàn)亂碼,如下:
運(yùn)行結(jié)果:
從圖中可以看到第二個(gè)漢子“京”的開始下標(biāo)是6,直接跳過了4和5,可見確實(shí)依照utf8編碼方式將三個(gè)字節(jié)組合成了一個(gè)漢字,str[3]-str[5]組合成“北”字,str[6]-str[8]組合成了“京”字。
由于下標(biāo)的不確定性,所以引出了下面的遍歷方式。
1 可以先將字符串轉(zhuǎn)成 []rune 切片
2 再用常規(guī)方法進(jìn)行遍歷
運(yùn)行效果:
由此可見下標(biāo)是按1遞增的,沒有產(chǎn)生跳躍現(xiàn)象。
定義一個(gè)切片,然后讓切片去引用一個(gè)已經(jīng)創(chuàng)建好的數(shù)組?;菊Z法如下:
索引1:切片引用的起始元素位
索引2:切片只引用該元素位之前的元素
例程如下:
在該方法中,我們未指定容量cap,這里的值為5是系統(tǒng)定義的。
在方法一中,可以用arr數(shù)組名來操控?cái)?shù)組中的元素,也可以通過slice切片來操控?cái)?shù)組中的元素。切片是直接引用數(shù)組,數(shù)組是事先存在的,程序員是可見的。
通過 make 來創(chuàng)建切片,基本語法如下:
make函數(shù)第三個(gè)參數(shù)cap即容量是可選的,如果一定要自己注明的話,要注意保證cap≥len。
用該方法可以 指定切片的大小(len)和容量(cap)
例程如下:
由于未賦值系統(tǒng)默認(rèn)將元素值置為0,即:
數(shù)值類型數(shù)組:????默認(rèn)值為 0
字符串?dāng)?shù)組:? ? ? ?默認(rèn)值為 ""
bool數(shù)組:? ? ? ? ? ?默認(rèn)值為 false
在方法二中,通過make方式創(chuàng)建的切片對(duì)應(yīng)的數(shù)組是由make底層維護(hù),對(duì)外不可見,即只能通過slice去訪問各個(gè)元素。
定義一個(gè)切片,直接就指定具體數(shù)組,使用原理類似于make的方式。
例程如下:
操作字符串離不開字符串的拼接,但是Go中string是只讀類型,大量字符串的拼接會(huì)造成性能問題。
拼接字符串,無外乎四種方式,采用“+”,“fmt.Sprintf()”,"bytes.Buffer","strings.Builder"
上面我們創(chuàng)建10萬字符串拼接的測試,可以發(fā)現(xiàn)"bytes.Buffer","strings.Builder"的性能最好,約是“+”的1000倍級(jí)別。
這是由于string是不可修改的,所以在使用“+”進(jìn)行拼接字符串,每次都會(huì)產(chǎn)生申請(qǐng)空間,拼接,復(fù)制等操作,數(shù)據(jù)量大的情況下非常消耗資源和性能。而采用Buffer等方式,都是預(yù)先計(jì)算拼接字符串?dāng)?shù)組的總長度(如果可以知道長度),申請(qǐng)空間,底層是slice數(shù)組,可以以append的形式向后進(jìn)行追加。最后在轉(zhuǎn)換為字符串。這申請(qǐng)了不斷申請(qǐng)空間的操作,也減少了空間的使用和拷貝的次數(shù),自然性能也高不少。
bytes.buffer是一個(gè)緩沖byte類型的緩沖器存放著都是byte
是一個(gè)變長的 buffer,具有 Read 和Write 方法。 Buffer 的 零值 是一個(gè) 空的 buffer,但是可以使用,底層就是一個(gè) []byte, 字節(jié)切片。
向Buffer中寫數(shù)據(jù),可以看出Buffer中有個(gè)Grow函數(shù)用于對(duì)切片進(jìn)行擴(kuò)容。
從Buffer中讀取數(shù)據(jù)
strings.Builder的方法和bytes.Buffer的方法的命名幾乎一致。
但實(shí)現(xiàn)并不一致,Builder的Write方法直接將字符拼接slice數(shù)組后。
其沒有提供read方法,但提供了strings.Reader方式
Reader 結(jié)構(gòu):
Buffer:
Builder:
可以看出Buffer和Builder底層都是采用[]byte數(shù)組進(jìn)行裝載數(shù)據(jù)。
先來說說Buffer:
創(chuàng)建好Buffer是一個(gè)empty的,off 用于指向讀寫的尾部。
在寫的時(shí)候,先判斷當(dāng)前寫入字符串長度是否大于Buffer的容量,如果大于就調(diào)用grow進(jìn)行擴(kuò)容,擴(kuò)容申請(qǐng)的長度為當(dāng)前寫入字符串的長度。如果當(dāng)前寫入字符串長度小于最小字節(jié)長度64,直接創(chuàng)建64長度的[]byte數(shù)組。如果申請(qǐng)的長度小于二分之一總?cè)萘繙p去當(dāng)前字符總長度,說明存在很大一部分被使用但已讀,可以將未讀的數(shù)據(jù)滑動(dòng)到數(shù)組頭。如果容量不足,擴(kuò)展2*c + n 。
其String()方法就是將字節(jié)數(shù)組強(qiáng)轉(zhuǎn)為string
Builder是如何實(shí)現(xiàn)的。
Builder采用append的方式向字節(jié)數(shù)組后添加字符串。
從上面可以看出,[]byte的內(nèi)存大小也是以倍數(shù)進(jìn)行申請(qǐng)的,初始大小為 0,第一次為大于當(dāng)前申請(qǐng)的最大 2 的指數(shù),不夠進(jìn)行翻倍.
可以看出如果舊容量小于1024進(jìn)行翻倍,否則擴(kuò)展四分之一。(2048 byte 后,申請(qǐng)策略的調(diào)整)。
其次String()方法與Buffer的string方法也有明顯區(qū)別。Buffer的string是一種強(qiáng)轉(zhuǎn),我們知道在強(qiáng)轉(zhuǎn)的時(shí)候是需要進(jìn)行申請(qǐng)空間,并拷貝的。而Builder只是指針的轉(zhuǎn)換。
這里我們解析一下 *(*string)(unsafe.Pointer(b.buf)) 這個(gè)語句的意思。
先來了解下unsafe.Pointer 的用法。
也就是說,unsafe.Pointer 可以轉(zhuǎn)換為任意類型,那么意味著,通過unsafe.Pointer媒介,程序繞過類型系統(tǒng),進(jìn)行地址轉(zhuǎn)換而不是拷貝。
即*A = Pointer = *B
就像上面例子一樣,將字節(jié)數(shù)組轉(zhuǎn)為unsafe.Pointer類型,再轉(zhuǎn)為string類型,s和b中內(nèi)容一樣,修改b,s也變了,說明b和s是同一個(gè)地址。但是對(duì)s重新賦值后,意味著s的地址指向了“WORLD”,它們所使用的內(nèi)存空間不同了,所以s改變后,b并不會(huì)改變。
所以他們的區(qū)別就在于 bytes.Buffer 是重新申請(qǐng)了一塊空間,存放生成的string變量, 而strings.Builder直接將底層的[]byte轉(zhuǎn)換成了string類型返回了回來,去掉了申請(qǐng)空間的操作。
go語言的字符串是UTF-8編碼的、不可改變的字節(jié)序列。
要修改字符串,只能以原串為基礎(chǔ),創(chuàng)建一個(gè)新串。下面的圖中是一個(gè)參考示例,提供了以原串為藍(lán)本,創(chuàng)建新串的兩種方法。
代碼
輸出
因?yàn)閏har *strings[]不是指針而是指針數(shù)組,那么
temp = strings[top];
strings[top] = strings[seek];
strings[seek] = temp;
這種交換交換的就是主調(diào)函數(shù)中的數(shù)組中的指針,把指向字符串的指針順序改變了,當(dāng)然按次序輸出就達(dá)到排序目的了……