Go IO之文件处理,TCPUDP讲解
文章目录
- 1 文件处理
- 1.1 打开和关闭文件
- 1.2 读取文件
- 1.2.1 简单示例
- 1.2.2 中文乱码
- 1.2.2.1 bufio
- 1.2.2.2 ioutil
- 1.3 写入文件
- 1.3.1 Write 和 WriteString
- 1.3.2 fmt.Fprintln
- 1.3.2.1 写入文件
- 1.3.2.2 写入标准输出
- 1.3.3 bufio.NewWriter
- 1.3.4 ioutil.WriteFile
- 2 TCP&UDP
- 2.1 TCP
- 2.1.1 服务端
- 2.1.2 客户端配置
- 2.2 UDP
- 2.2.1 服务端配置
- 2.2.2 客户端配置
1 文件处理
1.1 打开和关闭文件
os.open()
函数能够打开一个文件,返回一个 *File
和一个 err
,对得到的文件使用 close()
方法能够关闭文件。
package main
import (
"fmt"
"os"
)
// 打开关闭文件
func main() {
// 打开当前目录下的 abc.txt 文件
file,err := os.Open("./abc.txt")
if err != nil {
fmt.Println("文件打开失败",err)
} else {
fmt.Println("文件打开成功")
file.Close() // 从内存释放资源
fmt.Println("文件关闭成功")
}
}
/*
文件打开成功
文件关闭成功
*/
1.2 读取文件
接收一个字节切片
,返回读取的字节数
和可能的具体错误
,读到文件末尾时会返回 0
和 io.EOF
。
func (f *File) Read(b []byte) (n int, err error)
1.2.1 简单示例
package main
import (
"fmt"
"io"
"os"
)
// 打开关闭文件
func main() {
// 打开当前目录下的 abc.txt 文件
file,err := os.Open("./abc.txt")
if err != nil {
fmt.Println("文件打开失败",err)
return
}
// 文件能打开
defer file.Close() // 使用 defer 延迟开关,main 函数即将结束前释放文件资源
fmt.Println("文件成功打开")
// 读文件
var tmp [128]byte // 定义一个字节切片,每次读 128 字节
n,err := file.Read(tmp[:])
// 文件读完
if err == io.EOF {
fmt.Println("文件已读完")
return
}
// 读取中报错
if err != nil {
fmt.Println("Read from file failed,err",err)
return
}
fmt.Printf("读取 %d 个字节\n",n)
fmt.Println(string(tmp[:]))
// fmt.Printf("获取的内容是:%s\n",string(tmp[:]))
}
/*
读取 23 个字节
adadadsad
sdsadasdas
*/
循环读取文件
package main
import (
"fmt"
"io"
"os"
)
// 打开关闭文件
func main() {
// 打开当前目录下的 abc.txt 文件
file,err := os.Open("./abc.txt")
if err != nil {
fmt.Println("文件打开失败",err)
return
}
// 文件能打开
defer file.Close() // 使用 defer 延迟开关,main 函数即将结束前释放文件资源
fmt.Println("文件成功打开")
// 读文件
var tmp [128]byte // 定义一个字节切片,每次读 128 字节
// for 循环读取内容,输出到程序中
for {
_,err := file.Read(tmp[:])
// 文件读完
if err == io.EOF {
fmt.Println("文件已读完",err)
return
}
// 读取中报错
if err != nil {
fmt.Println("Read from file failed,err",err)
return
}
fmt.Printf("%s",string(tmp[:]))
}
}
1.2.2 中文乱码
1.2.2.1 bufio
如果有乱码,可以使用 bufio
读取。bufio
在 file
的基础上封装了一层 API
,支持更多的功能。
package main
import (
"fmt"
"os"
"io"
"bufio"
)
// bufio 读数据
func main() {
file,err := os.Open("./abc.txt")
if err != nil {
fmt.Println("文件打开失败",err)
return
}
defer file.Close()
// 封装一个 API 层
// 利用缓冲区从文件读数据
reader := bufio.NewReader(file)
for {
str,err := reader.ReadString('\n') // 字符
if err == io.EOF {
fmt.Print(str) // 要输出,否则不显示
return
}
if err != nil {
fmt.Println("读取文件内容失败",err)
return
}
fmt.Print(str) // 取消文件中的自带换行
}
}
示例:读取奇/偶数行
package main
import (
"bufio"
"fmt"
"io"
"os"
)
// bufio 读数据
func main() {
file, err := os.Open("./abc.txt")
if err != nil {
fmt.Println("文件打开失败", err)
return
}
defer file.Close()
// 利用缓冲区从文件读数据
reader := bufio.NewReader(file)
count := 0
for {
// 输出奇数行
str, _, err := reader.ReadLine()
count++
if err == io.EOF {
return
}
if err != nil {
fmt.Println("读取文件内容失败", err)
return
}
if count%2 == 1 {
// 0 为偶数
fmt.Println(string(str))
}
}
}
1.2.2.2 ioutil
package main
import (
"fmt"
"io/ioutil"
)
// ioutil 读取文件
func readFile(filename string) {
content,err := ioutil.ReadFile(filename)
if err != nil {
fmt.Println("READ FILE FAILED,ERR:",err)
return
}
fmt.Println(string(content))
}
func main() {
readFile("./abc.txt")
}
1.3 写入文件
os.OpenFile()
函数能够以指定模式打开文件,从而实现文件写入相关功能。
func OpenFile(name string, flag int, perm FileMode)(*File,error) {
...
}
说明:
- name:要打开的文件名
- flag:打开文件的模式
模式种类:os.O_WRONLY
:只写os.O_CREATE
:创建文件os.O_RDONLY
:只读vos.O_RDWR
:读写os.O_TRUNC
:清空os.O_APPEND
:追加
- perm:文件权限,一个八进制数。r(读)04,w(写)02,x(执行)01。
1.3.1 Write 和 WriteString
package main
import (
"fmt"
"os"
)
// 打开文件支持文件写入
func main() {
// 创建 tmp.txt 文件,分配权限 755
file,err := os.OpenFile("tmp.txt",os.O_CREATE|os.O_WRONLY,0755)
if err != nil {
fmt.Println("打开文件失败",err)
return
}
defer file.Close()
// 定义写入内容
str := "hello world"
// 字节方式写入
file.Write([]byte("this is test\n"))
// 字符串方式写入
defer file.WriteString(str)
}
/* tmp.txt
this is test
hello world
*/
1.3.2 fmt.Fprintln
1.3.2.1 写入文件
fmt.Fprintln(f, v)
是 把 v 的内容写入到 f
中。
解释:
- f 是一个
io.Writer
类型的对象,表示你要写入数据的目标。常见的目标包括文件、标准输出(os.Stdout)、缓冲区等。 - v 是希望写入到 f 中的数据,v 可以是任何可以格式化的类型,例如 string、int、struct 等。
fmt.Fprintln(f, v)
的作用是将 v 的值写入 f 中,并且在写入的值后面会自动添加换行符 \n
。
package main
import (
"fmt"
"os"
)
func main() {
// 创建或打开文件
f, err := os.Create("output.txt")
if err != nil {
fmt.Println("Error creating file:", err)
return
}
defer f.Close()
// 要写入文件的内容
v := "Hello, world!"
fmt.Fprintln(f, v) // 将 v 写入文件 f
}
1.3.2.2 写入标准输出
如果想把某个值写到控制台
fmt.Fprintln(os.Stdout, "Hello, Go!")
这个代码会将字符串 “Hello, Go!” 输出到标准输出(即控制台)
1.3.3 bufio.NewWriter
package main
import (
"fmt"
"os"
"bufio"
)
// 打开文件支持文件写入
func main() {
file,err := os.OpenFile("tmp.txt",os.O_CREATE|os.O_WRONLY|os.O_TRUNC,0666)
if err != nil {
fmt.Println("打开文件失败",err)
return
}
defer file.Close()
// 定义写入内容
write := bufio.NewWriter(file)
defer write.Flush() // 确保缓冲区数据被写入
for i:=0;i<10;i++ {
write.WriteString("hello world\n") // 将数据写入缓存
}
}
/* tmp.txt
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
hello world
*/
1.3.4 ioutil.WriteFile
package main
import (
"fmt"
"io/ioutil"
)
func main() {
str := "hello world\nthis is test"
// 字符串转换为字节数组写入
err := ioutil.WriteFile("./tmp.txt",[]byte(str),0666)
if err != nil {
fmt.Println("文件写入错误",err)
return
}
}
/* tmp.txt
hello world
this is test
*/
2 TCP&UDP
2.1 TCP
2.1.1 服务端
一个服务端连接多个客户端,例如:世界各地的用户使用自己电脑的浏览器访问淘宝网。
package main
import (
"fmt"
"net"
)
/* TCP 服务端 */
// 处理客户端连接函数
func process(conn net.Conn) {
defer conn.Close()
var buf [1024]byte
n,err := conn.Read(buf[:]) // 读取切片数据
if err != nil {
fmt.Println("read err:",err)
return
}
str := string(buf[:n]) // 转换字符串
fmt.Println("收到客户端发来的数据:",str)
}
func main() {
// 1. 监听端口
listener,err := net.Listen("tcp","127.0.0.1:20000")
// 连接失败处理
if err != nil {
fmt.Println("启动服务失败,err:",err)
return
}
// 程序退出时释放端口
defer listener.Close()
for {
// 2. 建立连接
// conn 是返回的接口
conn,err := listener.Accept()
if err != nil {
fmt.Println("接收客户端连接失败,err",err)
continue // 继续阻塞,等待客户端再次连接
}
// 3. 启动一个 goroutine 处理客户端连接
go process(conn)
}
}
2.1.2 客户端配置
package main
import (
"fmt"
"net"
)
/* TCP 客户端配置 */
func main() {
// 1. 拨号方式建立与服务端连接
conn,err := net.Dial("tcp","127.0.0.1:20000")
if err != nil {
fmt.Println("连接服务端失败,err:",err)
return
}
// 注意:关闭连接位置,不能写在连接失败判断上面
defer conn.Close()
// 2. 向 server 发送信息
// fmt.Fprintln(conn,"hello")
_,err = conn.Write([]byte("hello,tom"))
if err != nil {
fmt.Println("发送消息失败,err:",err)
return
}
}
2.2 UDP
UDP是用户数据报协议,是一种无连接传输协议,不需要建立连接就可以直接发送和接收数据,属于不可靠的,没有时序的通信,UDP 实时性好,适合直播环境。
2.2.1 服务端配置
package main
import (
"fmt"
"net"
)
/* UDP 服务端 */
func main() {
// 1. 监听
listener,err := net.ListenUDP("udp",&net.UDPAddr {
IP: net.ParseIP("127.0.0.1"),
Port: 30000,
})
if err != nil {
fmt.Println("启动 server 失败,err:",err)
return
}
// 退出时关闭资源
defer listener.Close()
// 循环收发数据
for {
var buf [1024]byte
// 因为是无连接,所以需要知道对方地址 Addr
n,addr,err := listener.ReadFromUDP(buf[:])
if err != nil {
fmt.Println("接收消息失败,err:",err)
return
}
fmt.Printf("接收到来自 %v 的消息:%v\n",addr,string(buf[:n]))
// 回复消息
n,err = listener.WriteToUDP([]byte("hi"),addr)
if err != nil {
fmt.Println("回复失败,err:",err)
return
}
}
}
2.2.2 客户端配置
package main
import (
"fmt"
"net"
)
/* UDP 客户端 */
func main() {
// 连接 UDP 服务器
conn,err := net.Dial("udp","127.0.0.1:30000")
if err != nil {
fmt.Println("连接失败,err:",err)
return
}
defer conn.Close()
// 发送消息
n,err := conn.Write([]byte("hello"))
if err != nil {
fmt.Println("发送失败,err:",err)
return
}
// 接收消息
var buf [1024]byte
n,err = conn.Read(buf[:]) // n 为返回的有效字节个数
if err != nil {
fmt.Println("接收消息失败,err:",err)
return
}
fmt.Println("收到回复:",string(buf[:n]))
}