Golang基础(1)
GO語言基礎
GO語言基礎
- <center>**GO語言基礎**
- **前言**
- Go語言的起源
- **Go語言項目**
- 第一章、入門
- **1.1. Hello World**
- 1.2.命令行參數
- 1.3查找重復行
- 1.4 GIF動畫
- **第二章、編程語言**
- 編程語言介紹
- 2.1、什么是編程語言
- 2.2、編譯型語言與解釋性語言
- 第三章、Go安裝
- 3.1、GO編譯器下載
- 3.2 MAC系統
- mac下安裝SDK
- 3.3 WIndows系統
- 3.3.1 Windows下安裝SDK
- 3.3.2、Windows下配置環境
- 3.4、IDE的安裝與使用
- 安裝IDEA
- 3.5、idea快捷鍵
- 第四章、基礎語法
- 4.1、注釋
- 4.2 變量
- 4.2.1、GO聲明變量
- 4.2.2 變量賦值
- 4.2.3 匿名變量
- 4.2.4變量名命名的規則
- 4.3基本數據類型
- 4.3.1整型
- 4.3.2浮點型
- 4.3.3布爾類型
- 4.3.4字符串類型
- 4.3.4.1 字符串常用方法
- 4.3.5轉義符
- 4.3.6 進制轉換
- 4.3.7 數據類型轉換
- 4.3.8 運算符
- 4.3.9 輸入輸出函數
- 4.3.9.1 輸出函數
- 4.3.9.2 輸入函數
- 4.3.10 常量與iota
- 4.3.10.1 iota計數器
前言
“Go是一個開源的編程語言,它很容易用于構建簡單、可靠和高效的軟件。”(摘自Go語言官
方網站:http://golang.org )
“前言、起源,均摘錄于GO語言圣經”
Go語言由來自Google公司的Robert Griesemer,Rob Pike和Ken Thompson三位大牛于2007
年9月開始設計和實現,然后于2009年的11月對外正式發布(譯注:關于Go語言的創世紀過
程請參考 http://talks.golang.org/2015/how-go-was-made.slide )。語言及其配套工具的設計
目標是具有表達力,高效的編譯和執行效率,有效地編寫高效和健壯的程序。
Go語言有著和C語言類似的語法外表,和C語言一樣是專業程序員的必備工具,可以用最小的
代價獲得最大的戰果。 但是它不僅僅是一個更新的C語言。它還從其他語言借鑒了很多好的
想法,同時避免引入過度的復雜性。 Go語言中和并發編程相關的特性是全新的也是有效的,
同時對數據抽象和面向對象編程的支持也很靈活。 Go語言同時還集成了自動垃圾收集技術用
于更好地管理內存。
Go語言尤其適合編寫網絡服務相關基礎設施,同時也適合開發一些工具軟件和系統軟件。 但
是Go語言確實是一個通用的編程語言,它也可以用在圖形圖像驅動編程、移動應用程序開發
和機器學習等諸多領域。目前Go語言已經成為受歡迎的作為無類型的腳本語言的替代者: 因
為Go編寫的程序通常比腳本語言運行的更快也更安全,而且很少會發生意外的類型錯誤。
Go語言還是一個開源的項目,可以免費獲編譯器、庫、配套工具的源代碼。 Go語言的貢獻
者來自一個活躍的全球社區。Go語言可以運行在類UNIX系統—— 比如
Linux、FreeBSD、OpenBSD、Mac OSX——和Plan9系統和Microsoft Windows操作系統之
上。 Go語言編寫的程序無需修改就可以運行在上面這些環境。
Go語言的起源
編程語言的演化跟生物物種的演化類似,一個成功的編程語言的后代一般都會繼承它們祖先
的優點;當然有時多種語言雜合也可能會產生令人驚訝的特性;還有一些激進的新特性可能
并沒有先例。通過觀察這些影響,我們可以學到為什么一門語言是這樣子的,它已經適應了
怎樣的環境。
下圖展示了有哪些早期的編程語言對Go語言的設計產生了重要影響
Go語言有時候被描述為“C類似語言”,或者是“21世紀的C語言”。Go從C語言繼承了相似的表
達式語法、控制流結構、基礎數據類型、調用參數傳值、指針等很多思想,還有C語言一直所
看中的編譯后機器碼的運行效率以及和現有操作系統的無縫適配。
但是在Go語言的家族樹中還有其它的祖先。其中一個有影響力的分支來自Niklaus Wirth所設
計的Pascal語言。然后Modula-2語言激發了包的概念。然后Oberon語言摒棄了模塊接口文件
和模塊實現文件之間的區別。第二代的Oberon-2語言直接影響了包的導入和聲明的語法,還有Oberon語言的面向對象特性所提供的方法的聲明語法等。
Go語言的另一支祖先,帶來了Go語言區別其他語言的重要特性,靈感來自于貝爾實驗室的
Tony Hoare于1978年發表的鮮為外界所知的關于并發研究的基礎文獻 順序通信進程 (
communicating sequential processes ,縮寫為CSP。在CSP中,程序是一組中間沒有共享狀
態的平行運行的處理過程,它們之間使用管道進行通信和控制同步。不過Tony Hoare的CSP
只是一個用于描述并發性基本概念的描述語言,并不是一個可以編寫可執行程序的通用編程
語言。
接下來,Rob Pike和其他人開始不斷嘗試將CSP引入實際的編程語言中。他們第一次嘗試引
入CSP特性的編程語言叫Squeak(老鼠間交流的語言),是一個提供鼠標和鍵盤事件處理的
編程語言,它的管道是靜態創建的。然后是改進版的Newsqueak語言,提供了類似C語言語
句和表達式的語法和類似Pascal語言的推導語法。Newsqueak是一個帶垃圾回收的純函數式
語言,它再次針對鍵盤、鼠標和窗口事件管理。但是在Newsqueak語言中管道是動態創建
的,屬于第一類值, 可以保存到變量中。
在Plan9操作系統中,這些優秀的想法被吸收到了一個叫Alef的編程語言中。Alef試圖將
Newsqueak語言改造為系統編程語言,但是因為缺少垃圾回收機制而導致并發編程很痛苦。
(譯注:在Aelf之后還有一個叫Limbo的編程語言,Go語言從其中借鑒了很多特性。 具體請
參考Pike的講稿:http://talks.golang.org/2012/concurrency.slide#9 )
Go語言的其他的一些特性零散地來自于其他一些編程語言;比如iota語法是從APL語言借鑒,
詞法作用域與嵌套函數來自于Scheme語言(和其他很多語言)。當然,我們也可以從Go中
發現很多創新的設計。比如Go語言的切片為動態數組提供了有效的隨機存取的性能,這可能
會讓人聯想到鏈表的底層的共享機制。還有Go語言新發明的defer語句
Go語言項目
所有的編程語言都反映了語言設計者對編程哲學的反思,通常包括之前的語言所暴露的一些
不足地方的改進。Go項目是在Google公司維護超級復雜的幾個軟件系統遇到的一些問題的反
思(但是這類問題絕不是Google公司所特有的)。
正如Rob Pike所說,“軟件的復雜性是乘法級相關的”,通過增加一個部分的復雜性來修復問題
通常將慢慢地增加其他部分的復雜性。通過增加功能、選項和配置是修復問題的最快的途
徑,但是這很容易讓人忘記簡潔的內涵,即從長遠來看,簡潔依然是好軟件的關鍵因素。
簡潔的設計需要在工作開始的時候舍棄不必要的想法,并且在軟件的生命周期內嚴格區別好
的改變和壞的改變。通過足夠的努力,一個好的改變可以在不破壞原有完整概念的前提下保
持自適應,正如Fred Brooks所說的“概念完整性”;而一個壞的改變則不能達到這個效果,它
們僅僅是通過膚淺的和簡單的妥協來破壞原有設計的一致性。只有通過簡潔的設計,才能讓
一個系統保持穩定、安全和持續的進化。
Go項目包括編程語言本身,附帶了相關的工具和標準庫,最后但并非代表不重要的是,關于
簡潔編程哲學的宣言。就事后諸葛的角度來看,Go語言的這些地方都做的還不錯:擁有自動 垃圾回收、一個包系統、函數作為一等公民、詞法作用域、系統調用接口、只讀的UTF8字符串等。
但是Go語言本身只有很少的特性,也不太可能添加太多的特性。例如,它沒有隱式的
數值轉換,沒有構造函數和析構函數,沒有運算符重載,沒有默認參數,也沒有繼承,沒有
泛型,沒有異常,沒有宏,沒有函數修飾,更沒有線程局部存儲。但是,語言本身是成熟和
穩定的,而且承諾保證向后兼容:用之前的Go語言編寫程序可以用新版本的Go語言編譯器和
標準庫直接構建而不需要修改代碼。
Go語言有足夠的類型系統以避免動態語言中那些粗心的類型錯誤,但是,Go語言的類型系統
相比傳統的強類型語言又要簡潔很多。雖然,有時候這會導致一個“無類型”的抽象類型概念,
但是Go語言程序員并不需要像C++或Haskell程序員那樣糾結于具體類型的安全屬性。在實踐
中,Go語言簡潔的類型系統給程序員帶來了更多的安全性和更好的運行時性能。
Go語言鼓勵當代計算機系統設計的原則,特別是局部的重要性。它的內置數據類型和大多數
的準庫數據結構都經過精心設計而避免顯式的初始化或隱式的構造函數,因為很少的內存分
配和內存初始化代碼被隱藏在庫代碼中了。
Go語言的聚合類型(結構體和數組)可以直接操
作它們的元素,只需要更少的存儲空間、更少的內存寫操作,而且指針操作比其他間接操作
的語言也更有效率。由于現代計算機是一個并行的機器,Go語言提供了基于CSP的并發特性
支持。Go語言的動態棧使得輕量級線程goroutine的初始棧可以很小,因此,創建一個
goroutine的代價很小,創建百萬級的goroutine完全是可行的。
Go語言的標準庫(通常被稱為語言自帶的電池),提供了清晰的構建模塊和公共接口,包含 I/O操作、文本處理、圖像、密碼學、網絡和分布式應用程序等,并支持許多標準化的文件格
式和編解碼協議。庫和工具使用了大量的約定來減少額外的配置和解釋,從而最終簡化程序的邏輯,而且,每個Go程序結構都是如此的相似,因此,Go程序也很容易學習。使用Go語
言自帶工具構建Go語言項目只需要使用文件名和標識符名稱, 一個偶爾的特殊注釋來確定所有
的庫、可執行文件、測試、基準測試、例子、以及特定于平臺的變量、項目的文檔等;Go語
言源代碼本身就包含了構建規范。
第一章、入門
本章介紹Go語言的基礎組件。本章提供了足夠的信息和示例程序,希望可以幫你盡快入門, 寫
出有用的程序。本章和之后章節的示例程序都針對你可能遇到的現實案例。先了解幾個Go程
序,涉及的主題從簡單的文件處理、圖像處理到互聯網客戶端和服務端并發。當然,第一章
不會解釋細枝末節,但用這些程序來學習一門新語言還是很有效的。
學習一門新語言時,會有一種自然的傾向, 按照自己熟悉的語言的套路寫新語言程序。學習Go
語言的過程中,請警惕這種想法,盡量別這么做。我們會演示怎么寫好Go語言程序,所以,
請使用本書的代碼作為你自己寫程序時的指南。
1.1. Hello World
我們以現已成為傳統的“hello world”案例來開始吧, 這個例子首次出現于1978年出版的C語言
圣經《The C Programming Language》
gopl.io/ch1/helloworld
package mainimport "fmt"func main() {fmt.Println("Hello, 世界") }Go是一門編譯型語言,Go語言的工具鏈將源代碼及其依賴轉換成計算機的機器指令(譯注:
靜態編譯)。Go語言提供的工具都通過一個單獨的命令 go 調用, go 命令有一系列子命令。
最簡單的一個子命令就是run。這個命令編譯一個或多個以.go結尾的源文件,鏈接庫文件,并
運行最終生成的可執行文件。
毫無意外,這個命令會輸出:
Hello, 世界Go語言原生支持Unicode,它可以處理全世界任何語言的文本。
如果不只是一次性實驗,你肯定希望能夠編譯這個程序,保存編譯結果以備將來之用。可以
用 build子命令: $ go build helloworld.go
這個命令生成一個名為helloworld的可執行的二進制文件(譯注:Windows系統下生成的可執
行文件是helloworld.exe,增加了.exe后綴名),之后你可以隨時運行它(譯注:在Windows
系統下在命令行直接輸入helloworld.exe命令運行),不需任何處理(譯注:因為靜態編譯,
所以不用擔心在系統庫更新的時候沖突,幸福感滿滿)
Go的標準庫提供了100多個包,以支持常見功能,如輸入、輸出、排序以及文本處理。比
如 fmt包,就含有格式化輸出、接收輸入的函數。 Println是其中一個基礎函數,可以打印
以空格間隔的一個或多個值,并在最后添加一個換行符,從而輸出一整行。
main 包比較特殊。它定義了一個獨立可執行的程序,而不是一個庫。在 main里的 main函
數 也很特殊,它是整個程序執行時的入口(譯注:C系語言差不多都這樣)。 main函數所做
的事情就是程序做的。當然了, main 函數一般調用其它包里的函數完成很多工作, 比如,
fmt.Println。
必須告訴編譯器源文件需要哪些包,這就是跟隨在 package 聲明后面的 import 聲明扮演的角
色。hello world例子只用到了一個包,大多數程序需要導入多個包。
必須恰當導入需要的包,缺少了必要的包或者導入了不需要的包,程序都無法編譯通過。這
項嚴格要求避免了程序開發過程中引入未使用的包(譯注:Go語言編譯過程沒有警告信息,
爭議特性之一)。
import 聲明必須跟在文件的 package聲明之后。隨后,則是組成程序的函數、變量、常量、
類型的聲明語句(分別由關鍵字 func, var, const, type定義)。這些內容的聲明順序并
不重要(譯注:最好還是定一下規范)。這個例子的程序已經盡可能短了,只聲明了一個函
數, 其中只調用了一個其他函數。為了節省篇幅,有些時候, 示例程序會省
略 package和 import聲明,但是,這些聲明在源代碼里有,并且必須得有才能編譯。
一個函數的聲明由 func 關鍵字、函數名、參數列表、返回值列表(這個例子里的 main函數
參數列表和返回值都是空的)以及包含在大括號里的函數體組成
Go語言不需要在語句或者聲明的末尾添加分號,除非一行上有多條語句。實際上,編譯器會
主動把特定符號后的換行符轉換為分號, 因此換行符添加的位置會影響Go代碼的正確解析(譯
注:比如行末是標識符、整數、浮點數、虛數、字符或字符串文字、關鍵
字 break、 continue、 fallthrough或 return中的一個、運算符和分隔符 ++、 - -、 )、 ]或 }中的一個)。舉個例子, 函數的左括號 { 必須和 func函數聲明在同一行上,
且位于末尾,不能獨占一行,而在表達式x + y中,可在 + 后換行,不能在 +前換行(譯
注:以+結尾的話不會被插入分號分隔符,但是以x結尾的話則會被分號分隔符,從而導致編
譯錯誤)。
Go語言在代碼格式上采取了很強硬的態度。 gofmt工具把代碼格式化為標準格式(譯注:這
個格式化工具沒有任何可以調整代碼格式的參數,Go語言就是這么任性),并且 go 工具中
的 fmt子命令會對指定包, 否則默認為當前目錄, 中所有.go源文件應用 gofmt命令。本博客中
所有代碼都被gofmt過。你也應該養成格式化自己的代碼的習慣。以法令方式規定標準的代碼
格式可以避免無盡的無意義的瑣碎爭執(譯注:也導致了Go語言的TIOBE排名較低,因為缺
少撕逼的話題)。更重要的是,這樣可以做多種自動源碼轉換,如果放任Go語言代碼格式,
這些轉換就不大可能了。
很多文本編輯器都可以配置為保存文件時自動執行gofmt ,這樣你的源代碼總會被恰當地格
式化。還有個相關的工具, goimports,可以根據代碼需要, 自動地添加或刪除 import聲
明。這個工具并沒有包含在標準的分發包中,可以用下面的命令安裝:
但是如果你用的是Goland進行編譯那就不用
1.2.命令行參數
大多數的程序都是處理輸入,產生輸出;這也正是“計算”的定義。但是, 程序如何獲取要處理
的輸入數據呢?一些程序生成自己的數據,但通常情況下,輸入來自于程序外部:文件、網
絡連接、其它程序的輸出、敲鍵盤的用戶、命令行參數或其它類似輸入源。下面幾個例子會
討論其中幾個輸入源,首先是命令行參數。
os包以跨平臺的方式,提供了一些與操作系統交互的函數和變量。程序的命令行參數可從os
包的Args變量獲取;os包外部使用os.Args訪問該變量。
os.Args變量是一個字符串(string)的切片(slice)(譯注:slice和Python語言中的切片類
似,是一個簡版的動態數組),切片是Go語言的基礎概念,稍后詳細介紹。現在先把切片s當
作數組元素序列, 序列的長度動態變化, 用 s[i]訪問單個元素,用 s[m:n]獲取子序列(譯注:
和python里的語法差不多)。序列的元素數目為len(s)。和大多數編程語言類似,區間索引時,
Go言里也采用左閉右開形式, 即,區間包括第一個索引元素,不包括最后一個, 因為這樣可以
簡化邏輯。(譯注:比如a = [1, 2, 3, 4, 5], a[0:3] = [1, 2, 3],不包含最后一個元素)。比如
s[m:n]這個切片,0 ≤ m ≤ n ≤ len(s),包含n-m個元素。
os.Args的第一個元素,os.Args[0], 是命令本身的名字;其它的元素則是程序啟動時傳給它的
參數。s[m:n]形式的切片表達式,產生從第m個元素到第n-1個元素的切片,下個例子用到的
元素包含在os.Args[1:len(os.Args)]切片中。如果省略切片表達式的m或n,會默認傳入0或
len(s),因此前面的切片可以簡寫成os.Args[1:]。
下面是Unix里echo命令的一份實現,echo把它的命令行參數打印成一行。程序導入了兩個
包,用括號把它們括起來寫成列表形式, 而沒有分開寫成獨立的 import聲明。兩種形式都合
法,列表形式習慣上用得多。包導入順序并不重要;gofmt工具格式化時按照字母順序對包名
排序。(示例有多個版本時,我們會對示例編號, 這樣可以明確當前正在討論的是哪個。)
gopl.io/ch1/echo1
package mainimport ("fmt""os" )func main() {var s, sep stringfor i := 1; i < len(os.Args); i++ {s += sep + os.Args[i]sep = " "}fmt.Println(s) }注釋語句以 //開頭。對于程序員來說,//之后到行末之間所有的內容都是注釋,被編譯器忽
略。按照慣例,我們在每個包的包聲明前添加注釋;對于 main package,注釋包含一句或幾
句話,從整體角度對程序做個描述。
var聲明定義了兩個string類型的變量s和sep。變量會在聲明時直接初始化。如果變量沒有顯
式初始化,則被隱式地賦予其類型的零值(zero value),數值類型是0,字符串類型是空字
符串""。這個例子里,聲明把s和sep隱式地初始化成空字符串。后面再來詳細地講解變量和
聲明。
對數值類型,Go語言提供了常規的數值和邏輯運算符。而對string類型, + 運算符連接字符
串(譯注:和C++或者js是一樣的)。所以表達式:
表示連接字符串sep和os.Args。程序中使用的語句:
s += sep + os.Args[i]是一條賦值語句, 將s的舊值跟sep與os.Args[i]連接后賦值回s,等價于:
s = s + sep + os.Args[i]運算符 +=是賦值運算符(assignment operator),每種數值運算符或邏輯運算符,
如 +或 *,都有對應的賦值運算符。
echo程序可以每循環一次輸出一個參數,這個版本卻是不斷地把新文本追加到末尾來構造字
符串。字符串s開始為空,即值為" ",每次循環會添加一些文本;第一次迭代之后,還會再插
入一個空格,因此循環結束時每個參數中間都有一個空格。這是一種二次加工(quadratic
process),當參數數量龐大時,開銷很大,但是對于echo,這種情形不大可能出現。本章會
介紹echo的若干改進版,后面會解決低效問題。
循環索引變量i在for循環的第一部分中定義。符號:= 是短變量聲明(short variable
declaration)的一部分, 這是定義一個或多個變量并根據它們的初始值為這些變量賦予適當類
型的語句。后面有這方面更多說明。
自增語句 i++給 i加1;這和 i += 1以及 i = i + 1都是等價的。對應的還有 i-- 給 i減
1。它們是語句,而不像C系的其它語言那樣是表達式。所以j = i++非法,而且++和--都只
能放在變量名后面,因此 --i 也非法。
Go語言只有for循環這一種循環語句。for循環有多種形式,其中一種如下所示:
for initialization; condition; post {// zero or more statements }for循環三個部分不需括號包圍。大括號強制要求, 左大括號必須和post語句在同一行。
initialization語句是可選的,在循環開始前執行。initalization如果存在,必須是一條簡單語句
(simple statement),即,短變量聲明、自增語句、賦值語句或函數調用。 condition是一
個布爾表達式(boolean expression),其值在每次循環迭代開始時計算。如果為 true 則執
行循環體語句。 post 語句在循環體執行結束后執行,之后再次對condition 求
值。 condition值為 false時,循環結束。
for循環的這三個部分每個都可以省略,如果省略 initialization和 post,分號也可以省
略:
如果連 condition也省略了,像下面這樣:
// a traditional infinite loop for {// ... }這就變成一個無限循環,盡管如此,還可以用其他方式終止循環, 如一條 break或 return語
句。
for循環的另一種形式, 在某種數據類型的區間(range)上遍歷,如字符串或切
片。 echo的第二版本展示了這種形式:
gopl.io/ch1/echo2
package mainimport ("fmt""os" )func main() {s, sep := "", ""for _, arg := range os.Args[1:] {s += sep + argsep = " "}fmt.Println(s) }每次循環迭代, range產生一對值;索引以及在該索引處的元素值。這個例子不需要索引,
但 range的語法要求, 要處理元素, 必須處理索引。一種思路是把索引賦值給一個臨時變量,
如 temp, 然后忽略它的值,但Go語言不允許使用無用的局部變量(local variables),因為這
會導致編譯錯誤。
Go語言中這種情況的解決方法是用 空標識符 (blank identifier),即 _ (也就是下劃線)。
空標識符可用于任何語法需要變量名但程序邏輯不需要的時候, 例如, 在循環里,丟棄不需要
的循環索引, 保留元素值。大多數的Go程序員都會像上面這樣使用 range和 _寫 echo程
序,因為隱式地而非顯式地索引os.Args,容易寫對。
echo的這個版本使用一條短變量聲明來聲明并初始化 s和 seps,也可以將這兩個變量分開
聲明,聲明一個變量有好幾種方式,下面這些都等價:
用哪種不用哪種,為什么呢?第一種形式,是一條短變量聲明,最簡潔,但只能用在函數內
部,而不能用于包變量。第二種形式依賴于字符串的默認初始化零值機制,被初始化為""。第
三種形式用得很少,除非同時聲明多個變量。第四種形式顯式地標明變量的類型,當變量類型與初值類型相同時,類型冗余,但如果兩者類型不同,變量類型就必須了。實踐中一般使
用前兩種形式中的某個,初始值重要的話就顯式地指定變量的類型,否則使用隱式初始化。
如前文所述,每次循環迭代字符串s的內容都會更新。+= 連接原字符串、空格和下個參數,
產生新字符串, 并把它賦值給 s。 s原來的內容已經不再使用,將在適當時機對它進行垃圾
回收。
如果連接涉及的數據量很大,這種方式代價高昂。一種簡單且高效的解決方案是使
用 strings包的 Join函數:
gopl.io/ch1/echo3
package mainimport ("fmt""os""strings" )func main() {fmt.Println(strings.Join(os.Args[1:], " ")) }最后,如果不關心輸出格式,只想看看輸出值,或許只是為了調試,可以用 Println為我們
格式化輸出。
這條語句的輸出結果跟 strings.Join得到的結果很像,只是被放到了一對方括號里。切片都
會被打印成這種格式
1.3查找重復行
對文件做拷貝、打印、搜索、排序、統計或類似事情的程序都有一個差不多的程序結構:一
個處理輸入的循環,在每個元素上執行計算處理,在處理的同時或最后產生輸出。我們會展
示一個名為 dup的程序的三個版本;靈感來自于Unix的 uniq命令,其尋找相鄰的重復行。
該程序使用的結構和包是個參考范例,可以方便地修改。
dup的第一個版本打印標準輸入中多次出現的行,以重復次數開頭。該程序將引入 if語
句, map數據類型以及 bufio包。
gopl.io/ch1/dup1
package mainimport ("bufio""fmt""os" )func main() {counts := make(map[string]int)input := bufio.NewScanner(os.Stdin)for input.Scan() {counts[input.Text()]++}for line, n := range counts {if n > 1 {fmt.Printf("%d\t%s\n", n, line)}} }正如 for循環一樣, if語句條件兩邊也不加括號,但是主體部分需要加。 if 語句
的 else部分是可選的,在 if 的條件為 false時執行。
map存儲了鍵/值(key/value)的集合,對集合元素,提供常數時間的存、取或測試操作。鍵
可以是任意類型,只要其值能用 ==運算符比較,最常見的例子是字符串;值則可以是任意類
型。這個例子中的鍵是字符串,值是整數。內置函數 make創建空 map,此外,它還有別的
作用。4.3節討論 map。
(譯注:從功能和實現上說, Go 的 map類似于 Java 語言中的 HashMap,Python語言中
的 dict, Lua 語言中的 table,通常使用 hash實現。遺憾的是,對于該詞的翻譯并不統一,數學界術語為 映射
,而計算機界眾說紛紜莫衷一是。為了防止對讀者造成誤解,保留不
譯。)
每次 dup讀取一行輸入,該行被當做 map,其對應的值遞增。 counts[input.Text()]++語句
等價下面兩句:
map中不含某個鍵時不用擔心,首次讀到新行時,等號右邊的表達式 counts[line]的值將被
計算為其類型的零值,對于int即0。
為了打印結果,我們使用了基于 range的循環,并在 counts這個 map上迭代。跟之前類
似,每次迭代得到兩個結果,鍵和其在 map中對應的值。 map的迭代順序并不確定,從實踐
來看,該順序隨機,每次運行都會變化。這種設計是有意為之的,因為能防止程序依賴特定
遍歷順序,而這是無法保證的。(譯注:具體可以參見這里
http://stackoverflow.com/questions/11853396/google-go-lang-assignment-order)
繼續來看 bufio包,它使處理輸入和輸出方便又高效。 Scanner類型是該包最有用的特性之
一,它讀取輸入并將其拆成行或單詞;通常是處理行形式的輸入最簡單的方法。
程序使用短變量聲明創建 bufio.Scanner類型的變量 input。
該變量從程序的標準輸入中讀取內容。每次調用 input.Scan(),即讀入下一行,并移除行末
的換行符;讀取的內容可以調用 input.Text()得到。 Scan函數在讀到一行時返回 true,不
再有輸入時返回 false。
類似于C或其它語言里的 printf函數, fmt.Printf函數對一些表達式產生格式化輸出。該函
數的首個參數是個格式字符串,指定后續參數被如何格式化。各個參數的格式取決于“轉換字
符”(conversion character),形式為百分號后跟一個字母。舉個例子, %d表示以十進制形
式打印一個整型操作數,而 %s則表示把字符串型操作數的值展開。
Printf 有一大堆這種轉換,Go程序員稱之為動詞(verb)。下面的表格雖然遠不是完整的規
范,但展示了可用的很多特性:
| %d | 十進制整數 |
| %x, %o, %b | 十六進制,八進制,二進制整數。 |
| %f, %g, %e | 浮點數: 3.141593 3.141592653589793 3.141593e+00 |
| %t | 布爾:true或false |
| %c | 字符(rune) (Unicode碼點) |
| %s | 字符串 |
| %q | 帶雙引號的字符串"abc"或帶單引號的字符’c’ |
| %v | 變量的自然形式(natural format) |
| %T | 變量的類型 |
| %% | 字面上的百分號標志(無操作數) |
dup1的格式字符串中還含有制表符 \t和換行符 \n。字符串字面上可能含有這些代表不可
見字符的轉義字符(escap sequences)。默認情況下, Printf不會換行。按照慣例,以字
母 f 結尾的格式化函數,如 log.Printf和 fmt.Errorf,都采用 fmt.Printf 的格式化準則。
而以 ln 結尾的格式化函數,則遵循 Println的方式,以跟 %v差不多的方式格式化參數,并
在最后添加一個換行符。(譯注:后綴 f 指 fomart, ln 指 line。)
很多程序要么從標準輸入中讀取數據,如上面的例子所示,要么從一系列具名文件中讀取數
據。 dup 程序的下個版本讀取標準輸入或是使用 os.Open打開各個具名文件,并操作它們。
gopl.io/ch1/dup2
package mainimport ("bufio""fmt""os" )func main() {counts := make(map[string]int)files := os.Args[1:]if len(files) == 0 {countLines(os.Stdin, counts)} else {for _, arg := range files {f, err := os.Open(arg)if err != nil {fmt.Fprintf(os.Stderr, "dup2: %v\n", err)continue}countLines(f, counts)f.Close()}}for line, n := range counts {if n > 1 {fmt.Printf("%d\t%s\n", n, line)}} } func countLines(f *os.File, counts map[string]int) {input := bufio.NewScanner(f)for input.Scan() {counts[input.Text()]++} }os.Open函數返回兩個值。第一個值是被打開的文件( *os.File),其后被 Scanner讀取。
os.Open 返回的第二個值是內置error類型的值。如果 err等于內置值 nil(譯注:相當于
其它語言里的NULL),那么文件被成功打開。讀取文件,直到文件結束,然后調用 Close關
閉該文件,并釋放占用的所有資源。相反的話,如果 err的值不是 nil,說明打開文件時出
錯了。這種情況下,錯誤值描述了所遇到的問題。我們的錯誤處理非常簡單,只是使
用 Fprintf與表示任意類型默認格式值的動詞 %v,向標準錯誤流打印一條信息,然
后 dup繼續處理下一個文件; continue語句直接跳到 for循環的下個迭代開始執行。
為了使示例代碼保持合理的大小,本書開始的一些示例有意簡化了錯誤處理,顯而易見的
是,應該檢查 os.Open返回的錯誤值,然而,使用 input.Scan讀取文件過程中,不大可能出
現錯誤,因此我們忽略了錯誤處理。我們會在跳過錯誤檢查的地方做說明。5.4節中深入介紹
錯誤處理。
注意 countLines函數在其聲明前被調用。函數和包級別的變量(package-level entities)可
以任意順序聲明,并不影響其被調用。(譯注:最好還是遵循一定的規范)
map是一個由 make 函數創建的數據結構的引用。 map作為為參數傳遞給某函數時,該函數
接收這個引用的一份拷貝(copy,或譯為副本),被調用函數對 map 底層數據結構的任何修
改,調用者函數都可以通過持有的 map引用看到。在我們的例子中, countLines函數
向 counts插入的值,也會被 main函數看到。(譯注:類似于C++里的引用傳遞,實際上指
針是另一個指針了,但內部存的值指向同一塊內存)
dup的前兩個版本以"流”模式讀取輸入,并根據需要拆分成多個行。理論上,這些程序可以處
理任意數量的輸入數據。還有另一個方法,就是一口氣把全部輸入數據讀到內存中,一次分
割為多行,然后處理它們。下面這個版本, dup3,就是這么操作的。這個例子引入
了 ReadFile函數(來自 io/ioutil 包),其讀取指定文件的全部內容, strings.Split 函
數把字符串分割成子串的切片。( Split 的作用與前文提到的 strings.Join 相反。)
我們略微簡化了 dup3。首先,由于 ReadFile函數需要文件名作為參數,因此只讀指定文
件,不讀標準輸入。其次,由于行計數代碼只在一處用到,故將其移回 main函數。
gopl.io/ch1/dup3
package mainimport ("fmt""io/ioutil""os""strings" )func main() {counts := make(map[string]int)for _, filename := range os.Args[1:] {data, err := ioutil.ReadFile(filename)if err != nil {fmt.Fprintf(os.Stderr, "dup3: %v\n", err)continue}for _, line := range strings.Split(string(data), "\n") {counts[line]++}}for line, n := range counts {if n > 1 {fmt.Printf("%d\t%s\n", n, line)}} }ReadFile函數返回一個字節切片(byte``````slice),必須把它轉換為 string ,才能
用 strings.Split分割。我們會在3.5.4節詳細講解字符串和字節切片。
實現上, bufio.Scanner、 ioutil.ReadFile和 ioutil.WriteFile都使
用 *os.File的 Read和 Write方法,但是,大多數程序員很少需要直接調用那些低級
(lower-level)函數。高級(higher-level)函數,像 bufio和 io/ioutil包中所提供的那
些,用起來要容易點。
1.4 GIF動畫
下面的程序會演示Go語言標準庫里的image這個package的用法,我們會用這個包來生成一系
列的bit-mapped圖,然后將這些圖片編碼為一個GIF動畫。我們生成的圖形名字叫利薩如圖形
(Lissajous figures),這種效果是在1960年代的老電影里出現的一種視覺特效。它們是協振子
在兩個緯度上振動所產生的曲線,比如兩個sin正弦波分別在x軸和y軸輸入會產生的曲線。圖
1.1是這樣的一個例子:
譯注:要看這個程序的結果,需要將標準輸出重定向到一個GIF圖像文件(使用 ./lissajous
> output.gif 命令)。下面是GIF圖像動畫效果:
這段代碼里我們用了一些新的結構,包括const聲明,struct結構體類型,復合聲明。和我們舉
的其它的例子不太一樣,這一個例子包含了浮點數運算。這些概念我們只在這里簡單地說明
一下,之后的章節會更詳細地講解。
gopl.io/ch1/lissajous
package mainimport ("image""image/color""image/gif""io""math""math/rand""os""time" )var palette = []color.Color{color.White, color.Black}const (whiteIndex = 0 // first color in paletteblackIndex = 1 // next color in palette )func main() {// The sequence of images is deterministic unless we seed// the pseudo-random number generator using the current time.// Thanks to Randall McPherson for pointing out the omission.rand.Seed(time.Now().UTC().UnixNano())lissajous(os.Stdout) } func lissajous(out io.Writer) {const (cycles = 5 // number of complete x oscillator revolutionsres = 0.001 // angular resolutionsize = 100 // image canvas covers [-size..+size]nframes = 64 // number of animation framesdelay = 8 // delay between frames in 10ms units)freq := rand.Float64() * 3.0 // relative frequency of y oscillatoranim := gif.GIF{LoopCount: nframes}phase := 0.0 // phase differencefor i := 0; i < nframes; i++ {rect := image.Rect(0, 0, 2*size+1, 2*size+1)img := image.NewPaletted(rect, palette)for t := 0.0; t < cycles*2*math.Pi; t += res {x := math.Sin(t)y := math.Sin(t*freq + phase)img.SetColorIndex(size+int(x*size+0.5), size+int(y*size+0.5),blackIndex)}phase += 0.1anim.Delay = append(anim.Delay, delay)anim.Image = append(anim.Image, img)}gif.EncodeAll(out, &anim) // NOTE: ignoring encoding errors }當我們import了一個包路徑包含有多個單詞的package時,比如image/color(image和color兩
個單詞),通常我們只需要用最后那個單詞表示這個包就可以。所以當我們寫color.White
時,這個變量指向的是image/color包里的變量,同理gif.GIF是屬于image/gif包里的變量。
這個程序里的常量聲明給出了一系列的常量值,常量是指在程序編譯后運行時始終都不會變
化的值,比如圈數、幀數、延遲值。常量聲明和變量聲明一般都會出現在包級別,所以這些
常量在整個包中都是可以共享的,或者你也可以把常量聲明定義在函數體內部,那么這種常
量就只能在函數體內用。目前常量聲明的值必須是一個數字值、字符串或者一個固定的
boolean值。
[]color.Color{...}和gif.GIF{...}這兩個表達式就是我們說的復合聲明(后面有說明)。這
是實例化Go語言里的復合類型的一種寫法。這里的前者生成的是一個slice切片,后者生成的
是一個struct結構體。
gif.GIF是一個struct類型(參考4.4節)。struct是一組值或者叫字段的集合,不同的類型集合
在一個struct可以讓我們以一個統一的單元進行處理。anim是一個gif.GIF類型的struct變量。
這種寫法會生成一個struct變量,并且其內部變量LoopCount字段會被設置為nframes;而其
它的字段會被設置為各自類型默認的零值。struct內部的變量可以以一個點(.)來進行訪問,就
像在最后兩個賦值語句中顯式地更新了anim這個struct的Delay和Image字段。
lissajous函數內部有兩層嵌套的for循環。外層循環會循環64次,每一次都會生成一個單獨的
動畫幀。它生成了一個包含兩種顏色的201*201大小的圖片,白色和黑色。所有像素點都會被
默認設置為其零值(也就是調色板palette里的第0個值),這里我們設置的是白色。每次外層
循環都會生成一張新圖片,并將一些像素設置為黑色。其結果會append到之前結果之后。這
里我們用到了append(參考)內置函數,將結果append到anim中的幀列表末尾,并設置一
個默認的80ms的延遲值。循環結束后所有的延遲值被編碼進了GIF圖片中,并將結果寫入到
輸出流。out這個變量是io.Writer類型,這個類型支持把輸出結果寫到很多目標,很快我們就
可以看到例子。
內層循環設置兩個偏振值。x軸偏振使用sin函數。y軸偏振也是正弦波,但其相對x軸的偏振是
一個0-3的隨機值,初始偏振值是一個零值,隨著動畫的每一幀逐漸增加。循環會一直跑到x
軸完成五次完整的循環。每一步它都會調用SetColorIndex來為(x, y)點來染黑色。
main函數調用lissajous函數,用它來向標準輸出流打印信息,所以下面這個命令會像圖1.1中
產生一個GIF動畫。
第二章、編程語言
上面說了那么多,后面我們來步入正題
編程語言介紹
2.1、什么是編程語言
編程語言是用來控制計算機的一系列指令(Instruction),它有固定的格式和詞匯(不同編程語言的格式和詞匯不一樣)。就像我們中國人之間溝通需要漢語,英國人溝通需要英語一樣,人與計算機之間進行溝通需要一門語言作為介質,即編程語言。
編程語言的發展經歷了機器語言(指令系統)=>匯編語言=>高級語言(C、java、Go等)。
010010101001-》ADD
計算機在設計中規定了一組指令(二級制代碼),這組指令的集和就是所謂的機器指令系統,用機器指令形式編寫的程序稱為機器語言。
但由于機器語言的千上萬條指令難以記憶,并且維護性和移植性都很差,所以在機器語言的基礎上,人們提出了采用字符和十進制數代替二進制代碼,于是產生了將機器語言符號化的匯編語言。
雖然匯編語言相較于機器語言簡單了很多,但是匯編語言是機器指令的符號化,與機器指令存在著直接的對應關系,無論是學習還是開發,難度依然很大。所以更加接近人類語言,也更容易理解和修改的高級語言就應運而生了,高級語言的一條語法往往可以代替幾條、幾十條甚至幾百條匯編語言的指令。因此,高級語言易學易用,通用性強,應用廣泛。
2.2、編譯型語言與解釋性語言
計算機是不能理解高級語言的,更不能直接執行高級語言,它只能直接理解機器語言,所以使用任何高級語言編寫的程序若想被計算機運行,都必須將其轉換成計算機語言,也就是機器碼。而這種轉換的方式分為編譯和解釋兩種。由此高級語言也分為編譯型語言和解釋型語言。
#mermaid-svg-v0t1bDbT54Lv99za {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-v0t1bDbT54Lv99za .error-icon{fill:#552222;}#mermaid-svg-v0t1bDbT54Lv99za .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-v0t1bDbT54Lv99za .edge-thickness-normal{stroke-width:2px;}#mermaid-svg-v0t1bDbT54Lv99za .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-v0t1bDbT54Lv99za .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-v0t1bDbT54Lv99za .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-v0t1bDbT54Lv99za .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-v0t1bDbT54Lv99za .marker{fill:#333333;stroke:#333333;}#mermaid-svg-v0t1bDbT54Lv99za .marker.cross{stroke:#333333;}#mermaid-svg-v0t1bDbT54Lv99za svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-v0t1bDbT54Lv99za .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-v0t1bDbT54Lv99za .cluster-label text{fill:#333;}#mermaid-svg-v0t1bDbT54Lv99za .cluster-label span{color:#333;}#mermaid-svg-v0t1bDbT54Lv99za .label text,#mermaid-svg-v0t1bDbT54Lv99za span{fill:#333;color:#333;}#mermaid-svg-v0t1bDbT54Lv99za .node rect,#mermaid-svg-v0t1bDbT54Lv99za .node circle,#mermaid-svg-v0t1bDbT54Lv99za .node ellipse,#mermaid-svg-v0t1bDbT54Lv99za .node polygon,#mermaid-svg-v0t1bDbT54Lv99za .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-v0t1bDbT54Lv99za .node .label{text-align:center;}#mermaid-svg-v0t1bDbT54Lv99za .node.clickable{cursor:pointer;}#mermaid-svg-v0t1bDbT54Lv99za .arrowheadPath{fill:#333333;}#mermaid-svg-v0t1bDbT54Lv99za .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-v0t1bDbT54Lv99za .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-v0t1bDbT54Lv99za .edgeLabel{background-color:#e8e8e8;text-align:center;}#mermaid-svg-v0t1bDbT54Lv99za .edgeLabel rect{opacity:0.5;background-color:#e8e8e8;fill:#e8e8e8;}#mermaid-svg-v0t1bDbT54Lv99za .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-v0t1bDbT54Lv99za .cluster text{fill:#333;}#mermaid-svg-v0t1bDbT54Lv99za .cluster span{color:#333;}#mermaid-svg-v0t1bDbT54Lv99za div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-v0t1bDbT54Lv99za :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 編譯型語言代碼 編譯器 可執行文件 操作系統+CPU 解釋型語言源代碼 解釋器- 編譯型語言
使用專門的編譯器,針對特定的平臺,將高級語言源代碼一次性的編譯成可被該平臺硬件執行的機器碼,并包裝成該平臺所能識別的可執行性程序的格式。
編譯型語言寫的程序執行之前,需要一個專門的編譯過程,把源代碼編譯成機器語言的文件,如exe格式的文件,以后要再運行時,直接使用編譯結果即可,如直接運行exe
文件。因為只需編譯一次,以后運行時不需要編譯,所以編譯型語言執行效率高。
1、一次性的編譯成平臺相關的機器語言文件,運行時脫離開發環境,運行效率高;
2、與特定平臺相關,一般無法移植到其他平臺;
- 解釋型語言
使用專門的解釋器對源程序逐行解釋成特定平臺的機器碼并立即執行。是代碼在執行時才被解釋器一行行動態翻譯和執行,而不是在執行之前就完成翻譯。
1.解釋型語言每次運行都需要將源代碼解釋稱機器碼并執行,執行效率低;
2.只要平臺提供相應的解釋器,就可以運行源代碼,所以可以方便源程序移植;
第三章、Go安裝
3.1、GO編譯器下載
– 官網:https://golang.google.cn/
– go中文網:https://studygolang.com/dl
或者聯系博主
3.2 MAC系統
mac下安裝SDK
作者沒用過mac
可以參考此文檔,點擊跳轉
3.3 WIndows系統
3.3.1 Windows下安裝SDK
雙擊我們下載好的Go語言開發包即可啟動安裝程序,如下圖所示,這是Go語言的用戶許可協議,無需管它,直接勾選“I accept
…”然后點擊“Next”即可。
安裝完成后,在我們所設置的安裝目錄下將生成一些目錄和文件,如下圖所示:
在默認情況下,win系統下Go 將會被安裝在目錄 c:\go
下,但如果你在安裝過程中修改安裝目錄,則需要手動修改所有的環境變量的值。
通過go env命令可以查看環境變量的所有情況。值得一提的是,GOROOT 表示 Go 開發包的安裝目錄。
國內Go語言庫鏡像:https://github.com/goproxy/goproxy.cn 在終端輸入:go env -w GOPROXY=https://goproxy.cn,direct
對代理進行修改。
GOPROXY https://proxy.golang.org,direct
阿里云: export GOPROXY=https://mirrors.aliyun.com/goproxy/
七牛云: export GOPROXY= https://goproxy.cn
go env -w “GO111MODULE=off” // 關閉go mod
3.3.2、Windows下配置環境
GOPATH 是 Go語言中使用的一個環境變量,它使用絕對路徑提供項目的工作目錄(workspace)。
GOPATH下創建src文件夾,即存放Go項目代碼的位置
開發包安裝完成后,我們還需要配置一下GOPATH環境變量,之后才可以使用Go語言進行開發。GOPATH
是開發人員編寫Go程序的工作空間路徑,也就是存放Go代碼的地方。
在桌面或者資源管理器右鍵“此電腦”(或者“我的電腦”)→“屬性”→“高級系統設置”→“環境變量”,如下圖所示。
在彈出的菜單里找到 GOPATH對應的選項點擊編輯之后就可以修改了,沒有的話可以選擇新建,并將變量名填寫為
GOPATH,變量值設置為任意目錄均可(盡量選擇空目錄),例如 F:\GoWork。
GOPATH對應創建的文件夾中里面,手動創建如下3個目錄
src 存儲go的源代碼(需要我們自己手動創建)
pkg 存儲編譯后生成的包文件 (自動生成)
bin 存儲生成的可執行文件(自動生成)
3.4、IDE的安裝與使用
安裝IDEA
GoLand是Jetbrains公司推出專為Go開發人員構建的跨平臺IDE,可以運行在Windows,Linux,macOS系統之上,
-聯系博主拿激活碼以及軟件,也可以前往官網自行下載 官方下載
-環境配置請參考-----參考文檔
3.5、idea快捷鍵
| Ctrl + / | 單行注釋 |
| Ctrl + Shift + / | 多行注釋 |
| Ctrl + D | 復制當前光標所在行 |
| Ctrl + X | 刪除當前光標所在行 |
| Ctrl + Alt + L | 格式化代碼 |
| Ctrl + Shift + | 方向鍵上或下 將光標所在的行進行上下移動(也可以使用 Alt+Shift+方向鍵上或下) |
| Ctrl + Alt + left/right | 返回至上次瀏覽的位置 |
| Ctrl + R | 替換 |
| Ctrl + F | 查找文本 |
| Ctrl + Shift + F | 全局查找 |
第四章、基礎語法
4.1、注釋
注釋就是對代碼的解釋和說明,其目的是讓人們能夠更加輕松地了解代碼。注釋是開發人員一個非常重要的習慣,也是專業的一種表現。單行注釋是最常見的注釋形式,你可以在任何地方使用以 //
開頭的單行注釋。多行注釋也叫塊注釋,均已以 /* 開頭,并以 */ 結尾。
注釋在代碼運行時不會被運行,
注釋是寫給人看的,不是寫給機器看的
4.2 變量
在計算機編程中,我們用變量來保存并管理很多數據,并用變量名來區分、識別和處理這些數據。
變量就是把數據,內存放到一個容器里面
4.2.1、GO聲明變量
和C語言一樣,Go語言也是通過var關鍵字進行聲明,不同的是變量名放在類型前,具體格式如下
var 變量名 變量類型 package mainimport "fmt"func main() {var x intvar s stringvar b boolfmt.Println(x) // 0fmt.Println(s) // ""fmt.Println(b) // false}Go里面聲明未賦值默認是零型
int的零型是 0
string—> “”
bool—> false
如果聲明多個變量,可以進行簡寫
package mainimport "fmt"func main() {var (name stringage int)fmt.Println(name, age) }4.2.2 變量賦值
GO中有三種賦值方式
- 一、變量名=值
- 二、變量名=變量名
- 三、變量名=值 + 值 (變量名)
4.2.3 匿名變量
匿名變量即沒有命名的變量,在使用多重賦值時,如果想要忽略某個值,可以使用匿名變量(anonymous variable)。
匿名變量用一個下劃線_表示
匿名變量不占用命名空間,不會分配內存
讓代碼非常清晰,基本上屏蔽掉了可能混淆代碼閱讀者視線的內容,從而大幅降低溝通的復雜度和代碼維護的難度。
4.2.4變量名命名的規則
變量命名是需要遵循一定的語法規范的,否則編譯器不會通過。
1、變量名稱必須由數字、字母、下劃線組成。
2、標識符開頭不能是數字。
3、標識符不能是保留字和關鍵字。
4、建議使用駝峰式命名,當名字有幾個單詞組成的時優先使用大小寫分隔
5、變量名盡量做到見名知意。
6、變量命名區分大小寫
go語言中有25個關鍵字,不能用于自定義變量名
| break | default | func | interface | select |
| case | defer | go | map | struct |
| chan | else | goto | package | switch |
| const | fallthrough | if | range | type |
| continue | for | import | return | var |
都會在后面一一提到
還有30多個預定義的名字,用于內建的常量、類型和函數
// 內建常量:
| false | int | float32 | make |
| iota | int8 | float64 | len |
| nil | int16 | complex128 | cap |
| int32 | complex64 | new | |
| int64 | bool | append | |
| uint | byte | copy | |
| uint8 | rune | close | |
| uint16 | string | delete | |
| uint32 | error | complex | |
| uint64 | real | ||
| uintptr | imag | ||
| panic | |||
| recover |
4.3基本數據類型
基本數據類型包含整型和浮點型,布爾類型以及字符串,這幾種數據類型在幾乎所有編程語言中都支持。
4.3.1整型
int8 : -127~127 \\2的8次方個數
uint8 : 0~255
等等
int 在32位系統上就用的int32
int 在64位系統上就用的int64
int8: 一個字節 [-127~128]
uint8 : 從0開始[0~255]
| int8 | -127到127 |
| int16 | -32768到32767 |
| int32 | -2147483648到2147483647 |
| uint32 | 0到4294967295 |
| int64 | -9223372036854775808到9223372036854775807 |
| uint64 | 0到18446744073709551615 |
| uint | 與平臺相關,32位操作系統上就是uint32,64位操作系統上就是uint64 |
| int | 與平臺相關,32位操作系統上就是int32,64位操作系統上就是int64 |
4.3.2浮點型
- float類型
float類型分為float32和float64兩種類型,這兩種浮點型數據格式遵循 IEEE 754 標準。
單精度浮點數占用4個字節(32位)存儲空間來存儲一個浮點數。而雙精度浮點數使用 8個字節(64位)存儲空間來存儲一個浮點數。
單精度浮點數最多有7位十進制有效數字,如果某個數的有效數字位數超過7位,當把它定義為單精度變量時,超出的部分會自動四舍五入。雙精度浮點數可以表示十進制的15或16位有效數字,超出的部分也會自動四舍五入。
浮點類型默認聲明為float64。
4.3.3布爾類型
布爾類型是最基本數據類型之一,只有兩個值:true和false,分別代表邏輯判斷中的真和假,主要應用在條件判斷中
var b bool // 聲明b是一個布爾類型b = trueb = false // 該類型只有true和false兩個值,分別代表真假兩種狀態fmt.Println(b, reflect.TypeOf(b))fmt.Println(1 == 1) // 比較運算符的結果是一個布爾值// fmt.Println(1 == "1") // 報錯,mismatched types不能比較fmt.Println(3 > 1)var name = "yuan"var b2 = name == "rain"//false ****fmt.Println(b2)4.3.4字符串類型
字符串是最基本也是最常用的數據類型,是通過雙引號將多個字符按串聯起來的一種數據,用于展示文本單引號只能標識字符,注意:
索引從零開始計數
go語言不支持負索引
4.3.4.1 字符串常用方法
| len(str) | 求長度 |
| strings.ToUpper,strings.ToLower | 生成一個新的全部大寫的字符串,生成一個新的全部小寫的字符串 |
| strings.ReplaceAll | 生成一個新的原字符串被指定替換后的字符串 |
| strings.Contains | 判斷是否包含 |
| strings.HasPrefix,strings.HasSuffix | 前綴/后綴判斷 |
| strings.Trim、 | 去除字符串兩端匹配的內容 |
| strings.Index(),strings.LastIndex() | 子串出現的位置 |
| strings.Split | 分割,將字符串按指定的內容分割成數組 |
| strings.Join(a[]string, sep string) | join操作,將數組按指定的內容拼接成字符串 |
4.3.5轉義符
| \r | 回車符(返回行首) |
| \n | 換行符(直接跳到下一行的同列位置) |
| \t | 制表符 |
| \’ | 單引號 |
| \" | 雙引號 |
| \\ | 反斜杠 |
4.3.6 進制轉換
// 十進制轉化var n int = 10//printf 傳輸數據進去fmt.Printf("%d \n", n) //%d 表示十進制fmt.Printf("%o \n", n) // 占位符%o表示八進制fmt.Printf("%b \n", n) //占位符%b表示二進制fmt.Printf("%x \n", n) //占位符%x表示十六進制// 八進制轉化var b int = 020fmt.Printf("%o \n", b) // 20fmt.Printf("%d \n", b) // 16fmt.Printf("%x \n", b) // 10fmt.Printf("%b \n", b) // 10000// 十六進制轉化var c = 0x12fmt.Printf("%d \n", c) // 18fmt.Printf("%o \n", c) // 22fmt.Printf("%x \n", c) // 12fmt.Printf("%b \n", c) // 100104.3.7 數據類型轉換
//(1)整型之間的轉換 int8 int16var x int8 = 10var y int16 = 20fmt.Println(x + int8(y))// (2)字符串與整型之間的轉換 strconv庫var agestr = "32"//var name = 12//字符串轉整型var age, _ = strconv.Atoi(agestr)fmt.Println(age)//fmt.Println("err: ", err) // <nil>空對象price := 100//整形轉字符price_str := strconv.Itoa(price)fmt.Println(price_str, reflect.TypeOf(price_str))//strconv parse系列函數//字符串轉整型 base進制 bitSize是比特位 8---int8ret, _ := strconv.ParseInt("28", 10, 8)fmt.Println(ret, reflect.TypeOf(ret))//字符串轉換為浮點型floats, _ := strconv.ParseFloat("3.1415926", 64)fmt.Println(floats, reflect.TypeOf(floats))//字符串轉換為布爾值b, _ := strconv.ParseBool("0")b1, _ := strconv.ParseBool("-1")b2, _ := strconv.ParseBool("true")b3, _ := strconv.ParseBool("T")fmt.Println(b, b1, b2, b3)4.3.8 運算符
和python運算符一樣
//計算一個數是為奇數還是偶數x, y := 10, 20fmt.Println(x%2 == 0, y)//關系運算符 與python相同 == != >= <= 返回布爾值fmt.Println(x >= y)//邏輯運算符//與或非運算/*與&&: 真真-真,真假-假,假假-假 ,或||: 真或真-真,真或假為真,假或假為假非運算 !:非真為假,非假為真 取反*/fmt.Println(true && false)fmt.Println(true || false)fmt.Println(!true || false)//database:root 123username := "zhang"password := 123fmt.Println(username == "root" && password == 123)/*賦值運算和python一樣*/var a = 12a += 1fmt.Println(a)var b = 10//自加一b++fmt.Println(b)//優先級var q, w, z = 1, 2, 3fmt.Println(q, w, z)var t = q + wfmt.Println(t)4.3.9 輸入輸出函數
4.3.9.1 輸出函數
fmt.Print有幾個變種:
Print: 輸出到控制臺,不接受任何格式化操作Println: 輸出到控制臺并換行Printf : 只可以打印出格式化的字符串,只可以直接輸出字符串類型的變量(不可以輸出別的類型) Sprintf:格式化并返回一個字符串而不帶任何輸出
//輸出函數//print printlnvar name, age = "yuan", 32fmt.Println("hello world")fmt.Println(name)fmt.Println(age)fmt.Println("姓名:", name, "年齡", age)//fmt.Print(name)//fmt.Print(age)var isMarried = falsefmt.Printf("姓名:%s,年齡:%d,婚否:%t\n", name, age, isMarried)//sprintf:s := fmt.Sprintf("姓名:%s,年齡:%d,婚否:%t", name, age, isMarried)fmt.Println(s)4.3.9.2 輸入函數
go語言fmt包下有三個函數,可以在程序運行過程中從標準輸入獲取用戶的輸入:
1、fmt.Scan
2、fmt.Scanf
3、fmt.Scanln
1、語法:
func Scan(a ...interface{}) (n int, err error) var (birth string)fmt.Println("輸入生日格式如:1988-3-16")fmt.Scan(&birth)birthslice := strings.Split(birth, "-")fmt.Printf("您的生日是%s年-%s月-%s日", birthslice[0], birthslice[1], birthslice[2])2、語法:
func Scanf(format string, a ...interface{})(n int, err error) //(2)fmt.scanf 按照指定的格式輸入var a, b intfmt.Scanf("%d+%d", &a, &b)fmt.Println(a + b)3、語法、
func Scanln(a ...interface{}) (n int, err error)Scanln類似于Scan,它遇到換行立即停止掃描。
本函數返回成功掃描的數據個數和遇到的任何錯誤。
Scanln和Scan的區別就是Scanln遇到換行立即結束輸入,而Scan則會將換行符作為一個空白符繼續下一個輸入
4.3.10 常量與iota
常量是?個簡單值的標識符,在程序運?時,不會被修改的量。 在Python、Java編程規范中,常量?般都是全?寫字母,但是在Golang中,??寫是具有?定特殊含義的,所以不?定所有常量都得全?寫。
聲明賦值方式與變量接近,通過const實現
const 常量名[數據類型] = value數據類型可以忽略不寫,Golang編譯器會?動推斷出數據類型。 在使?時,要注意以下?點:
數據類型只可以是布爾型、數字型(整數型、浮點型和復數)和字符串型
滿?多重賦值
常量只定義不使?,編譯不會報錯
常量可以作為枚舉,常量組
常量組中如不指定類型和初始化值,則與上???空常量右值相同
顯?指定類型的時候,必須確保常量左右值類型?致,需要時可做顯?類型轉換。
4.3.10.1 iota計數器
iota是go語言的常量計數器,只能在常量的表達式中使用。 使用iota時只需要記住以下兩點
1.iota在const關鍵字出現時將被重置為0。
2.const中每新增一行常量聲明將使iota計數一次(iota可理解為const語句塊中的行索引)。
部分內容出自于《GO語言圣經》
總結
以上是生活随笔為你收集整理的Golang基础(1)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Object-C,文件路径API
- 下一篇: 程序员的命就是哭