Go语言学习笔记 汇总(一)

一.常量
定义形式
const a=100
const str="HELLO"

const(
a=100
b=99
c=10
)
常量一经定义,其值便无法被更改

二.iota
1.iota是常量自动生成器,每行写一个,自动累加1
2.iota可以给常量赋值
3.iota的赋值形式
01.const a=iota

02.const(
a=iota
b
c
)
此时 a=0,b=1,c=2
03.const (
a, b = iota, iota
c= iota
)
此时a,b都是0
所以结论是定义在同一行的iota的值是一样的
4.go语言使用iota作为枚举变量使用

三.complex128类型
此类型是go语言的复数类型
var t complex128
t=2.1+3.14i
可以通过内建函数 real( t )imag( t ) 来取得复数的实部和虚部
*重点函数
real()
imag()

四.输入
同c语言一样,go语言格式化输入是 fmt.Scanf("%d",&a)
同时,这是阻塞等待用户的输入
*重点函数
fmt.Scanf()
fmt.Scan()//此函数简化了输入格式,它也需要取地址比如Scan(&a)

五.强制类型转换
重点 bool<-->int 不能相互转换
字符类型本质就是整型等价于ascii码
格式
var a int=100
var b float =23.0
int(b)
float(a)

类型别名 type myint int 此操作为int类型创建了一个别名叫 myint

六.if的使用
格式 if a:=1;a==1{ }
if a==1{ }
它可以指定一个赋值语句用 “;”与后边的条件隔开
它判断的是布尔条件
它支持&& ,||,!操作,同C一样
if_elseif_else的使用
if ... {
} else if...{

}
多个if_elseif_if嵌套使用单个if…{}也可以实现,不过,会增加判断的次数

七.switch 语法和C类似
switch 条件 {
case 1:
...
fallthrough
case 2:
...
default:
....
}

fallthrough 语句被加上后会执行下边一个的 case
不写 break执行完case后自动break
Switch 可以和 if 一样 有初始值用分号和后边条件隔开 比如 switch a:=1 ;a {}
Switch 还可以没有条件 如同 switch{}
Switch 中 case 可以写判断条件 如 case a>100: 也可以写多个条件 case 3,4,5:

八range&…
range返回一对值,range常用作数组的迭代
"…"不定参数只能用在函数参数列表的最后
func sum(val...int)(sum int){
sum:=0
for _,v:=range val{
sum+=v
}
return
}
此函数作用是接收不定个数的整数然后求和
调用格式如下 sum(1,2,3)
返回结果为6

九.函数
函数可以有多个返回值
func AM(a,b int ) (r1,r2 int) {
r1=a+b
r2=a-b
return
}
go语言函数用法非常灵活
func a( ) {}
func b( ) (int){}
func c( ) (s1 int,s2 float){}
func d(e int,f float){}
只有一个返回值时返回值列表可以没有圆括号
func e( ) int{}
重点:Go 语言函数形参不支持默认值 如 a int=100

十.函数类型
函数也是一种数据类型,通过type 可以给函数起别名,为函数起别名是为了实现多态
type myfuncType func(int,int) int //声明时 func后没有函数名,没有{}花括号
加入有以下两个函数

func Add( a , b int ) int { return a+b }
func Minus(a,b int ) int { return a-b }

那么调用Add()和Minus()两个函数大家一定都会
我们换种形式
var f1 myfuncType=Add
//我们用myfuncType定义了一个变量,同时把Add()函数的地址赋给f1(相当于C的函数引用/函数指针)
//同理我们定义变量f2 引用 Minus()函数
var f2 myfuncType=Minus
此时调用函数就可以写成
f1(1,2)//等价于Add(1,2)
f2(1,2)//等价于Minus(1,2)
这种类型的操作是为了统一类型,便于操作,实现多态

十一.回调函数
func Add( a , b int ) int { return a+b }
func Minus(a,b int ) int { return a-b }

func calc(a,b int , f myfunctype ) int{
return f(a,b)
}

