Go语言基础

Go语言是一种适合服务器编程、微服务开发和云平台构建的语言,其特点是静态强类型、编译型和并发型。它支持丰富的数据结构,如数组、切片、结构体和映射,以及并发机制如goroutine和通道。此外,Go语言提供了内置的垃圾回收功能,简化了内存管理。文章还介绍了变量定义、常量、函数、结构体、接口、并发以及文件操作等核心概念。
摘要由CSDN通过智能技术生成

GO语言

go是一种静态强类型、编译型、并发型,并具有垃圾回收功能的编程语言。

Go应用领域

Go语言作为服务器编程语言,很适合处理日志、数据打包、虚拟机处理、文件系统、分布式系统、数据库代理等;网络编程方面,Go语言广泛应用于Web应用、API应用、下载应用等;除此之外,Go语言还适用于内存数据库和云平台领域,目前国外很多云平台都是采用Go开发。

  • 服务器编程

以前你如果使用C或者C++做的那些事情,用Go来做很合适,例如处理日志、数据打包、虚拟机处理、文件系统等。

  • 微服务

现在越来越多新的项目采用微服务架构,前面介绍的优秀项目中也看到很多Go提供的微服务框架,如git-kit、go-micro等。

举些具体公司的例子。

比如今日头条,2017的一篇文章今日头条使用Go构建了千万级微服务,其中说到有80%的流量都跑在Go上,其中提到头条还开发了自己的一套微服务框架;还有B站也是采用的Go开发。

  • 云平台

云服务很多都是采用Go进行开发,比如国内著名的七牛云是全站采用Go开发;还有如盛大CDN、阿里云CDN,华为云等。而且云平台基础设施如docker、kubernetes等也是Go开发;

国内大厂开源的Golang项目

  • 字节跳动:kitex,开源
  • 滴滴:有自己的一套微服务框架,未开源
  • 腾讯:TarsGo,开源,并且是Linux基金会项目
  • B站:kratos,开源
  • 斗鱼:Jupiter,开源
  • 好未来:go-zero,开源
  • 阿里:dubbo-go,开源
  • 华为:Go Chassis,开源
  • 知乎:内部改用go重构后端

定义变量

使用var关键字是Go最基本的定义变量方式

//定义一个名称为“variableName”,类型为"type"的变量
var variableName type

定义多个变量

//定义三个类型都是“type”的变量
var vname1, vname2, vname3 type

定义变量并初始化值

//初始化“variableName”的变量为“value”值,类型是“type”
var variableName type = value

同时初始化多个变量

var vname1, vname2, vname3 type= v1, v2, v3

忽略类型声明:

var vname1, vname2, vname3 = v1, v2, v3

继续简化:

vname1, vname2, vname3 := v1, v2, v3

:=这个符号直接取代了vartype,这种形式叫做简短声明。不过它有一个限制,那就是它只能用在函数内部;在函数外部使用则会无法编译通过,所以一般用var方式来定义全局变量。

_(下划线)是个特殊的变量名,任何赋予它的值都会被丢弃。在这个例子中,我们将值35赋予b,并同时丢弃34

_, b := 34, 35

Go对于已声明但未使用的变量会在编译阶段报错,比如下面的代码就会产生一个错误:声明了i但未使用。

package main

func main() {
	var i int
}

定义常量

所谓常量,也就是在程序编译阶段就确定下来的值,程序在运行时无法改变该值。在Go程序中,常量可定义为数值、布尔值或字符串等类型。

它的语法如下:

const constantName [type] = value
//可以明确指定常量的类型:
const Pi float32 = 3.1415926

下面是一些常量声明的例子:

package main

import "fmt"

func main() {
   const LENGTH int = 10
   const WIDTH int = 5  
   var area int
   const a, b, c = 1, false, "str" //多重赋值

   area = LENGTH * WIDTH
   fmt.Printf("面积为 : %d", area)
   println()
   println(a, b, c)  
}

数据结构

go语言数据结构有四大类:1、基础类型,包括整型(有符号和无符号整数)、浮点数、复数、字符串(由不可变的字节序列构成)、布尔值(只有true和false两个值);2、聚合类型,包括数组、结构体(是由任意个任意类型的变量组合在一起的数据类型);3、引用类型,包括指针、slice(是一个拥有相同元素的可变长度序列)、map、function、channel;4、接口类型。

整型

有符号整数会分成 int8、int16、int32、int64

