📅 2024年5月10日
📦 使用版本为 1.21.5
读写数据
⭐️ 在 Go
中需要使用到 bufio
来读写数据
🌟 文件读写
1️⃣ 读文件
⭐️ 在 Go
中文件是用指向 os.File
类型的指针来表示,也叫做文件句柄
⭐️ 在 Go
中需要使用到 Open
打开文件,然后读写(和Python
类似 open with
),传参是一个 string
,返回两个参数一个是 *File
指针(在输入输出提到过),还有一个是错误,如果文件不存在或者程序没有足够的权限打开这个文件就会报错
可以看见 Open
函数就是调用了 OpenFile
,那么在后面执行肯定也可以使用 OpenFile
func Open(name string) (*File, error) {
return OpenFile(name, O_RDONLY, 0)
}
1️⃣ 在来看看这个 OpenFile
函数有三个参数:文件名、一个或多个标志(使用逻辑运算符 |
连接),使用的文件权限
func OpenFile(name string, flag int, perm FileMode) (*File, error) {
testlog.Open(name)
f, err := openFileNolog(name, flag, perm)
if err != nil { //如果有报错就返回报错
return nil, err
}
f.appendMode = flag&O_APPEND != 0
return f, nil //默认报错为nil
}
2️⃣ 和 Java
类似,之前学过 defer
和 finally
差不多,如果正常打开文件,就需要关闭文件
defer.[文件对象/文件变量].close
3️⃣ 在 OpenFile
函数中,会发现有个 O_RDONLY
传参,这个是文件的读取属性
// 用于OpenFile的标志,这些标志封装了底层系统的那些标志。并非所有系统都可能实现了所有标志。
const (
// 必须明确指定以下三个标志中的一个:O_RDONLY、O_WRONLY 或 O_RDWR。
O_RDONLY int = syscall.O_RDONLY // 以只读方式打开文件。
O_WRONLY int = syscall.O_WRONLY // 以只写方式打开文件。
O_RDWR int = syscall.O_RDWR // 以读写方式打开文件。
// 下列标志可按需组合使用以控制文件打开的行为。
O_APPEND int = syscall.O_APPEND // 写入时向文件追加数据。
O_CREATE int = syscall.O_CREAT // 如果文件不存在,则创建新文件。
O_EXCL int = syscall.O_EXCL // 与O_CREATE一起使用,要求文件必须不存在。
O_SYNC int = syscall.O_SYNC // 以同步I/O模式打开。
O_TRUNC int = syscall.O_TRUNC // 打开可写的常规文件时将其截断。
)
const (
ModeDir = fs.ModeDir // d: 目录
ModeAppend = fs.ModeAppend // a: 只能添加
ModeExclusive = fs.ModeExclusive // l: 专用
ModeTemporary = fs.ModeTemporary // T: 临时文件
ModeSymlink = fs.ModeSymlink // L: 符号链接
ModeDevice = fs.ModeDevice // D: 设备文件
ModeNamedPipe = fs.ModeNamedPipe // p: 具名管道 (FIFO)
ModeSocket = fs.ModeSocket // S: Unix 域套接字
ModeSetuid = fs.ModeSetuid // u: setuid
ModeSetgid = fs.ModeSetgid // g: setgid
ModeCharDevice = fs.ModeCharDevice // c: Unix 字符设备, 前提是设置了 ModeDevice
ModeSticky = fs.ModeSticky // t: 黏滞位
ModeIrregular = fs.ModeIrregular // ?: 非常规文件
// 类型位的掩码. 对于常规文件而言,什么都不会设置.
ModeType = fs.ModeType
ModePerm = fs.ModePerm // Unix 权限位, 0o777
)
⭐️ 在读文件的时候,文件的权限是被忽略的,所以在使用 OpenFile()
时传入的第三个参数可以用 0 。而在写文件时,不管是 Unix 还是 Windows,都需要使用 0666
4️⃣ 测试实例
func main() {
file, erros := os.Open("file") //读取文件
fmt.Printf("%v\n", erros) //输出错误如果打开正确那就是nil
fmt.Printf("%v", file) //输出一个指向文件的地址
defer file.Close(); //关闭文件
}
⭐️ 既然获取到文件地址了,那就可以对文件进行相应的操作,使用 bufio
来对文件进行操作
1️⃣ 使用 NewReader
来读取文件获得一个读取器变量,它返回的是一个 *Reader
类型,这个 defaultBufSize
应该是读取大小,默认是 4096
// NewReader returns a new Reader whose buffer has the default size.
func NewReader(rd io.Reader) *Reader {
return NewReaderSize(rd, defaultBufSize)
}
2️⃣ 再来看看 NewReaderSize
// NewReaderSize 返回一个新的Reader,其缓冲区大小至少为指定大小。
// 如果传入的io.Reader已经是一个缓冲区大小足够的Reader,那么直接返回这个底层的Reader。
//
// 参数:
// rd - 用于创建新Reader的io.Reader。它可以是实现io.Reader接口的任何类型。
// size - 新Reader所需缓冲区的最小大小。
//
// 返回:
// *Reader - 指向新创建的Reader的指针,其缓冲区大小至少为指定大小。
func NewReaderSize(rd io.Reader, size int) *Reader {
// 检查输入的reader是否已经是Reader类型并且缓冲区大小满足需求
b, ok := rd.(*Reader)
if ok && len(b.buf) >= size {
return b // 如果满足条件,直接复用现有的Reader
}
// 如果指定的大小小于最小缓冲区大小,则调整为最小值
if size < minReadBufferSize {
size = minReadBufferSize
}
// 创建并返回一个具有调整后缓冲区大小的新Reader
r := new(Reader)
r.reset(make([]byte, size), rd)
return r
}
2️⃣ Reader
类型也实现了非常多的接口,也提供了很多功能
它提供了很多 Read
方法来读取数据
⭐️ 在之前的例子中,我们看到,Unix 和 Linux 的行结束符是 \n
,而 Windows 的行结束符是 \r\n
。在使用 ReadString
和 ReadBytes
方法的时候,我们不需要关心操作系统的类型,直接使用 \n
就可以了
⭐️ 一旦读取到文件末尾,变量 readerError
的值将变成非空(事实上,其值为常量 io.EOF
),我们就会执行 return
语句从而退出循环
//ReadString
// ReadString 读取输入直到遇到第一个 delim,
// 返回一个包含数据(包括分隔符)的字符串。
// 如果 ReadString 在找到分隔符之前遇到错误,
// 它返回读取的数据和错误本身(通常是 io.EOF)。
// ReadString 仅当返回的数据不以 delim 结尾时才返回 err != nil。
// 对于简单用途,Scanner 可能更方便。
func (b *Reader) ReadString(delim byte) (string, error) {
full, frag, n, err := b.collectFragments(delim)
// 分配新的缓冲区以容纳完整的片段和片段。
var buf strings.Builder
buf.Grow(n)
// 将完整的片段和片段复制到缓冲区中。
for _, fb := range full {
buf.Write(fb)
}
buf.Write(frag)
return buf.String(), err
}
// ReadLine 是一个低级别的行读取基本操作。大多数调用者应考虑使用 ReadBytes('\n') 或 ReadString('\n') 替代,或者使用 Scanner。
//
// ReadLine 尝试返回一行内容,不包括行尾的字节。如果一行太长,超出了缓冲区的大小,则会设置 isPrefix 并返回行的开始部分。行的其余部分将从后续调用中返回。当返回行的最后一部分时,isPrefix 会被设置为 false。返回的缓冲区仅在下一次调用 ReadLine 之前有效。ReadLine 要么返回非空的行,要么返回错误,不会同时返回两者。
//
// 从 ReadLine 返回的文本不包含行结束符("\r\n" 或 "\n")。
// 如果输入结束时没有最后的行结束符,也不会给出指示或错误。
// 在调用 ReadLine 后调用 UnreadByte 总是会取消读取最后一个字节(可能是属于行尾的一部分),即使该字节不属于 ReadLine 返回的行。
func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error) {
line, err = b.ReadSlice('\n')
if err == ErrBufferFull {
// 处理 "\r\n" 分割缓冲区的情况。
if len(line) > 0 && line[len(line)-1] == '\r' {
// 将 '\r' 放回 buf 并从 line 中移除它。
// 让下一次调用 ReadLine 检查 "\r\n"。
if b.r == 0 {
// 应该是不可达的
panic("bufio: 尝试回退到缓冲区的起始位置之前")
}
b.r--
line = line[:len(line)-1]
}
return line, true, nil
}
if len(line) == 0 {
if err != nil {
line = nil
}
return
}
err = nil
if line[len(line)-1] == '\n' {
drop := 1
if len(line) > 1 && line[len(line)-2] == '\r' {
drop = 2 // 同时移除 "\r\n"
}
line = line[:len(line)-drop]
}
return
}
// ReadBytes 读取输入直到遇到第一个 delim,
// 返回一个包含数据(包括分隔符)的字节切片。
// 如果 ReadBytes 在找到分隔符之前遇到错误,
// 它返回读取的数据和错误本身(通常是 io.EOF)。
// ReadBytes 仅当返回的数据不以 delim 结尾时才返回 err != nil。
// 对于简单用途,Scanner 可能更方便。
func (b *Reader) ReadBytes(delim byte) ([]byte, error) {
full, frag, n, err := b.collectFragments(delim)
// 分配新的缓冲区以容纳完整的片段和片段。
buf := make([]byte, n)
n = 0
// 将完整的片段和片段复制到缓冲区中。
for i := range full {
n += copy(buf[n:], full[i])
}
copy(buf[n:], frag)
return buf, err
}
3️⃣ Read
实列
func main() {
file, erros := os.Open("file") //读取文件
if erros != nil { //错误
fmt.Println(erros)
}
defer file.Close()
fileReader := bufio.NewReader(file) //创建读取器
for {
line, err := fileReader.ReadString('\n') //按照行来读取
fmt.Printf("%v", line)
if err != nil { //遇到没有了,也就是报错了终止
break
}
}
}
//输出:
刮风这天我试过牵着你手
但偏偏
雨渐渐
大到我看你不见
4️⃣ 还可以将整个文件读取到一个字符内,使用 ReadFile
,它和 ReadLine
一样返回的是一个切片
// ReadFile 读取指定文件并返回其内容。
// 成功调用返回 err == nil,而不是 err == EOF。
// 因为 ReadFile 会读取整个文件,所以它不会将来自 Read 的 EOF 视为要报告的错误。
func ReadFile(name string) ([]byte, error) {
f, err := Open(name) // 打开文件
if err != nil { // 如果打开文件出错
return nil, err // 返回错误信息
}
defer f.Close() // 确保在函数结束时关闭文件
var size int // 定义变量 size 存储文件大小
if info, err := f.Stat(); err == nil { // 获取文件信息
size64 := info.Size() // 获取文件大小
if int64(int(size64)) == size64 { // 判断文件大小是否为整数
size = int(size64) // 将文件大小转换为整数类型
}
}
size++ // 为了在 EOF 处进行最后的读取,将文件大小加1
// 如果文件声明了较小的大小,则至少读取 512 字节。
// 特别是,Linux 中的 /proc 目录中的文件声明大小为 0,但
// 如果以小块读取,则无法正常工作,
// 因此初始读取 1 个字节将无法正确工作。
if size < 512 { // 如果文件大小小于 512
size = 512 // 将文件大小设置为 512
}
data := make([]byte, 0, size) // 创建一个长度为 size 的字节切片 data
for { // 循环读取文件内容
if len(data) >= cap(data) { // 如果 data 的长度大于等于容量
d := append(data[:cap(data)], 0) // 在 data 末尾添加一个字节
data = d[:len(data)] // 更新 data 的长度
}
n, err := f.Read(data[len(data):cap(data)]) // 从文件中读取数据到 data 中
data = data[:len(data)+n] // 更新 data 的长度
if err != nil { // 如果读取文件出错
if err == io.EOF { // 如果错误是 EOF
err = nil // 将错误设置为 nil
}
return data, err // 返回 data 和错误信息
}
}
}
测试
func main() {
file, err := os.ReadFile("file")
if err != nil {
fmt.Printf("%v", err)
}
fmt.Printf("%v", string(file))
}
//输出:
刮风这天我试过牵着你手
但偏偏
雨渐渐
大到我看你不见
5️⃣ 同样的还有 WriteFile
func main() {
file, err := os.ReadFile("file")
if err != nil {
fmt.Printf("%v", err)
}
fmt.Printf("%v", string(file))
err = os.WriteFile("file_copy", []byte(string(file)), 0644) //0644为读取权限
if err != nil {
fmt.Printf("%v", err)
}
}
⭐️ 带缓存读取很多情况下,文件的内容是不按行划分的,或者干脆就是一个二进制文件。在这种情况下,ReadString()
就无法使用了,就需要使用 Read
来读取字节数
// Read 将数据读取到 p 中。
// 它返回读取到 p 中的字节数量。
// 字节最多从底层 Reader 的一次读取操作中获取,因此 n 可能小于 len(p)。
// 要读取正好 len(p) 个字节,请使用 io.ReadFull(b, p)。
// 如果底层 Reader 在遇到 io.EOF 时可以返回非零计数,
// 那么此 Read 方法也可以这样做;请参阅 [io.Reader] 文档了解详情。
func (b *Reader) Read(p []byte) (n int, err error) {
n = len(p)
if n == 0 {
if b.Buffered() > 0 { // 缓冲区有数据但不读取
return 0, nil
}
return 0, b.readErr() // 直接返回错误
}
if b.r == b.w { // 缓冲区当前为空
if b.err != nil {
return 0, b.readErr() // 返回已存在的错误
}
if len(p) >= len(b.buf) { // 请求读取的字节数大于缓冲区大小,直接读到 p 中
n, b.err = b.rd.Read(p)
if n < 0 {
panic(errNegativeRead) // 防止读取负数字节的逻辑错误
}
if n > 0 {
b.lastByte = int(p[n-1]) // 记录最后一个字节
b.lastRuneSize = -1
}
return n, b.readErr()
}
// 单次读取操作,直接填充缓冲区
b.r = 0
b.w = 0
n, b.err = b.rd.Read(b.buf)
if n < 0 {
panic(errNegativeRead)
}
if n == 0 {
return 0, b.readErr()
}
b.w += n // 更新缓冲区写索引
}
// 尽可能多地复制数据到 p 中
n = copy(p, b.buf[b.r:b.w])
b.r += n // 更新读索引
b.lastByte = int(b.buf[b.r-1]) // 记录最后一个字节
b.lastRuneSize = -1
return n, nil // 成功读取 n 字节
}
1️⃣ 测试
func main() {
file, err := os.Open("file")
if err != nil {
fmt.Printf("%v", err)
}
defer file.Close()
fileReader := bufio.NewReader(file)
buf := make([]byte, 1024)
i, err := fileReader.Read(buf)
if err != nil {
fmt.Printf("%v", err)
}
fmt.Printf("%v", i)
}
⭐️ 如果数据是按列排列并用空格分隔的,你可以使用 fmt
包提供的以 FScan...
开头的一系列函数来读取他们,按列读取
func main() {
file, err := os.Open("file")
if err != nil {
panic(err)
}
defer file.Close()
var col1, col2, col3 []string
for {
var v1, v2, v3 string
_, err := fmt.Fscanln(file, &v1, &v2, &v3)
col1 = append(col1, v1)
col2 = append(col2, v2)
col3 = append(col3, v3)
// scans until newline
if err != nil {
break
}
}
fmt.Println(col1)
fmt.Println(col2)
fmt.Println(col3)
}
//输出:
[刮]
[风]
[这]
⭐️ Path
包内有个 filepath
,这个子包提供了跨平台的函数,用于处理文件名和路径。例如 Base()
函数用于获得路径中的最后一个元素(不包含后面的分隔符)
import "path/filepath"
filename := filepath.Base(path)
⭐️ 使用compress包读取压缩文件,compress
包提供了读取压缩文件的功能,支持的压缩文件格式为:bzip2、flate、gzip、lzw 和 zlib
。
package main
import (
"fmt"
"bufio"
"os"
"compress/gzip"
)
func main() {
fName := "MyFile.gz"
var r *bufio.Reader
fi, err := os.Open(fName)
if err != nil {
fmt.Fprintf(os.Stderr, "%v, Can't open %s: error: %s\n", os.Args[0], fName,
err)
os.Exit(1)
}
defer fi.Close()
fz, err := gzip.NewReader(fi)
if err != nil {
r = bufio.NewReader(fi)
} else {
r = bufio.NewReader(fz)
}
for {
line, err := r.ReadString('\n')
if err != nil {
fmt.Println("Done reading file")
os.Exit(0)
}
fmt.Println(line)
}
}
2️⃣ 写文件
⭐️ 写入和读入操作大差不多,注意事项基本上上面都提到了,写入使用 NewWriter
func NewWriter(w io.Writer) *Writer {
return NewWriterSize(w, defaultBufSize) //调用了NewWriterSize
}
// NewWriterSize 返回一个新的带有指定最小缓冲区大小的 Writer。如果传入的 io.Writer 本身已经是一个缓冲区大小足够的 Writer,那么就直接返回这个底层的 Writer。
//
// 参数:
// w io.Writer: 需要封装的io.Writer接口实例。
// size int: 缓冲区期望的最小大小。
//
// 返回值:
// *Writer: 一个具有指定缓冲区大小的 Writer 实例。
//
// 注意:
// - 如果传入的 w 本身就是一个 Writer 且其缓冲区大小不低于指定的 size,直接返回该 Writer。
// - 如果 size 小于等于0,则会使用默认的缓冲区大小 defaultBufSize。
func NewWriterSize(w io.Writer, size int) *Writer {
// 检查 w 是否已经是 Writer 类型
b, ok := w.(*Writer)
if ok && len(b.buf) >= size { // 如果是且其缓冲区足够大,直接返回
return b
}
if size <= 0 { // 如果指定的大小不合法,使用默认大小
size = defaultBufSize
}
// 创建并返回新的 Writer 实例,初始化缓冲区大小为 size
return &Writer{
buf: make([]byte, size),
wr: w,
}
}
⭐️ 它也提供了很多 Write
方法,测试实例
func main() {
file, err := os.OpenFile("file", os.O_WRONLY, 0666)
if err != nil {
panic(err)
}
defer file.Close()
fileWriter := bufio.NewWriter(file)
text := "晴天-周杰伦"
fileWriter.WriteString(text)
fileWriter.Flush()
}
3️⃣ 文件拷贝
⭐️ 使用 io.Copy
// filecopy.go
package main
import (
"fmt"
"io"
"os"
)
func main() {
CopyFile("target.txt", "source.txt")
fmt.Println("Copy done!")
}
func CopyFile(dstName, srcName string) (written int64, err error) {
src, err := os.Open(srcName)
if err != nil {
return
}
defer src.Close()
dst, err := os.Create(dstName)
if err != nil {
return
}
defer dst.Close()
return io.Copy(dst, src)
}
4️⃣ 从命令行读取参数
os
包
⭐️ os
包中有一个 string
类型的切片变量 os.Args
,用来处理一些基本的命令行参数
var Args []string //源代码是一个string切片
func init() {
if runtime.GOOS == "windows" {
// Initialized in exec_windows.go.
return
}
Args = runtime_args() //第一个索引也就是0,是当前运行的程序名
}
⭐️ 简单测试
package main
import (
"fmt"
"os"
)
func main() {
fmt.Printf("%v", os.Args)
}
//命令行执行go build .
//会生成一个exe然后
.\var.exe Linux!!
[D:\learnCode\Go\var\var.exe Linux!!]
⭐️ 可以实现 echo
,也就是 linux
的输出
flag
包
⭐️ 它有个扩展功能来解析命令行选项。但是通常别替换成基本常量,也就是它可以实现像 linux
的 shell
功能,如果以后需要使用 linux
命令行开发,可能需要用到
⭕️ 下面是一个测试案例(这里我新建了一个项目叫做 gosql
)
⭐️ 它使用 类型var
类的函数来接受参数,它可以支持很多类型,可以在 flag
源文件内看,var
的函数需要自己定义一个变量然后传参到函数内去接收,还有一种不带 var
的,它少了一个接收参数函数,多了一个返回值,也可以直接用这个返回值来定义变量
//第一个传参表示变量,第二个为参数名字,第二个为参数默认值也就是在命令未指定情况下的配置,第三个为解释,就是使用-h或者--help会显示
func BoolVar(p *bool, name string, value bool, usage string)
func DurationVar(p *time.Duration, name string, value time.Duration, usage string)
func Float64Var(p *float64, name string, value float64, usage string)
func Int64Var(p *int64, name string, value int64, usage string)
func IntVar(p *int, name string, value int, usage string)
func StringVar(p *string, name string, value string, usage string)
func Uint64Var(p *uint64, name string, value uint64, usage string)
func UintVar(p *uint, name string, value uint, usage string)
func Bool(name string, value bool, usage string) *bool
func Duration(name string, value time.Duration, usage string) *time.Duration
func Float64(name string, value float64, usage string) *float64
func Int(name string, value int, usage string) *int
func Int64(name string, value int64, usage string) *int64
func String(name string, value string, usage string) *string
func Uint(name string, value uint, usage string) *uint
func Uint64(name string, value uint64, usage string) *uint64
⭐️ Parse()
函数用于解析 var
函数接受的参数
这个是它的源代码,你会发现最后还是接收的一个 os.Args
,它内部也是使用的 CommadLine
的 Parse
func Parse() {
// Ignore errors; CommandLine is set for ExitOnError.
CommandLine.Parse(os.Args[1:])
}
CommadLine
的类型是一个 FlagSet
,FlagSet
就是一个命令行参数的集合体,当我们调用诸如 IntVar
这类的函数时,就是将命令行的 默认值
、参数说明
,参数名称
,接收参数的变量
等信息告诉flag库,而 flag
内部会让 CommandLine
来处理,用这些信息创建 Flag
类型的变量,将添加到这个集合体中
var CommandLine = NewFlagSet(os.Args[0], ExitOnError)
// Parse 从参数列表中解析标志定义,该参数列表不应包含命令名称。
// 必须在FlagSet中定义所有标志之后调用,并在程序访问标志之前调用。
// 如果设置了-help或-h但没有定义,则返回值将为ErrHelp。
func (f *FlagSet) Parse(arguments []string) error {
f.parsed = true // 标记已解析
f.args = arguments // 保存参数列表
for {
seen, err := f.parseOne() // 尝试解析一个标志
if seen { // 如果成功解析了一个标志,继续解析下一个
continue
}
if err == nil { // 如果没有错误,说明所有标志都已解析完毕,跳出循环
break
}
switch f.errorHandling { // 根据错误处理方式进行处理
case Continue: // 继续处理错误
if err == ErrHelp { // 如果错误是帮助信息,退出程序
os.Exit(0)
}
os.Exit(2) // 其他错误,退出程序并返回错误码2
case PanicOnError: // 遇到错误时,抛出异常
panic(err)
}
}
return nil // 返回nil表示解析成功
}
测试代码使用带var的:
package main
import (
"flag"
"fmt"
)
// 设置字段
var (
host string
dbName string
port int
user string
password string
)
func main() {
//StringVar、IntVar 函数将变量绑定到命令行参数上
flag.StringVar(&host, "host", "", "数据库地址")
flag.StringVar(&dbName, "db_name", "", "数据库名称")
flag.StringVar(&user, "user", "", "数据库用户")
flag.StringVar(&password, "password", "", "数据库密码")
flag.IntVar(&port, "port", 3306, "数据库端口")
// 解析命令行参数
flag.Parse()
// 打印接受的参数
fmt.Printf("数据库地址:%s\n", host)
fmt.Printf("数据库名称:%s\n", dbName)
fmt.Printf("数据库用户:%s\n", user)
fmt.Printf("数据库密码:%s\n", password)
fmt.Printf("数据库端口:%d\n", port)
}
使用不带 var
的
func main() {
//StringVar、IntVar 函数将变量绑定到命令行参数上
host := flag.String("host", "", "数据库地址")
dbName := flag.String( "db_name", "", "数据库名称")
user := flag.String("user", "", "数据库用户")
password := flag.String( "password", "", "数据库密码")
port := flag.Int("port", 3306, "数据库端口")
// 解析命令行参数
flag.Parse()
// 打印接受的参数
fmt.Printf("数据库地址:%s\n", host)
fmt.Printf("数据库名称:%s\n", dbName)
fmt.Printf("数据库用户:%s\n", user)
fmt.Printf("数据库密码:%s\n", password)
fmt.Printf("数据库端口:%d\n", port)
}
1️⃣ 使用 go build
(开始就说过用处了),然后在使用 gosql
命令
PS D:\learnCode\Go\gosql> .\gosql.exe -host localhost -db_name gosql -user root -password xxxxxx -port 3305
数据库地址:localhost
数据库名称:gosql
数据库用户:root
数据库密码:xxxxxx
数据库端口:3305
//它还会提示
Usage of D:\learnCode\Go\gosql\gosql.exe:
-db_name string
数据库名称
-host string
数据库地址
-password string
数据库密码
-port int
数据库端口 (default 3306)
-user string
数据库用户
⭐️ 上面使用的是 cmd -参数 传参
这种命令它还支持下面这几种
cmd -flag //只用布尔值的选项,如果该参数出现,则为true,不出则为默认值,而其他数据类型不能使用这种格式传值
cmd -flag=x //=
cmd -flag x //不可用于布尔值的选项
⚠️ 注意: 布尔值可以接收 1, 0, t, f, T, F, true, false, TRUE, FALSE, True, False
⭐️ 下面是一个简单的 echo
实现代码
package main
import (
"flag"
)
func main() {
isEcho := flag.Bool("echo", false, "echo the string") //获取是否输入了参数
flag.Parse()
if *isEcho { //如果没有输出参数打印帮助信息
flag.PrintDefaults() //打印帮助信息
} else if flag.NArg() > 0 { //如果输入了NArg就是接收输入参数的长度
for _, arg := range flag.Args() { //使用for循环来打印默认分隔符为空格
println(arg)
}
}
}
❓ 下面是一个 Flag
包使用函数的方法
函数名 | 功能描述 |
---|---|
Bool() |
返回一个布尔类型的值,如果命令行中存在指定的参数,则返回该参数的布尔值,否则返回false。 |
BoolVar() |
返回一个布尔类型的值,如果命令行中存在指定的参数,则返回该参数的布尔值,否则返回指定的默认值。 |
Duration() |
返回一个时间间隔类型的值,如果命令行中存在指定的参数,则返回该参数的时间间隔值,否则返回0。 |
DurationVar() |
返回一个时间间隔类型的值,如果命令行中存在指定的参数,则返回该参数的时间间隔值,否则返回指定的默认值。 |
Float64() |
返回一个浮点数类型的值,如果命令行中存在指定的参数,则返回该参数的浮点数值,否则返回0。 |
Float64Var() |
返回一个浮点数类型的值,如果命令行中存在指定的参数,则返回该参数的浮点数值,否则返回指定的默认值。 |
Int() |
返回一个整数类型的值,如果命令行中存在指定的参数,则返回该参数的整数值,否则返回0。 |
IntVar() |
返回一个整数类型的值,如果命令行中存在指定的参数,则返回该参数的整数值,否则返回指定的默认值。 |
String() |
返回一个字符串类型的值,如果命令行中存在指定的参数,则返回该参数的字符串值,否则返回空字符串。 |
StringVar() |
返回一个字符串类型的值,如果命令行中存在指定的参数,则返回该参数的字符串值,否则返回指定的默认值。 |
Parse() |
解析命令行参数,并将结果存储在flag包中的变量中。 |
Parsed() |
返回一个布尔值,表示是否已解析命令行参数。 |
Args() |
返回一个字符串切片,包含未被flag包处理的命令行参数。 |
NArg() |
返回一个整数,表示指定的参数在命令行中出现的次数。 |
NFlag() |
返回一个布尔值,表示是否设置了指定的参数。 |
NBool() |
返回一个布尔值,如果命令行中存在指定的参数,则返回该参数的布尔值,否则返回false。 |
NBoolVar() |
返回一个布尔值,如果命令行中存在指定的参数,则返回该参数的布尔值,否则返回指定的默认值。 |
NDuration() |
返回一个时间间隔类型的值,如果命令行中存在指定的参数,则返回该参数的时间间隔值,否则返回0。 |
NDurationVar() |
返回一个时间间隔类型的值,如果命令行中存在指定的参数,则返回该参数的时间间隔值,否则返回指定的默认值。 |
NFloat64() |
返回一个浮点数类型的值,如果命令行中存在指定的参数,则返回该参数的浮点数值,否则返回0。 |
NFloat64Var() |
返回一个浮点数类型的值,如果命令行中存在指定的参数,则返回该参数的浮点数值,否则返回指定的默认值。 |
NInt() |
返回一个整数类型的值,如果命令行中存在指定的参数,则返回该参数的整数值,否则返回0。 |
NIntVar() |
返回一个整数类型的值,如果命令行中存在指定的参数,则返回该参数的整数值,否则返回指定的默认值。 |
NString() |
返回一个字符串类型的值,如果命令行中存在指定的参数,则返回该参数的字符串值,否则返回空字符串。 |
NStringVar() |
返回一个字符串类型的值,如果命令行中存在指定的参数,则返回该参数的字符串值,否则返回指定的默认值。 |
Visit() |
遍历并处理命令行参数,对于每个参数,调用提供的回调函数进行处理。 |
VisitAll() |
遍历并处理所有命令行参数,包括被flag包处理和未被处理的参数,对于每个参数,调用提供的回调函数进行处理。 |
PrintDefaults() |
打印当前flag包中所有变量的默认值。 |
Set() |
设置指定的参数的值,用于在程序运行时动态修改参数值。 |
SetOutput() |
设置输出函数,用于将解析结果输出到指定的位置,如文件或标准输出。 |
Usage |
打印命令行参数的使用方法。 |