当我们在外部调用 calc( 1,2,Add)时,得到的是1,2的和
当我们在外部调用 calc( 1,2,Minus)时,得到的是1,2的差
此时calc叫回调函数
回调函数:函数有一个参数是函数类型的函数
作用:实现多态,(多种形态),就是调用同一个接口,有不同的表现,或者是先有一个想法,能力不够,留作后面去实现

十二.匿名函数与闭包
//闭包:一个函数捕获了和它在同一作用域的其他常量和变量。
这就意味着当闭包被调用的时候,不管在程序什么地方调用,闭包能够使用这些常量或者变量。
关于闭包概念的讲解:
引用:https://studygolang.com/articles/11239?fr=sidebar
引用:https://studygolang.com/articles/15398

重点:闭包不关心这些捕获了的变量和常量是否已经超出了作用域,所以只有闭包还在使用它,这些变量还会存在。
package main
import "fmt"
func main(){

var T string="函数内的字符串"
f1:=func(){ //f1是自动推导类型
fmt.Println("我是匿名函数1")
}
//匿名函数,没有函数名字,定义和调用

f1()//间接的调用了匿名函数

func(){
  fmt.Println("我是匿名函数2")
}()

//如果想调用匿名函数,还可以在函数结尾处加上()来调用匿名函数
func(a,b int){
fmt.Println("我是匿名函数3")
fmt.Printf("%d\n",a+b)
}(1,2)
//带参的匿名函数调用,把参数放在()内和普通的函数调用一样

func(){
T="闭包"
}()
fmt.Println(T)
//此时我们发现,变量T的值已经被匿名函数所修改
//所以闭包已引用的方式捕获外部变量
}
package main
import “fmt”
func test() int {
var x int
x++
return x * x
}

func main() {
f := test()

fmt.Println(f())
fmt.Println(f())
fmt.Println(f())
fmt.Println(f())
fmt.Println(f())

}
这个例子的结果是:1 1 1 1 1
因为每次调用时 text()函数中变量x都是零,自增1再平方结果仍然是1

package main
import “fmt”

func test() func() int {
var x int
return func() int {
x++
return x * x
}
}

func main() {
f := test()

fmt.Println(f())
fmt.Println(f())
fmt.Println(f())
fmt.Println(f())
fmt.Println(f())

}
test()函数使用了闭包的思想
结果是: 1 4 9 16 25
1.函数的返回值是一个匿名函数,返回一个函数类型
2.返回值为一个匿名函数,返回一个函数类型,通过f来调用返回的匿名函数,f来调用闭包函数
3.所以只有闭包还在使用它,这些变量就真还会存在

十三.defer
关键字 defer 如同面向对象语言中的析构函数一样作用
在函数调用完毕一刹那间做一些清理工作
清理工作 文件 打开,关闭文件在函数结束前一刹那 网络客户端和我连接,有用户连接,需要关闭
defer 语句只能放在函数的内部
defer 作用延迟调用

func main() {
defer fmt.Println("bbbbb")
fmt.Println("aaaa")
} // 结果: aaaa bbbbb
func main() {
fmt.Println("aaaa")
defer fmt.Println("bbbbb")
}// 结果同上

多个defer同时出现在一个函数中,那么采用LIFO(后进先出/先进后出)的原则。如果函数某个延迟调用发生错误,这些调用依旧会被执行
格式
defer fmt.Println(“结束了”)
defer func(){}

func main(){
a,b:=10,20
defer func(a,b int){
fmt.Printlf(“内部:a=%d b=%d”,a.b)
} (a,b)//()圆括号用于函数调用
a=111
b=222
fmt.Printlf(“外部:a=%d b=%d”,a.b)
}
这段代码很有意思,在程序执行到defer func的时候当前的a,b已经被传入函数的参数列表,已经做好了记录
所以输出结果是 外部: 111 222
内部: 10 20

