Go 语言的基础组成有以下几个部分:
- 包声明
- 引入包
- 函数
- 变量
- 语句 & 表达式
- 注释
其中安装的时候测试的代码:
package main
import "fmt"
func main() {
/* 这是我的第一个简单的程序 */
fmt.Println("Hello, World!")
}
-
第一行代码 package main 定义了包名。你必须在源文件中非注释的第一行指明这个文件属于哪个包,如:package main。package main表示一个可独立执行的程序,每个 Go 应用程序都包含一个名为 main 的包。
-
下一行 import "fmt" 告诉 Go 编译器这个程序需要使用 fmt 包(的函数,或其他元素),fmt 包实现了格式化 IO(输入/输出)的函数。
-
下一行 func main() 是程序开始执行的函数。main 函数是每一个可执行程序所必须包含的,一般来说都是在启动后第一个执行的函数(如果有 init() 函数则会先执行该函数)。
-
下一行 /*...*/ 是注释,在程序执行时将被忽略。单行注释是最常见的注释形式,你可以在任何地方使用以 // 开头的单行注释。多行注释也叫块注释,均已以 /* 开头,并以 */ 结尾,且不可以嵌套使用,多行注释一般用于包的文档描述或注释成块的代码片段。
-
下一行 fmt.Println(...) 可以将字符串输出到控制台,并在最后自动增加换行字符 \n。
使用 fmt.Print("hello, world\n") 可以得到相同的结果。
Print 和 Println 这两个函数也支持使用变量,如:fmt.Println(arr)。如果没有特别指定,它们会以默认的打印格式将变量 arr 输出到控制台。 -
当标识符(包括常量、变量、类型、函数名、结构字段等等)以一个大写字母开头,如:Group1,那么使用这种形式的标识符的对象就可以被外部包的代码所使用(客户端程序需要先导入这个包),这被称为导出(像面向对象语言中的 public);标识符如果以小写字母开头,则对包外是不可见的,但是他们在整个包的内部是可见并且可用的(像面向对象语言中的 protected )。
————————————————————分割线————————————————————
以下按照个人习惯整理,主要参考:
http://c.biancheng.net/golang/intro/
一、数据类型
Go语言的基本类型有:
- bool
- string
- int、int8、int16、int32、int64
- uint、uint8、uint16、uint32、uint64、uintptr
- byte // uint8 的别名
- rune // int32 的别名 代表一个 Unicode 码
- float32、float64
- complex64、complex128
声明变量:
var name type
举例:
package main
import "fmt"
func main() {
var name string
name = "Tom"
fmt.Println(name)
var age int
age=20
fmt.Println(age)
}
输出:
这样写也OK:
package main
import "fmt"
func main() {
var name string = "Tom"
fmt.Println(name)
var age int = 20
fmt.Println(age)
}
“当一个变量被声明之后,系统自动赋予它该类型的零值:int 为 0,float 为 0.0,bool 为 false,string 为空字符串,指针为 nil 等。所有的内存在 Go 中都是经过初始化的。”
举例(只初始化,不赋值):
package main
import "fmt"
func main() {
var name string//字符串
fmt.Println(name)
var age int//整型
fmt.Println(age)
var flag bool//布尔型
fmt.Println(flag)
var inch float64//浮点型
fmt.Println(inch)
var point *int//指针
fmt.Println(point)
var score [10] int//数组
fmt.Println(score)
}
输出:
另有懒人特供版:
package main
import "fmt"
func main() {
var (
name string//字符串
age int//整型
flag bool//布尔型
inch float64//浮点型
point *int//指针
score [10] int//数组
)
fmt.Println(name)
fmt.Println(age)
fmt.Println(flag)
fmt.Println(inch)
fmt.Println(point)
fmt.Println(score)
}
像这样同时定义多个变量也是work的:
package main
import "fmt"
func main() {
var age1, age2, age3 int = 20, 21, 22
fmt.Println(age1)
fmt.Println(age2)
fmt.Println(age3)
fmt.Printf("%v %v %v\n", age1, age2, age3)
}
输出:
同时,即使不定义数据类型,Go也会自己判断(一个成熟的语言×):
package main
import (
"fmt"
"reflect"
)
func main() {
var (
name = "Tom"//字符串
age = 20//整型
)
fmt.Println(name)
fmt.Println(age)
fmt.Println("type:", reflect.TypeOf(name))
fmt.Println("type:", reflect.TypeOf(age))
}
输出:
其他声明变量的方式:
:=
package main
import "fmt"
func main() {
age := 20
fmt.Println(age)
}
输出:
注意:由于使用了
:=
,而不是赋值的=
,因此推导声明写法的左值变量必须是没有定义过的变量。若定义过,将会发生编译错误。
如果 hp 已经被声明过,但依然使用:=
时编译器会报错,代码如下:
package main
import "fmt"
func main() {
var age int
age := 20
fmt.Println(age)
}
报错信息如下:
GOROOT=null #gosetup
GOPATH=null #gosetup
go.exe build -i -o D:\SOFTware\GoLand\GOcode\go_build_awesomeProject_.exe . #gosetup
go build: -i flag is deprecated
# _/D_/SOFTware/GoLand/awesomeProject
.\hello.go:7:6: no new variables on left side of :=
Compilation finished with exit code 2
字符串操作:http://c.biancheng.net/view/17.html
二、运算符
注意:优先级值越大,表示优先级越高。
有关逻辑运算符:
下表列出了所有Go语言的逻辑运算符。假定 A 值为 True,B 值为 False。
运算符 | 描述 | 实例 |
---|---|---|
&& | 逻辑 AND 运算符。 如果两边的操作数都是 True,则条件 True,否则为 False。 | (A && B) 为 False |
|| | 逻辑 OR 运算符。 如果两边的操作数有一个 True,则条件 True,否则为 False。 | (A || B) 为 True |
! | 逻辑 NOT 运算符。 如果条件为 True,则逻辑 NOT 条件 False,否则为 True。 | !(A && B) 为 True |
其他:https://www.runoob.com/go/go-operators.html
三、数组
数组的声明语法如下:
var 数组变量名 [元素数量]Type
(可参见上文有关变量声明部分的举例)
var score [10] int
数组初始化
1.确定数组大小的:
package main
import "fmt"
func main() {
var age = [10] int {20, 21, 22, 23, 20, 21, 22, 23, 20, 21}
fmt.Println(age)
fmt.Println(age[0])
fmt.Println(age[9])
}
输出:
不过要注意,Go这里不具备和Python一样玩的条件,至少要保证索引值的正确。
比如,如果把age[0]和age[9]改成age[-1]和age[10]:
package main
import "fmt"
func main() {
var age = [10] int {20, 21, 22, 23, 20, 21, 22, 23, 20, 21}
fmt.Println(age)
fmt.Println(age[-1])
fmt.Println(age[10])
}
并不能输出倒数第一个,报错如下:
GOROOT=null #gosetup
GOPATH=null #gosetup
go.exe build -i -o D:\SOFTware\GoLand\GOcode\go_build_awesomeProject_.exe . #gosetup
go build: -i flag is deprecated
# _/D_/SOFTware/GoLand/awesomeProject
.\hello.go:8:17: invalid array index -1 (index must be non-negative)
.\hello.go:9:17: invalid array index 10 (out of bounds for 10-element array)
Compilation finished with exit code 2
2.不确定数组长度
可以使用 ... 代替数组的长度,编译器会根据元素个数自行推断数组的长度:
package main
import "fmt"
func main() {
var age = [...] int {20, 21, 22, 23, 20, 21, 22, 23, 20, 21}
fmt.Println(age)
fmt.Println(age[0])
fmt.Println(age[9])
}
输出:
要注意,此处虽然未确定数组长度,但是还是会因为索引值的问题报错的(仍以-1和10实验,报错如下)
go build: -i flag is deprecated
# _/D_/SOFTware/GoLand/awesomeProject
.\hello.go:8:17: invalid array index -1 (index must be non-negative)
.\hello.go:9:17: invalid array index 10 (out of bounds for 10-element array)
Compilation finished with exit code 2
修改数组中元素
直接改就可以了:
package main
import "fmt"
func main() {
var age = [...] int {20, 21, 22, 23, 20, 21, 22, 23, 20, 21}
age[1] = 30
fmt.Println(age)
fmt.Println(age[0])
fmt.Println(age[9])
}
输出:
多维数组
package main
import "fmt"
func main() {
var age = [3][4] int {
{0, 9, 8, 7},
{1, 2, 3, 4},
{5, 6, 7, 8},
}
fmt.Println(age)
fmt.Println(age[0])
fmt.Println(age[1])
fmt.Println(age[2])
}
输出:
其余部分见循环语句部分
四、条件语句
if
语法
if condition {
// do something
}
if condition {
// do something
} else {
// do something
}
if condition1 {
// do something
} else if condition2 {
// do something else
}else {
// catch-all or default
}
举例(参考https://blog.csdn.net/study_in/article/details/102919019生成伪随机数):
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().Unix())
var flag int
flag = rand.Intn(40)
fmt.Println(flag)
if flag < 20 {
fmt.Println("小于20")
} else if flag == 20{
fmt.Println("等于20")
} else {
fmt.Println("大于20")
}
}
输出:
switch
Go 编程语言中 switch 语句的语法如下:
switch var1 {
case val1:
...
case val2:
...
default:
...
}
举例:
package main
import (
"fmt"
"math/rand"
"time"
)
func main() {
rand.Seed(time.Now().Unix())
var flag int
flag = rand.Intn(40)
fmt.Println(flag)
switch{
case flag < 20:
fmt.Println("小于20")
case flag == 20:
fmt.Println("等于20")
default:
fmt.Println("大于20")
}
}
输出:
select
Go 编程语言中 select 语句的语法如下:
select {
case communication clause :
statement(s);
case communication clause :
statement(s);
/* 你可以定义任意数量的 case */
default : /* 可选 */
statement(s);
}
https://www.runoob.com/go/go-select-statement.html
这个例子没看懂
“
select语句类似于switch语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。
select可以监听channel的数据流动,用于处理异步IO操作,它最大的限制就是每个case语句里必须是一个IO操作(channel操作)
select是Go中的一个控制结构,类似于用于通信的switch语句。每个case必须是一个通信操作,要么是发送要么是接收。一个默认的子句应该总是可运行的。
————————————————
版权声明:本文为CSDN博主「Aaron_1997」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Aaron_1997/article/details/110196040
”
package main
import "fmt"
func main() {
var i, j int
var col1, col2 chan int
select{
case i = <- col1:
fmt.Println("received", i, "from col1")
case col2 <- j:
fmt.Println("sent", j, "to col2")
default:
fmt.Println("no communication")
}
}
输出:
五、循环语句
语法
与多数语言不同的是,Go语言中的循环语句只支持 for 关键字,而不支持 while 和 do-while 结构,关键字 for 的基本使用方法与C语言和 C++ 中非常接近:
sum := 0
for i := 0; i < 10; i++ {
sum += i
}
可以看到比较大的一个不同在于 for 后面的条件表达式不需要用圆括号()括起来,Go语言还进一步考虑到无限循环的场景,让开发者不用写无聊的 for(;;){}和do{} while(1);,而直接简化为如下的写法:
sum := 0
for {
sum++
if sum > 100 {
break
}
}
举例:
package main
import "fmt"
func main() {
var cnt[10] int
for i := 0; i < 10;i++ {
cnt[i] = i
}
fmt.Println(cnt)
}
输出:
嵌套:
package main
import "fmt"
func main() {
var cnt[10] int
var sum int
for i := 0; i < 10;i++ {
cnt[i] = i
for j :=10; j < 11; j++ {
sum = i+j
fmt.Println(sum)
}
}
fmt.Println(cnt)
}
输出:
另:
package main
import "fmt"
func main() {
var sum[10] int
for i := 0; i < 10;i++ {
for j :=10; j < 11; j++ {
sum[i] = i+j
fmt.Println(sum)
}
}
}
输出:
题外话:我感觉这样一点一点填进去好治愈
六、函数
Go 语言函数定义格式如下:
func function_name( [parameter list] ) [return_types] {
函数体
}
函数定义解析:
- func:函数由 func 开始声明
- function_name:函数名称,函数名和参数列表一起构成了函数签名。
- parameter list:参数列表,参数就像一个占位符,当函数被调用时,你可以将值传递给参数,这个值被称为实际参数。参数列表指定的是参数类型、顺序、及参数个数。参数是可选的,也就是说函数也可以不包含参数。
- return_types:返回类型,函数返回一列值。return_types 是该列值的数据类型。有些功能不需要返回值,这种情况下 return_types 不是必须的。
- 函数体:函数定义的代码集合。
举例:
package main
import "fmt"
func main() {
var (
string1 = "hello "
string2 = "world!"
)
hello_world(string1, string2)
}
func hello_world(str1, str2 string) (string, string){
fmt.Print(str1)
fmt.Print(str2)
return str1, str2
}
输出:
————————————————————分割线————————————————————
用之前注册白嫖的搜狗语音合成做个测试
package main
import (
"fmt"
"io/ioutil"
"net/http"
"strings"
)
func main() {
url := "https://api.zhiyin.sogou.com/apis/tts/v1/synthesize"
appid := "创建任务时得到的appid"
token := "鉴权得到的token"
payload := strings.NewReader(`{
"input": {
"text": "语音合成测试"
},
"config": {
"audio_config": {
"audio_encoding": "MP3",
"pitch": 1,
"volume": 1,
"speaking_rate": 1
},
"voice_config": {
"language_code": "zh-cmn-Hans-CN",
"speaker": "male"
}
}
}`)
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Appid", appid)
req.Header.Add("Authorization", "Bearer "+token)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
if res.StatusCode == http.StatusOK {
ioutil.WriteFile("audio.mp3", body, 0644)
fmt.Printf("save finish")
} else {
fmt.Println("err :", res.StatusCode)
}
}
输出得到的mp3文件属性:
鉴权代码:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"time"
)
func CreateToken(appid, appkey string, exp time.Duration) (string, error) {
reqbody, err := json.Marshal(map[string]string{
"appid": appid,
"appkey": appkey,
"exp": fmt.Sprintf("%ds", int(exp.Seconds())),
})
resp, err := http.Post(
"https://api.zhiyin.sogou.com/apis/auth/v1/create_token",
"application/json",
bytes.NewBuffer(reqbody))
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("http status: %s", resp.Status)
}
respbody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
var js struct {Token string}
err = json.Unmarshal(respbody, &js)
return js.Token, nil
}
func main() {
fmt.Println(CreateToken("appid,创建应用时获得", "appkey,创建应用时获得", time.Hour))
}
输出结果:
PS,搜狗给的demo就很坑,真的,开头就是
package sogouspeech
还没有调用,下面给的调用部分也莫名其妙(我删了uuid,所以少一个参数)
sogouspeech.CreateToken("your_app_id", "your_app_key", time.Hour, "can_be_empty")
会疯狂报错
GOROOT=null #gosetup
GOPATH=null #gosetup
go.exe build -i -o D:\SOFTware\GoLand\GOcode\go_build_awesomeProject_.exe -gcflags "-N -l" . #gosetup
go build: -i flag is deprecated
# _/D_/SOFTware/GoLand/awesomeProject
.\hello.go:46:14: undefined: sogouspeech
Compilation finished with exit code 2
——————————再次更新——————————
分开获得token和合成语音太过麻烦了,百度的demo都是写在一起的,所以为了能弄明白怎么把百度的demo迁移到go上,把搜狗的demo合在一起了。
如下:
package main
import (
"bytes"
"encoding/json"
"fmt"
"io/ioutil"
"net/http"
"strings"
"time"
)
func CreateToken(appid, appkey string, exp time.Duration) (string, error) {
/*json.Marshal:将某种数据结构生成为json字符串
Map 是一种无序的键值对的集合。Map 最重要的一点是通过 key
来快速检索数据,key 类似于索引,指向数据的值。
Map 是一种集合,所以我们可以像迭代数组和切片那样迭代它。不过,Map 是无序的,
我们无法决定它的返回顺序,这是因为 Map 是使用 hash 表来实现的。
*/
reqbody, err := json.Marshal(map[string]string{
"appid": appid,
"appkey": appkey,
"exp": fmt.Sprintf("%ds", int(exp.Seconds())),
})
/*
map定义
声明变量,默认map是nil
var map_variable map[kay_data_type]value_data_type
fmt.Sprintf 格式化输出
请求体
{
"appid": string,
"appkey": string,
"exp": string,
"uuid": string
}
exp:访问令牌失效时间,有效范围0-10小时。如一小时为“3600s”,1分钟为“60s”
注:本字符串格式是 google.protobuf.Duration 类型的 JSON 序列化,
根据相关标准,时间应格式化为类似 "123s",即数字 + 后缀s,表示多少秒。
*/
resp, err := http.Post(
"https://api.zhiyin.sogou.com/apis/auth/v1/create_token",
"application/json",
bytes.NewBuffer(reqbody))
/*
如果在使用bytes.NewBuffer的时候参数是[]byte的slice的话
缓冲器里就是这个slice的内容,如果参数是nil的话,意思是New一个空的缓冲器里
*/
if err != nil {
return "", err
}
/*
if err != nil {
// do something....
}
当出现不等于nil的时候,说明出现某些错误了,需要我们对这个错误进行一些处理,
而如果等于nil说明运行正常。
注:
每种类型对应的零值:
bool--> false
numbers--> 0
string--> ""
pointers-->nil
slices-->nil
map-->nil
channels-->nil
functions-->nil
interfaces-->nil
*/
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return "", fmt.Errorf("http status: %s", resp.Status)
}
respbody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
var js struct {Token string}
err = json.Unmarshal(respbody, &js)
return js.Token, nil
}
func main() {
//var token_str string
token, err_str := CreateToken("获得的appid", "获得的appkey", time.Hour)
fmt.Println(token)
fmt.Println(err_str)
url := "https://api.zhiyin.sogou.com/apis/tts/v1/synthesize"
appid := "获得的appid"
payload := strings.NewReader(`{
"input": {
"text": "搜狗语音合成测试文本"
},
"config": {
"audio_config": {
"audio_encoding": "MP3",
"pitch": 1,
"volume": 1,
"speaking_rate": 1
},
"voice_config": {
"language_code": "zh-cmn-Hans-CN",
"speaker": "male"
}
}
}`)
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Content-Type", "application/json")
req.Header.Add("Appid", appid)
req.Header.Add("Authorization", "Bearer "+token)
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := ioutil.ReadAll(res.Body)
if res.StatusCode == http.StatusOK {
ioutil.WriteFile("audio.mp3", body, 0644)
fmt.Printf("save finish")
} else {
fmt.Println("err :", res.StatusCode)
}
}
输出:
输出音频在代码所在的文件夹