无符号整数会分成 uint8、uint16、uint32、uint64

但通常会直接使用 int 和 uint,因为这两种类型会根据平台的特性,自动转成运算效率最高的类型

浮点数

float32 的范围是 1.4e-45 ~ 3.4e38

float64 的范围是 4.9e-324 ~ 1.8e308

复数

complex128(64 位实数和虚数)和 complex64(32 位实数和虚数),其中 complex128 为复数的默认类型。

var name complex128 = complex(x, y)
//上面的声明语句也可以简写为下面的形式
name := complex(x, y)

var x complex128 = complex(1, 2) // 1+2i
var y complex128 = complex(3, 4) // 3+4i
fmt.Println(x*y)                 // "(-5+10i)"
fmt.Println(real(x*y))           // "-5"
fmt.Println(imag(x*y))           // "10"
布尔

只有两个值: truefalse

字符串

字符串由不可变的字节序列([]byte)构成,字符串的内容通常会用 UTF-8 的编码格式处理。

Go 的字符串内容是不可变的,对当前字符串做的操作都会生成一个新的字符串。Go 字符串天然支持 UTF-8,而且习惯上也会优先使用这种编码方式,乱码的烦恼会少一些。

Go 语言也为字符串提供了丰富的类库:

  • bytes:用于操作字节slice([]byte),如果需要对字符串进行频繁的修改, 使用 byte.Buffer 会高效
  • strings:用于搜索、替换等等字符传操作
  • strconv:主要用于字符串和其他基本数据类型之间的转换
  • unicode:用来判断字符的特性,比如是否是数字、是否大写等等

聚合类型的值由内存中的一组变量构成。数组和结构体都是聚合类型,数组和结构体的长度都是固定的。数组中的的元素类型必须都相同,而结构体中的元素可以不同。

数组

数组是一个长度固定,拥有0 个或多个(不超过数组长度)相同数据类型的序列。数组在声明的时候必须指定长度,可以使用常量,但是不能使用变量。

实际上,我们很少在代码中直接使用数组,数组在绝大部分的时候都是做为 slice 的底层存储,并不会直接使用。

数组的可比较性取决于元素的可比较性,如果元素是可比较的,那么数组也是可比较的,反之亦然。