十四.go工程管理

  1. 分文件编程(多个文件),必须放在src目录
  2. 设置GOPATH环境变量
  3. 同一个目录,文件定义的包名必须一样
  4. go env命令用于查看go相关的环境路径
  5. 同一个目录,调用别的文件的函数,直接调用即可,无需包名引用
  6. 不同目录,包名不一样
  7. 调用不同包里的函数,格式:包名.函数名()
  8. 调用别的包的函数,这个包函数名字如果首字母是小写,无法让别人调用,要想别人能调用,必须首字母大写
  9. src文件夹放源代码
  10. bin文件夹放可执行程序
  11. pkg文件夹放和平台相关的库文件
  12. 如果有多个文件或多个包
    *1 配置GOPATH环境变量,配置src同级目录的绝对路径
    如:C:\Users\myGofile\src\go
    *2 自动生成bin或pkg目录,需要使用go instal命令
    除了要配置GOPATH环境变量,还要配置GOBIN环境变量

十五.指针
Go指针是一个代表着某一个内存地址的值,这个内存地址往往是在内存中存储的另一个变量的值的起始位置。Go语言对指针的支持介于java语言和C/C++语言之间,它既没有像java语言那样取消了代码对指针的直接操作能力,也避免了C/C++语言中由于对指针的滥用二造成的安全和可靠性问题。
Go语言虽然保留了指针,但与其他编程语言不同的是

  1. 默认值nil,没有NULL常量
  2. 操作符”&”取变量地址,”*”通过指针访问目标对象
  3. 不支持指针运算,不支持“–>”运算符,直接用”.”访问目标成员
  4. **p 存放 *p的地址
    格式: var 变量名 数据类型
    特别的,var p *int=1//是错误的,指针不可以操作非法内存
    var a int
    var p *int=&a
    *p=100//这类操作才是安全的
    var p *int P=nil
    *p=666 //是错误的写法,它没有合法指向
    5.new()函数分配一块能操作的内存 返回值时
    T
    6.Go 语言不需要释放 new()的空间

十六.数组
Go 语言数组是静态定义的,数组宽度:var a [常量]int
也就是说数组定义元素个数必须是常量
数组的初始化,声明定义同时赋值叫初始化 var a [5]int=[5]int{1,2,3,4,5}
d:=[5]int{2:10,4:20}的意思是把下标为 2 的赋值为 10.把数组下标为 4 的赋值为 20
二维数组也可以用 下标:一组值的形式初始化
C:=[2][2]int{ 1:{1,2} }
数组可以赋值 可以用 “==”,“!=” 运算符来比较
数组作为函数参数时是值传递,也就是说,形参是实参的复制品
随机数函数在包”math/rand”
使用随机数函数要设置种子,只需设置一次
如果种子参数一样,每次运行程序产生的随机数也都一样
形式 rand.Seed(time.Now().UnixNano())//以系统时间作为随机数种子参数
rand.Int()产生一个很大区间的伪随机数
rand.Initn(100)//产生一个100以内的伪随机数
数组缺点:长度固定,作为形参时开销很大 整个数组都复制给形参

十七.slice(切片)
切片本质上不是数组,是一种数据结构,引用原来的数组的一个长度
切片不是数组或数组指针,它是通过内部指针和相关类型引用数组片段,以实现变长方案。
Slice并不是真正意义上的动态数组,而是一个应用那个类型,slice总是指向一个底层array,slice的声明也可以像array一样,只是不需要长度
格式 var s[ ] int 或 var s […]string //切片不指定长度,用…代替
array:=[…]int{10,20,30,40,0,0}
slice:=array[0:3:5]
array[low:high:max]
low:下标的起点
high:下标的终点(不包括此下标),[a[low],a[high] )左闭右开
cap = max-low cap是容量
如果容量不够,那么slice 会自动以二倍长度扩容
函数len(slice)是检测slice的长度
函数cap(slice) 是检测slice的容量
函数append(slice,11)是在切片slice中追加一个新元素,值是11
函数copy(s1,s2)将s2中的元素拷贝到s1中,在s1中的位置是原s2的位置
S1:=[]int{1,2}
S2:=[]int{6,6,6,6,6,6}
S3:=s2[]
则s3中的元素是{1,2,6,6,6,6}
在原切片末尾追加元素 s2=append(s2,20)
函数make可以生成切片,格式:make(切片类型,长度,容量)
s:=make([]int,5,10)
定义切片还可以用自动推倒类型
改变切片的值也就是改变原数组的值切片和底层数组的关系
Slice作为函数参数时是引用传递
Slice的一系列截取操作

S[a] 切片s中索引位置为n的项
S[:] 从切片s的索引位置0到len(s)-1处所获得的切片
S[low:] 从切片s的索引位置low到len(s)-1处所获得的切片
S[:high] 从切片s的索引位置0到high处所获得的切片,len=high
S[low:high] 从切片s的索引位置low到high处所获得的切片 len=high-low
S[low:high:max] 从切片s的索引位置low到high处所获得的切片,len=high-low,cap=max-low
len(s) 切片s的长度,总是<=cap(s)
cap(s) 切片s的容量

十八.map
Go语言中的map(映射,字典)是一种内置的数据结构,它是一个无序的key–value对的集合,比如以身份证号作为唯一键来标识一个人的信息
格式 map[keyType]valueType
在一个map里所有的键都是唯一的,而且必须支持==!=操作符的类型,切片,函数以及包含切片的结构类型这些类型由于具有引用语义,不能作为映射的键。
map值可以是任意类型,没有限制。
map里所有键的数据类型必须是相同的,值也必须如此,但键和值的类型可以不同。
注意:map是无序的(基于hash算法),我们无法决定它的返回顺序,所以,每次打印结果的顺序是有可能不同的。
创建:可以通过make()函数来创建map
M0=make(map[int]string,100)//此时指定了键值对的容量,如果容量不够了,则会自动扩容
M0[10000]=”扩容了”
M1:=make(map[int]string)
此时可以添加元素
M1[1]=”go”
M1[2]=”lang”
M2:=map[int]string{1:”hello”,2:”你好”}
Go语言map键值是唯一的
M3:=map[int]string{1:”hello”,2:”你好”,1:”hello1”}//这种写法是错误的
不过我们可以在外部更新
M3[1]=”hello1”//这么写没问题
一个完整的map例子:
package main
import “fmt”
func main()
{
M:=map[int]string{1:”mike”,2:”yoyo”,3:”go”}
//第一个返回值是key,第二个返回值是value,遍历结果是无序的
for key,value:=rang m{
fmt.Printf(“%d======>%s”,key,value)
}
//如何判断一个key值是否存在
//第一个返回值为key所对应的value,第二个返回值为key是否存在的条件,存在ok为true
value , ok :=m[0]
if ok==true {
fmt.Println(“m[1]= “,value)
} else {
fmt.Println(“key不存在”)
}

}

函数delete(m,1)可以从键值对m中删去key为1的项
Map作为函数参数时是引用传递

十九.结构体
有时候我们需要将不同的数据组合成一个有机的整体,如:一个学生有学号,性别,姓名,年龄,住址等信息,显然单独定义以上变量比较繁琐,数据还不便于管理。
var id int
var name string
var sex byte
var age intv
var addr string

学生信息的一般表示法

 typedef Student struct { 
     id int
     set byte
     age int
     addr string
}

学生信息的结构体表示法

结构体里不使用 var 关键字
结构体是一种聚合数据类型,它是一系列只有相同类型或不同类型的数据构成的数据集合。每个数据称为结构体的成员。
结构体的赋值:形式如下
var st Student=Student{20100000,”mike”,’m’,10,”北京”}
var st Student=Student{id:20100000,name:”mike”,sex:’m’,age:10,addr:”北京”}