//声明数组
var variable_name [SIZE] variable_type
var balance [10] float32
//初始化数组
var balance = [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
balance := [5]float32{1000.0, 2.0, 3.4, 7.0, 50.0}

如果数组长度不确定,可以使用 ... 代替数组的长度,编译器会根据元素个数自行推断数组的长度:

var balance = [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}
//或
balance := [...]float32{1000.0, 2.0, 3.4, 7.0, 50.0}

如果设置了数组的长度,我们还可以通过指定下标来初始化元素:

//将索引为 1 和 3 的元素初始化
balance := [5]float32{1:2.0,3:7.0}

获取数组中的元素

var salary float32 = balance[9]
salary := balance[9]
切片(slice)

切片(slice)是对数组的一个连续片段的引用,所以切片是一个引用类型

  • 切片的声明
var identifier []type
//使用make()函数创建切片
var slice []type = make([]type, len)
//也可以简写为
slice := make([]type, len)
//也可以指定容量,其中 capacity 为可选参数
make([]T, length, capacity)
  • 切片初始化
//直接初始化
s :=[] int {1,2,3}
//从数组或切片中生成新的切片
slice [开始位置 : 结束位置]

slice := []int{0,1,2,3,4,5,6,7,8}
//新切片从索引1(包含) 到索引4(不包含)
newSlice := slice[1:4]	//[1 2 3]
指针

即内存地址

每个变量在运行时都拥有一个地址,这个地址代表变量在内存中的位置。

Go语言中使用在变量名前面添加&操作符(前缀)来获取变量的内存地址(取地址操作),格式如下:

//获取a的指针(内存地址)
var a int = 10 
ptr := &v  
//直接声明指针
var ptr *var-type
var ip *int

在指针类型前面加上 * 号(前缀)来获取指针指向的地址中的内容

package main

import "fmt"

func main() {
   var a int= 20   /* 声明实际变量 */
   var ip *int        /* 声明指针变量 */

   ip = &a  /* 指针变量的存储地址 */

   fmt.Printf("a 变量的地址是: %x\n", &a  )

   /* 指针变量的存储地址 */
   fmt.Printf("ip 变量储存的指针地址: %x\n", ip )

   /* 使用指针访问值 */
   fmt.Printf("*ip 变量的值: %d\n", *ip )
}

当一个指针被定义后没有分配到任何变量时,它的值为 nil。

nil 指针也称为空指针。

字典(map)
//使用 make 函数声明map
map_name := make(map[KeyType]ValueType, capacity)

// 创建一个空的 Map
m := make(map[string]int)

// 创建一个初始容量为 10 的 Map
m := make(map[string]int, 10)

// 直接初始化创建Map
m := map[string]int{
    "apple": 1,
    "banana": 2,
    "orange": 3,
}

// 获取键值对
v1 := m["apple"]
v2, ok := m["pear"]  // 如果键不存在,ok 的值为 false,v2 的值为该类型的零值

// 获取 Map 的长度
len := len(m)

// 删键值对
delete(m, "banana")
函数
//函数的声明
func function_name(参数列表) [返回参数列表] {
   函数体
}
//声明一个函数
func test(id int, name string) (string, string) {
	//处理逻辑
	return "a", "b"
}
  • 匿名函数
func(参数列表) [返回参数列表]{
    函数体
}

//匿名函数可以在声明后调用:
func(data int){
    fmt.Println("Hello", data)
}(100)

//匿名函数体可以被赋值
// 将匿名函数体保存到 f() 中
f := func(data int) {
    fmt.Println("Hello", data)
}
 
// 使用 f() 调用
f(100)

  • 匿名函数作为回调函数
package main
 
import "fmt"
 
// 遍历切片的每个元素,通过给定函数进行元素访问
func visit(list []int, f func(int)) {
 
    for _, v := range list {
        f(v)
    }
}
 
func main() {
 
    //使用匿名函数打印切片内容
    visit([]int{1, 2, 3, 4}, func(v int) {
        fmt.Println(v)
    })
}
  • 递归函数
func recursion() {
   recursion() /* 函数调用自身 */
}
结构体

结构体是由任意个任意类型的变量组合在一起的数据类型,和其他语言中类的概念相似。

type Person struct {
    Name string
    age int
}

go不是面向对象语言,面向对象是一种思想,go语言可以通过结构体来实现面向对象

结构体匿名字段

package main

import "fmt"

type Person struct {
	Name string
	age  int
}

type Student struct {
	id int
    Person 	//匿名
    int 	//匿名
}

func main() {
	p := Person{"enzo", 10}

	student1 := Student{p, 111}
	fmt.Println(student1, student1.Name, student1.age)
	
	student2 := Student{Person{"hx", 10}, 111}
	fmt.Println(student2)
}
函数方法

即相应结构体可调用的方法

func (variable_name variable_data_type) function_name() [return_type]{
   /* 函数体*/
}

实例:

package main

import "fmt"

type Person struct {
	Name string
	age  int
}

func (p Person) say() string {
	return "my name is " + p.Name + " " + strconv.Itoa(p.age) + " years old"
}

func main() {
	p := Person{"enzo", 10}
	fmt.Println(p.say())
}
接口

interface类型定义了一组方法,如果某个对象实现了某个接口的所有方法,则此对象就实现了此接口。

Go 语言的接口设计是非侵入式的,接口编写者无须知道接口被哪些类型实现。而接口实现者只需知道实现的是什么样子的接口,但无须指明实现哪一个接口。编译器知道最终编译时使用哪个类型实现哪个接口,或者接口应该由谁来实现。

非侵入式设计是 Go 语言设计师经过多年的大项目经验总结出来的设计之道。只有让接口和实现者真正解耦,编译速度才能真正提高,项目之间的耦合度也会降低不少。

type 接口名 interface{
    方法名1( 参数列表1 ) 返回值列表1
    方法名2( 参数列表2 ) 返回值列表2}

// 定义interface
type Human interface {
	Say()
	Eat(food string)
}

接口实现

package main

import (
	"fmt"
	"strconv"
)

type Person struct {
	Name string //首字母大写,对外可见
	age  int    //首字母小写,对外不可见
}

type Student struct {
	Person
	id int
}

func (p Person) Say() {
	fmt.Println("my name is " + p.Name + " " + strconv.Itoa(p.age) + " years old")
}

func (p Person) Eat() {
	fmt.Println("i like eat")
}

func (s Student) Learn() {
	fmt.Println("i like Learning")
}

type Human interface {
	Say()
	Eat()
}

type Learner interface {
	Learn()
}

func main() {
	p := Person{"enzo", 10}
	p.Say()
	p.Eat()

	student := Student{Person{"hx", 20}, 111}
	student.Say()
	student.Eat()
	student.Learn()

	var h Human = p
	h.Say()

	var l Learner = student
	l.Learn()
}
  • 空interface

空接口是接口类型的特殊形式,空接口没有任何方法,因此任何类型都无须实现空接口。从实现的角度看,任何值都满足这个接口的需求。因此空接口类型可以保存任何值,也可以从空接口中取出原值。

// 定义a为空接口
var a interface{}
fmt.Println(a) 		//<nil>
var num int = 5
str := "Hello world"
// a可以存储任意类型的数值
a = num
fmt.Println(a)		//5
a = str
fmt.Println(a)		//Hello world
  • 类型断言

value.(type) 即判断value是否是指定的type类型

可以对接口中的值进行类型断言,来获取接口中的值和判断值类型

package main

func main() {
	var i interface{} = "Hello, World"
	//var i interface{} = 1
	str, ok := i.(string) //返回两个值,ok为true或者false,如果false,值为空
	if ok {
		println(str)
	} else {
		println("i 不是string类型")
	}
}
通道(channel)

通道是用来传递数据的一个数据结构,可用于两个 goroutine 之间通过传递一个指定类型的值来同步运行和通讯。

必须使用make 创建channel:

ch := make(chan int)
ch := make(chan string)
ch := make(chan interface{})

操作符 <- 用于指定通道的方向,发送或接收。如果未指定方向,则为双向通道。

ch <- v    // 把 v 发送到通道 ch
v := <-ch  // 从 ch 接收数据
           // 并把值赋给 v

通道有双通道和单通道之分,如果未指定方向,则为双向通道。

package main

import (
	"fmt"
	"time"
)

// 单通道,将数据发送到goroutine
func produce(p chan<- int) {
	for i := 0; i < 10; i++ {
		p <- i
		//i := <-p  //错误示例,只能发送数据到通道中,不能从通道中获取数据
	}
}

// 单通道,接收goroutine中的数据
func consumer(c <-chan int) {
	for i := 0; i < 10; i++ {
		v := <-c
		fmt.Println("receive:", v)
		//c <- i	//错误示例,只能从通道中接收数据,不能发送数据到通道中
	}
}

// 双通道,可发送和接收数据
func user(p chan int) {

	//将数据发送到goroutine
	for i := 0; i < 10; i++ {
		p <- i
	}

	//接收goroutine中的数据
	for i := 0; i < 10; i++ {
		a := <-p
		fmt.Println(a)
	}
}

func main() {
	ch := make(chan int, 10)
	go produce(ch)
	go consumer(ch)
	time.Sleep(100 * time.Millisecond)

	go user(ch)
	time.Sleep(100 * time.Millisecond)
}

并发(goroutine)

goroutine是Go并发设计的核心。goroutine说到底其实就是协程,是比线程更小的一种执行单元,执行goroutine只需极少的栈内存(大概是4~5KB),当然会根据相应的数据伸缩(几十到几百K)。也正因为如此,可同时运行成千上万个并发任务。goroutine比thread更易用、更高效、更轻便。

goroutine是通过Go的runtime管理的一个线程管理器。goroutine通过go关键字实现了,其实就是一个普通的函数。

go say()

通过关键字go就启动了一个goroutine。我们来看一个例子

package main

import (
	"fmt"
	"runtime"
)

func say(s string) {
	for i := 0; i < 5; i++ {
		runtime.Gosched()
		fmt.Println(s)
	}
}

func main() {
	go say("world") //开一个新的goroutine执行
	say("hello") //当前Goroutines执行
}

// 以上程序执行后将输出:
// hello
// world
// hello
// world
// hello
// world
// hello
// world
// hello

流程控制

if

满足条件就做某事,否则做另一件事。

Go里面if条件判断语句中不需要括号,如下代码所示

if x > 10 {
	fmt.Println("x is greater than 10")
} else {
	fmt.Println("x is less than 10")
}

Go的if还有一个强大的地方就是条件判断语句里面允许声明一个变量,这个变量的作用域只能在该条件逻辑块内,其他地方就不起作用了,如下所示

// 计算获取值x,然后根据x返回的大小,判断是否大于10。
if x := computedValue(); x > 10 {
	fmt.Println("x is greater than 10")
} else {
	fmt.Println("x is less than 10")
}

//这个地方如果这样调用就编译出错了,因为x是条件里面的变量
fmt.Println(x)

多个条件的时候如下所示:

if integer == 3 {
	fmt.Println("The integer is equal to 3")
} else if integer < 3 {
	fmt.Println("The integer is less than 3")
} else {
	fmt.Println("The integer is greater than 3")
}
for

Go里面最强大的一个控制逻辑就是for,它既可以用来循环读取数据,又可以当作while来控制逻辑,还能迭代操作。它的语法如下:

for init; condition; post {
	//...
}
  • init: 一般为赋值表达式,给控制变量赋初值;
  • condition: 关系表达式或逻辑表达式,循环控制条件;
  • post: 一般为赋值表达式,给控制变量增量或减量。

例子:

package main

import "fmt"

func main(){
	sum := 0;
	for index:=0; index < 10 ; index++ {
		sum += index
	}
	fmt.Println("sum is equal to ", sum)
}
// 输出:sum is equal to 45

有些时候需要进行多个赋值操作,由于Go里面没有,操作符,那么可以使用平行赋值i, j = i+1, j-1

有些时候如果我们忽略initpost

sum := 1
for ; sum < 1000;  {
	sum += sum
}

其中;也可以省略,那么就变成如下的代码了,类似while

sum := 1
for sum < 1000 {
	sum += sum
}

在循环里面有两个关键操作breakcontinue ,break操作是跳出当前循环,continue是跳过本次循环。当嵌套过深的时候,break可以配合标签使用,即跳转至标签所指定的位置,详细参考如下例子:

for index := 10; index>0; index-- {
	if index == 5{
		break // 或者continue
	}
	fmt.Println(index)
}
// break打印出来10、9、8、7、6
// continue打印出来10、9、8、7、6、4、3、2、1

breakcontinue还可以跟着标号,用来跳到多重循环中的外层循环

for配合range可以用于读取slicemap的数据:

for k,v:=range map {
	fmt.Println("map's key:",k)
	fmt.Println("map's val:",v)
}

由于 Go 支持 “多值返回”, 而对于“声明而未被调用”的变量, 编译器会报错, 在这种情况下, 可以使用_来丢弃不需要的返回值 例如

for _, v := range map{
	fmt.Println("map's val:", v)
}

label

break label,跳出label代码块

label:
	for i := 0; i < 10; i++ {
		fmt.Println(i)
		if i == 5 {
			break label
		}
	}
//打印0~5

continue label,跳出当前循环,继续label代码块

label:
	for i := 0; i < 10; i++ {
		fmt.Println(i)
		if i == 5 {
			continue label
		}
	}
//打印0~9
switch

有些时候你需要写很多的if-else来实现一些逻辑处理,这个时候代码看上去就很丑很冗长,而且也不易于以后的维护,这个时候switch就能很好的解决这个问题。它的语法如下

switch sExpr {
case expr1:
	some instructions
case expr2:
	some other instructions
case expr3:
	some other instructions
default:
	other code
}

sExprexpr1expr2expr3的类型必须一致。Go的switch非常灵活,表达式不必是常量或整数,执行的过程从上至下,直到找到匹配项;而如果switch没有表达式,它会匹配true

i := 10
switch i {
case 1:
	fmt.Println("i is equal to 1")
case 2, 3, 4:
	fmt.Println("i is equal to 2, 3 or 4")
case 10:
	fmt.Println("i is equal to 10")
default:
	fmt.Println("All I know is that i is an integer")
}

在第5行中,我们把很多值聚合在了一个case里面,同时,Go里面switch默认相当于每个case最后带有break,匹配成功后不会自动向下执行其他case,而是跳出整个switch, 但是可以使用fallthrough强制执行后面的case代码。

integer := 6
switch integer {
case 4:
	fmt.Println("The integer was <= 4")
	fallthrough
case 5:
	fmt.Println("The integer was <= 5")
	fallthrough
case 6:
	fmt.Println("The integer was <= 6")
	fallthrough
case 7:
	fmt.Println("The integer was <= 7")
	fallthrough
case 8:
	fmt.Println("The integer was <= 8")
	fallthrough
default:
	fmt.Println("default case")
}

上面的程序将输出

The integer was <= 6
The integer was <= 7
The integer was <= 8
default case
select

select 语句类似于 switch语句,但是select会随机执行一个可运行的case。如果没有case可运行,它将阻塞,直到有case可运行。

select 语句只能用于通道操作,每个 case 必须是一个通道操作,要么是发送要么是接收。

select 语句会监听所有指定的通道上的操作,一旦其中一个通道准备好就会执行相应的代码块。

如果多个通道都准备好,那么 select 语句会随机选择一个通道执行。如果所有通道都没有准备好,那么执行 default 块中的代码。

package main

import (
	"fmt"
)

func main() {
	c1 := make(chan string, 3)
	c2 := make(chan string, 3)
	
	for i := 0; i < 3; i++ {
		go func() {
			c1 <- "one"
		}()
		go func() {
			c2 <- "two"
		}()
	}
	
	for i := 0; i < 10; i++ {
		select {
		case message1 := <-c1:
			fmt.Println(message1)
		case message2 := <-c2:
			fmt.Println(message2)
		default:
			fmt.Println("no message")
		}
	}
}
//one
//one
//two
//one
//two
//two
//no message
//no message
//no message
//no message
goto

Go有goto语句——请明智地使用它。用goto跳转到必须在当前函数内定义的标签。例如假设这样一个循环:

func myFunc() {
	i := 0
Here:   //这行的第一个词,以冒号结束作为标签
	println(i)
	i++
	goto Here   //跳转到Here去
}

标签名是大小写敏感的。

字符串操作

下面这些函数来自于strings包,这里介绍一些我平常经常用到的函数,更详细的请参考官方的文档。

  • func Contains(s, substr string) bool

    字符串s中是否包含substr,返回bool值

fmt.Println(strings.Contains("seafood", "foo"))
fmt.Println(strings.Contains("seafood", "bar"))
fmt.Println(strings.Contains("seafood", ""))
fmt.Println(strings.Contains("", ""))
//Output:
//true
//false
//true
//true
  • func Join(a []string, sep string) string

    字符串链接,把slice a通过sep链接起来

s := []string{"foo", "bar", "baz"}
fmt.Println(strings.Join(s, ", "))
//Output:foo, bar, baz		
  • func Index(s, sep string) int

    在字符串s中查找sep所在的位置,返回位置值,找不到返回-1

fmt.Println(strings.Index("chicken", "ken"))
fmt.Println(strings.Index("chicken", "dmr"))
//Output:4
//-1
  • func Repeat(s string, count int) string

    重复s字符串count次,最后返回重复的字符串

fmt.Println("ba" + strings.Repeat("na", 2))
//Output:banana
  • func Replace(s, old, new string, n int) string

    在s字符串中,把old字符串替换为new字符串,n表示替换的次数,小于0表示全部替换

fmt.Println(strings.Replace("oink oink oink", "k", "ky", 2))
fmt.Println(strings.Replace("oink oink oink", "oink", "moo", -1))
//Output:oinky oinky oink
//moo moo moo
  • func Split(s, sep string) []string

    把s字符串按照sep分割,返回slice

fmt.Printf("%q\n", strings.Split("a,b,c", ","))
fmt.Printf("%q\n", strings.Split("a man a plan a canal panama", "a "))
fmt.Printf("%q\n", strings.Split(" xyz ", ""))
fmt.Printf("%q\n", strings.Split("", "Bernardo O'Higgins"))
//Output:["a" "b" "c"]
//["" "man " "plan " "canal panama"]
//[" " "x" "y" "z" " "]
//[""]
  • func Trim(s string, cutset string) string

    在s字符串的头部和尾部去除cutset指定的字符串

fmt.Printf("[%q]", strings.Trim(" !!! Achtung !!! ", "! "))
//Output:["Achtung"]
  • func Fields(s string) []string

    去除s字符串的空格符,并且按照空格分割返回slice

fmt.Printf("Fields are: %q", strings.Fields("  foo bar  baz   "))
//Output:Fields are: ["foo" "bar" "baz"]

字符串转换

字符串转化的函数在strconv中,如下也只是列出一些常用的:

  • Append 系列函数将整数等转换为字符串后,添加到现有的字节数组中。
package main

import (
	"fmt"
	"strconv"
)

func main() {
	str := make([]byte, 0, 100)
	str = strconv.AppendInt(str, 4567, 10)
	str = strconv.AppendBool(str, false)
	str = strconv.AppendQuote(str, "abcdefg")
	str = strconv.AppendQuoteRune(str, '单')
	fmt.Println(string(str))
}
  • Format 系列函数把其他类型的转换为字符串
package main

import (
	"fmt"
	"strconv"
)

func main() {
	a := strconv.FormatBool(false)
	b := strconv.FormatFloat(123.23, 'g', 12, 64)
	c := strconv.FormatInt(1234, 10)
	d := strconv.FormatUint(12345, 10)
	e := strconv.Itoa(1023)
	fmt.Println(a, b, c, d, e)
}
  • Parse 系列函数把字符串转换为其他类型
package main

import (
	"fmt"
	"strconv"
)
func checkError(e error){
	if e != nil{
		fmt.Println(e)
	}
}
func main() {
	a, err := strconv.ParseBool("false")
	checkError(err)
	b, err := strconv.ParseFloat("123.23", 64)
	checkError(err)
	c, err := strconv.ParseInt("1234", 10, 64)
	checkError(err)
	d, err := strconv.ParseUint("12345", 10, 64)
	checkError(err)
	e, err := strconv.Atoi("1023")
	checkError(err)
	fmt.Println(a, b, c, d, e)
}

文件操作

目录操作

文件操作的大多数函数都是在os包里面,下面列举了几个目录操作的:

func Mkdir(name string, perm FileMode) error

创建名称为name的目录,权限设置是perm,例如0777

func MkdirAll(path string, perm FileMode) error

根据path创建多级子目录,例如enzo/test1/test2。

func Remove(name string) error

删除名称为name的目录,当目录下有文件或者其他目录时会出错

func RemoveAll(path string) error

根据path删除多级子目录,如果path是单个名称,那么该目录下的子目录全部删除。

package main

import "os"

func main() {
	//os.Mkdir("C:/Users/Enzo/Desktop/enzo", 0777)
	os.MkdirAll("C:/Users/Enzo/Desktop/enzo/test1/test2", 0777)
	//err := os.Remove("C:/Users/Enzo/Desktop/enzo")
	//if err != nil {
	//	fmt.Println(err)
	//}
	//os.RemoveAll("C:/Users/Enzo/Desktop/enzo")
}

建文件

新建文件可以通过如下两个方法

func Create(name string) (file *File, err Error)

根据提供的文件名创建新的文件,返回一个文件对象,默认权限是0666的文件,返回的文件对象是可读写的。

func NewFile(fd uintptr, name string) *File

根据文件描述符创建相应的文件,返回一个文件对象

打开文件

func Open(name string) (file *File, err Error)

该方法打开一个名称为name的文件,但是是只读方式,内部实现其实调用了OpenFile。

func OpenFile(name string, flag int, perm uint32) (file *File, err Error)

打开名称为name的文件,flag是打开的方式,只读、读写等,perm是权限

写文件

写文件函数:

func (file *File) Write(b []byte) (n int, err Error)

写入byte类型的信息到文件

func (file *File) WriteAt(b []byte, off int64) (n int, err Error)

在指定位置开始写入byte类型的信息

func (file *File) WriteString(s string) (ret int, err Error)

写入string信息到文件

package main

import (
	"fmt"
	"os"
)

func main() {
	os.Mkdir("C:/Users/Enzo/Desktop/enzo", 0777)
	userFile := "C:/Users/Enzo/Desktop/enzo/enzo.txt"
	fout, err := os.Create(userFile)
	if err != nil {
		fmt.Println(userFile, err)
		return
	}
	defer fout.Close()
	for i := 0; i < 10; i++ {
		fout.WriteString("Just a test!\r\n")
		fout.Write([]byte("Just a test!\r\n"))
	}
}

读文件

读文件函数:

func (file *File) Read(b []byte) (n int, err Error)

读取数据到b中

func (file *File) ReadAt(b []byte, off int64) (n int, err Error)

从off开始读取数据到b中

package main

import (
	"fmt"
	"os"
)

func main() {
	userFile := "asatxie.txt"
	fl, err := os.Open(userFile)		
	if err != nil {
		fmt.Println(userFile, err)
		return
	}
	defer fl.Close()
	buf := make([]byte, 1024)
	for {
		n, _ := fl.Read(buf)
		if 0 == n {
			break
		}
		os.Stdout.Write(buf[:n])
	}
}

删除文件

Go语言里面删除文件和删除文件夹是同一个函数

func Remove(name string) Error

调用该函数就可以删除文件名为name的文件

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值