st.id=20100000
st.name=”mike”
……
所以,如果想要访问结构体内的成员,就要用”.”运算符,无论读写
结构体顺序初始化时每个成员都要写上 用成员名:值的形式初始化时,可以不写全所有成员,没被初始到的是他的默认值
结构体变量支持“==” “!=”两种比较方式
结构体变量作为函数参数时是值传递
同类型的两个结构体可以相互赋值

看一个关于内存的例子:

package main
import “fmt”
import “unsafe”
type x struct {
	bool
	int16
   float64
}
type y struct {
	float64
	int16
	bool
}
type z struct {
	int16
	float64
	bool
}
func main(){
a, b, c := x{false, 21, 32}, y{}, z{}
fmt.Printf("%d   %d    %d\n", unsafe.Sizeof(a), unsafe.Sizeof(b), unsafe.Sizeof(c))
}

函数Unsafe.size()的作用是返回变量在内存中的大小,单位是字节
我们运行以上程序,得到结果是 16 16 24(我使用的是64位的windows系统)
为什么不一样呢,
因为go语言底层存储会有对变量内存地址对齐的操作做,便于访问,所以会有内存空隙,这就导致了三个变量的空间不一样,在定义变量时,最好是相同类型的放在一起,这样能省一些内存空间

二十.可见性
Go语言对关键字的增加非常吝啬,其中没有package,protected,public这样的关键字。
要使用某个符号对包(package)可见(即可访问),需要该符号定义以大写字母开头。

二十一.面向对象编程
对于面向对象编程支持Go语言设计得非常简洁而优雅。因为,Go语言并没有沿袭传统面向对象编程中的诸多概念,比如继承(不支持继承,尽管匿名字段的内存布局和行为类似继承,但它并不是继承),虚函数,构造函数和析构函数,隐藏的this指针等。
尽管Go语言中没有封装,继承,多态这些概念,但同样可以通过其他方式来实现这些特性。
封装:通过方法实现
继承:通过匿名字段实现
多态:通过接口实现

1.匿名字段
一般情况下,定义结构体的时候是字段名与其类型一一对应,实际上Go支持提供类型而不写字段名的方式,也就是匿名字段,也称为嵌入字段。当匿名字段也是一个结构体的时候,那么这个结构体所拥有的全部字段都被隐式地引入当前的这个结构体

type Person struct{
sex byte
name string
}

type Student struct{
Person //只有类型,没有名字,继承了Person的全部成员
Age int
}
此时定义一个Student结构体变量:var s Student
那么,s中包含Person和Student中的所有成员
像这样赋值可以表示为:

var s Student=Student{Person{‘m‘,”mike”},18}
type Student struct{
  Person
  Age int
  Name string
}

如果后边这个结构体里有和Person结构体里同样的变量
那么默认以后边这个结构体优先
如果要想访问Person结构体中的name ,则用s.Person.name的方式
匿名字段可以是结构体,或其他类型
2.方法
在面向对象的编程中,一个对象其实也就是一个简单的值或者一个变量,在这个对象中会包含一些函数。这种带有接收者的函数,我们称之为方法(method)。本质上,一个方法则是一个和特殊类型关联的函数。
一个面向对象的程序会用方法来表达其属性和对应的操作,这样使用这个对象的用户就不需要直接去操作对象,而是借助方法来做这些事情。
在go语言中,可以给任意自定义类型(包括内置类型,但不包括指针类型)添加相应的方法。
格式: func (接收者 接收者类型) 方法名(参数列表)(返回值列表){}
其中:
接收者可以随意写,如果在方法中没有用到,可以省略参数名
接收者类型可以是*T或T 类型T不能是接口或指针
重点:不支持重载方法,也就是说,不能定义名字相同但是不同参数的方法
但是,接收者类型不一样,这个方法就算方法重载
普通函数不可以重载
方法可被继承,可被重写
使用格式如下:

type Person struct{
  name string
}

func (p *Person) set(str string){
  p.name=str
}
func (p  Person) set(str string){
  p.name=str
}

3.接口
接口被用来规定一个标准
定义格式:

type 接口名 interface{
    抽象函数名()
}
type Personer interface{
     Say()
}

接口里只能放方法名
如果某个类实现了某个接口里的全部方法,那么就说这个类继承了该接口
接口命名习惯以er结尾
接口只有方法声明,没有实现,没有数据字段
接口可以匿名嵌入到其他接口,或嵌入到结构体中
接口的实现:接口是哟过来定义行为的类型,这写被定义的行为不由接口直接实现,而是由用户定义的类型实现,一个实现了这些方法的具体类型是这个接口类型的实例。
如果用户定义的类型实现了某个接口类型声明的一组方法,那么这个用户定义的类型就可以赋给接口类型的值,这个赋值会把用户定义的类型的值存入接口类型的值。
interface{}之中类型叫做空接口类型,空接口类型是万能类型,它可以保存任意类型的值

接口例子如下:

package main
import "fmt"
type Personer interface{ say() }
type Person struct {
	name string
}

func (p Person) say() {
	fmt.Println("人说hello world")
}

func main() {
	var intf Personer
	var pers Person
	intf = pers
	intf.say()
}
  1. 多态
    下面是一个多态的例子
ackage main

import "fmt"

type SAY interface{ say() }
type Person struct {
	name string
}

func (p Person) say() {
	fmt.Println("人说hello world")
}

type Dog struct {
	name string
}

func (d Dog) say() {
	fmt.Println("狗说wang~wang~wang")
}

type Cat struct {
	name string
}

func (c Cat) say() {
	fmt.Println("猫说miao~miao~miao")
}
func whosay(who SAY) {
	who.say()
}

func main() {
	var intf SAY
	var pers Person
	var cat Cat
	var dog Dog
	intf = pers
	whosay(intf)
	intf = cat
	whosay(intf)
	intf = dog
	whosay(intf)
}

多态就是多种形态,一个方法传入不同类型的参数,实现不同的结果,就是一种多态

二十二. 反射
反射:可以在运行时动态获取变量的相关信息
所需的包: import “reflect”

反射中所用到的主要的函数

1.	reflect.TypeOf()
功能:获取变量的类型,返回reflect.Type类型
2.	reflect.ValueOf()
功能:获取变量的值,返回reflecct.Value类型
3.	reflect.Value.Kind()
功能:获取变量的类别,返回一个常量
4.	reflect.Value.Interface()
功能:转换成interface{}类型
5.	reflect.Value.NumField()
功能:获取结构体中字段的个数
6.	reflect.ValueOf().NumMethod()
功能:获取结构体中方法的个数
7.	reflect.Value.Method(n).Call()
功能:调用结构体中的方法

反射: 变量<—>Interface{}<—> Reflect.Value

反射可以对变量动态的分析,甚至可以调用变量的方法
Elem()如同*(解引用)的作用
例子:

package main
import "fmt"
import "reflect"
func main() {
	var a float64
	fv := reflect.ValueOf(&a)
	fv.Elem().SetFloat(100.1)
	fmt.Printf("%f\n", a)
}

其中 fv.Elem()用于获取指针指向的变量,相当于
var a *int
*a=100.1

二十三.异常处理
Go语言的异常处理极其清新……
Go语言引入一个关于错误处理的标准模式,即error接口,它是go语言内建的接口类型,该接口的定义如下:

type error interface {
     Error() string
}

写一个例子:

func myDiv(a,b int) (result int,err error){
  e rr=nil
if b==0 {
   err=errors.New(“分母不能为0。。。。。。。”)
} else {
   result=a/b
}
     return
}

生成错误信息有两种格式
1Err1 := fmt.Errorf(“%s”,”this is an error1”)
2Err2:=errors.New(“this is an error2”)
然后我们打印即可
fmt.Println(Err1,Err2)

函数panic(interface{})//其中参数可以是任意类型
用于打印系统级别的错误(比如数组下标越界),如果直接调用panic()函数,那么程序会崩溃

函数recover() 的作用是恢复
即不想让程序崩溃来恢复一下,直接执行 recover()会让程序略过错误信息,继续执行

二十四.字符串
字符串处理:

  contains()
  Join()
  Index()
  Repeat()
  Replace()
  Split()
  Trim()
  Fields()

字符串转换

   Append()
   Format()
   Parse()

对字符串操作需要导入“strconv"包
以上函数见名知意
go语言同样支持正则表达式

package main

import "fmt"
import "regexp" //正则所需的包
func main() {
	buf := "abc azs a8c 888 a9c tac"
	//1)解释规则,它会解析正则表达式,如果成功,返回解释器
	//reg1:=regexp.MustCompile(`a.c`)//··用于原生字符串
	//reg2:=regexp.MustCompile(`a[0-9]c`)
	reg1 := regexp.MustCompile(`a\dc`)
	if reg1 == nil {
		fmt.Println("regexp err")
		return
	}
	//2)根据规则提取关键信息
	result1 := reg1.FindAllStringSubmatch(buf, -1)
	fmt.Println("result1=", result1)

}

另附正则表:
在这里插入图片描述

二十五.文件
文件分类
1.设备文件:
屏幕(标准输出设备)fmt.Println() 往标准输出设备里写内容
键盘(标准输入设备)fmt.Scan() 从标准输入设备读取内容
2.磁盘文件:放在存储设备上的文件
1)文本文件 以记事本打开,能看到内容(不是乱码)的文件
2)二进制文件 以记事本打开,能看到内容( 是乱码)的文件
为什么需要文件
内存掉电丢失,程序结束,内存中的内容就消失了
文件放在磁盘,程序结束,文件还是存在,掉电不会丢失,便于管理

建立与打开文件
新建文件通过如下两个方法:

1.func Create (name String) (file *File,err Error)
根据提供的文件名创建新文件,返回一个文件对象,默认权限是0666的文件,返回的文件对象是可读写的.
2.func  NewFile(fd uintptr,name string) *File
根据文件描述符创建相应的文件,返回一个文件对象.
打开文件通过如下两个方法:
1.func Open(name string) (file *File,err Error)
该方法打开一个名称为name的文件,但是是只读方式,内部实现调用了OpenFile.
2.func OpenFile(name string , flag int,perm uint32)(file *File,err Error)
打开名称为name的文件,flag是打开的方式,只读,读写等,perm是权限.
重点:无论是Create() 还是Open()最终都调用了OpenFile()
方式:O_RDWR:读写 、O_CREATE:创建 、O_TRUNC:截断   对应0666

写文件

1.func (file *File) Write(b []byte) ( n int ,err Error)     //处理二进制文件、文本文件
写入byte类型的信息到文件,n代表写了多长
2.func (file *File)WriteAt(b []byte,off int64) (n int ,err Error)
从指定位置吸入byte类型的信息
3.func (file *File) WriteString(s string)(ret int ,err Error) //处理字符串,文本文件
写入string信息到文件

读文件

func (file *File)Read( b []byte)(n int ,err Error)
读取到切片b中,n代表独了多长
func (file *File)ReadAt(b []byte,off int64)(n int,err Error)
从off开始读取数据到b中

删除文件

func Remove(name string) Error
删除文件名为name的文件

设备文件的使用

package main
import "fmt"
import "os" //对文件操作所需的包

func main() {
	//os.Stdout.Close()//关闭后无法输出
	fmt.Println("R U OK?") //往标准输出设备(屏幕)写内容,自动换行
	//标准设备文件(os.Stdout),默认给用户已经打开,用户可以直接使用
	
	os.Stdout.WriteString("are you ok?\n")

	os.Stdin.Close() //关闭后无法输入
	var a int
	fmt.Println("轻松输入a:")
	fmt.Scan(&a) //从标准输入设备中读取内容,放在a中
	fmt.Println("a= ", a)
}

文件的读写

package main

import (
	"bufio"
	"fmt"
	"io"
	"os"
)

func WriteFile(path string) {
	//打开文件
	f, err := os.Create(path)
	if err != nil {
		fmt.Println("err= ", err)
	}
	//使用完毕,需要关闭文件
	defer f.Close()
	//写内容
	var buf string
	for i := 0; i < 10; i++ {
		//"i=1 \n",这个字符串存储在buf中
		buf = fmt.Sprintf("i=%d\n", i) //该函数功能是格式化字符串并赋值给接收的变量buf
		n, err := f.WriteString(buf)
		if err != nil {
			fmt.Println("err= ", err)
		}
		fmt.Println("n=  ", n)
	}
}

func ReadFile(path string) {
	//打开文件
	f, err := os.Open(path)
	if err != nil {
		fmt.Println("err= ", err)
		return
	}

	//关闭文件
	defer f.Close()
	buf := make([]byte, 1024*2) //2k大小
	// n代表从文件读取内容的长度
	n, err1 := f.Read(buf)
	if err1 != nil && err != io.EOF { //并且非文件尾

		fmt.Println("err1= ", err1)
		return
	}
	fmt.Println("buf= ", string(buf[:n]))
}

//每次读取一行,借助另外一个包 “bufio”,带缓存的io
//func NewReader(rd io.Reader) *Reader
//NewReader创建一个具有默认大小缓冲、从r读取的*Reader
//func (b *Reader) ReadBytes(delim byte)(line []byte,err error)//可以给它一个"\n"终止读取
//ReadBytes读取到第一次遇到delim字节,返回一个包含已读取的数据和delim字节的切片。如果ReadBytes方法在读取到delim之前遇到了错误,
//它返回在错误之前读取的数据以及该错误(一般是io.EOF)。当且仅当ReadBytes方法返回的切片不以delim结尾时,会返回一个非nil的错误

func ReadFileLine(path string) {
	//打开文件
	f, err := os.Open(path)
	if err != nil {
		fmt.Println("err= ", err)
		return
	}
	//关闭文件
	defer f.Close()
	//新建一个缓冲区,把内容先放在缓冲区里
	r := bufio.NewReader(f)
	//遇到'\n'结束读取,但是'\n'也读取进来了
	for {
		buf, err := r.ReadBytes('\n')
		if err != nil {
			if err == io.EOF { //文件已经读到结束
				break
			}
			fmt.Println("err = ", err)
		}
		fmt.Printf("buf=#%s#\n", string(buf)) //强转
	}
}

func main() {
	path := "./demo.txt" //当前路径的demo.txt

	WriteFile(path)
	fmt.Println()
	ReadFile(path)
	fmt.Println()
	ReadFileLine(path)
}

实现拷贝文件案例
注意,编译的二进制文件不要和系统的copy命令重名哦

package main
import (
	"fmt"
	"io"
	"os"
)

func main() {
	list := os.Args //获取命令行参数
	if len(list) != 3 {
		fmt.Println("命令行参数不够")
		return
	}
	srcFileName := list[1]
	dstFileName := list[2]

	if srcFileName == dstFileName {
		fmt.Println("原文件和目的文件相同")
		return
	}
	//只读方式打开源文件
	sF, err1 := os.Open(srcFileName)
	if err1 != nil {
		fmt.Println("err1= ", err1)
		return
	}

	//新建目的文件
	dF, err2 := os.Create(dstFileName)
	if err2 != nil {
		fmt.Println("err2= ", err2)
		return
	}

	//操作完毕,需要关闭文件
	defer sF.Close()
	defer dF.Close()

	//核心文件处理,从源文件,读取内容,往目的文件写,读多少,写多少
	buf := make([]byte, 4*1024) //4k大小临时缓冲区
	for {
		n, err := sF.Read(buf)
		if err != nil {
			if err == io.EOF { //文件读取完毕
				break
			}
			fmt.Println("err= ", err)
		}
		//往目的文件写,读多少,写多少
		dF.Write(buf[:n])

	}

}

(本学习笔记整理自网络资源,侵删)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值