文件处理&命令行参数

文件处理

打开文件和关闭文件

核心 API

  • 打开:os.Open(path)(只读)、os.OpenFile(path, flag, perm)
  • 关闭:defer file.Close()

按行读取示例

 f, err := os.Open("test.txt")
 if err != nil {
     // 处理错误
     return
 }
 defer f.Close()
 ​
 scanner := bufio.NewScanner(f)
 for scanner.Scan() {
     line := scanner.Text()
     fmt.Println(line)
 }
 if err := scanner.Err(); err != nil {
     // 处理读取过程中的错误
 }

带缓冲的 Reader 读取文件

要点

  • bufio.NewReader 在底层 *os.File 上再包一层缓冲,减少系统调用
  • 适合大文件、频繁小块读取等场景
  • 用 Read 循环读取,每次读到一个 []byte 缓冲中
 f, err := os.Open("test.txt")
 if err != nil {
     panic(err)
 }
 defer f.Close()
 ​
 reader := bufio.NewReader(f)
 buf := make([]byte, 1024) // 1KB 缓冲
 for {
     n, err := reader.Read(buf)
     if n > 0 {
         fmt.Print(string(buf[:n]))
    }
     if err == io.EOF {
         break
    }
     if err != nil {
         fmt.Println("read err:", err)
         break
    }
 }
 ​

一次性读取文件

要点

  • os.ReadFile:一次性把整个文件读入内存,返回 []byte
  • 适合文件较小,如配置文件、模板、示例数据等
  • Go 1.16 之后推荐用 os.ReadFile(代替 ioutil.ReadFile)
 data, err := os.ReadFile("test.txt")
 if err != nil {
     panic(err)
 }
 ​
 fmt.Printf("文件大小: %d 字节\n", len(data))
 fmt.Println(string(data))
创建文件并写入内容

要点

  • os.Create:如果文件存在则清空,不存在则创建
  • 返回的 *os.File 可直接调用 Write / WriteString
  • 默认权限 0666(再受 umask 影响)
 f, err := os.Create("hello.txt")
 if err != nil {
     panic(err)
 }
 defer f.Close()
 ​
 _, err = f.WriteString("Hello, Golang!\n")
 if err != nil {
     fmt.Println("write err:", err)
 }
 ​

写文件的四种方式

方式 1:os.WriteFile(一次性写入)
 data := []byte("hello, world")
 ​
 err := os.WriteFile("a.txt", data, 0644)
 if err != nil {
     fmt.Println("writefile err:", err)
 }
 ​

特点:简单粗暴,一次性写入所有数据,适合数据量较小场景。


方式 2:(*os.File).Write / WriteString
 f, err := os.OpenFile("b.txt", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
 if err != nil {
     panic(err)
 }
 defer f.Close()
 ​
 if _, err := f.Write([]byte("hello\n")); err != nil {
     fmt.Println("write err:", err)
 }
 ​
 // 或者
 if _, err := f.WriteString("hello again\n"); err != nil {
     fmt.Println("writeString err:", err)
 }
 ​

特点:适合需要多次写入、按块写入的场景。


方式 3:bufio.Writer 缓冲写
 f, err := os.OpenFile("c.txt", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
 if err != nil {
     panic(err)
 }
 defer f.Close()
 ​
 bw := bufio.NewWriter(f)
 if _, err := bw.WriteString("hello, bufio\n"); err != nil {
     fmt.Println("buffer write err:", err)
 }
 ​
 // 非常重要:写完后要 Flush
 if err := bw.Flush(); err != nil {
     fmt.Println("flush err:", err)
 }
 ​

特点

  • 先写到内存缓冲区,Flush() 时才真正写到文件
  • 适合很多小写操作,减少系统调用次数

方式 4:fmt.Fprint* 系列格式化写
 f, err := os.OpenFile("d.txt", os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
 if err != nil {
     panic(err)
 }
 defer f.Close()
 ​
 name := "Tom"
 age := 18
 fmt.Fprintf(f, "name: %s, age: %d\n", name, age)
 ​

特点

  • 支持格式化输出,和 fmt.Printf 一样
  • 更适合结构化文本输出(日志、报表等)

判断文件或目录是否存在

要点

  • os.Stat 获取文件信息
  • errors.Is(err, os.ErrNotExist) 判断“确实不存在”
  • 返回值中info.IsDir()可判断是否为目录

封装成工具函数

 func PathExists(path string) (exists bool, isDir bool, err error) {
     info, err := os.Stat(path)
     if err == nil {
         return true, info.IsDir(), nil
    }
     if errors.Is(err, os.ErrNotExist) {
         return false, false, nil
    }
     return false, false, err
 }
 ​

使用

 exists, isDir, err := PathExists("test.txt")
 if err != nil {
     fmt.Println("stat err:", err)
 } else if !exists {
     fmt.Println("文件不存在")
 } else if isDir {
     fmt.Println("是目录")
 } else {
     fmt.Println("是普通文件")
 }
 ​

拷贝文件(图片/视频/音频通用)

要点

  • 对于二进制文件(图片、视频、音频等),只是按“字节流”拷贝即可
  • io.Copy从源文件复制到目标文件,效率高、代码简单
  • 最后用Sync() 确保数据刷到磁盘

实现一个通用拷贝函数

 func CopyFile(src, dst string) error {
     in, err := os.Open(src)
     if err != nil {
         return err
    }
     defer in.Close()
 ​
     out, err := os.OpenFile(dst, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, 0644)
     if err != nil {
         return err
    }
     defer out.Close()
 ​
     if _, err := io.Copy(out, in); err != nil {
         return err
    }
     return out.Sync()
 }
 ​

使用

 if err := CopyFile("a.jpg", "b.jpg"); err != nil {
     fmt.Println("copy err:", err)
 }
 ​

统计不同类型的字符个数

要点

  • bufio.Reader.ReadRune 按“字符”(rune)读取,支持 UTF-8(中文不会被拆开)
  • unicode包判断字符类型:
    • unicode.IsLetter:字母(含中英文)
    • unicode.IsDigit:数字
    • unicode.IsSpace:空白字符(空格、换行、制表符等)

示例:统计字母、数字、空白、其他字符数量

 func CountChars(path string) (letters, digits, spaces, others int, err error) {
     f, err := os.Open(path)
     if err != nil {
         return
    }
     defer f.Close()
 ​
     r := bufio.NewReader(f)
     for {
         ch, _, e := r.ReadRune()
         if e != nil {
             if e == io.EOF {
                 break
            }
             err = e
             return
        }
 ​
         switch {
         case unicode.IsLetter(ch):
             letters++
         case unicode.IsDigit(ch):
             digits++
         case unicode.IsSpace(ch):
             spaces++
         default:
             others++
        }
    }
     return
 }
 ​

使用

 letters, digits, spaces, others, err := CountChars("test.txt")
 if err != nil {
     fmt.Println("count err:", err)
 } else {
     fmt.Println("字母:", letters)
     fmt.Println("数字:", digits)
     fmt.Println("空白:", spaces)
     fmt.Println("其他:", others)
 }
 ​

命令行参数

在实际开发中我们需要从命令行获得参数,比如配置文件的路径,监听哪个端口,我们希望灵活一点,可以通过命令行动态的传入参数

os包中有一个os.Args用来存储所有的命令行参数,我们来个例子

 package main
 ​
 import (
  "fmt"
  "os"
 )
 ​
 func main() {
  fmt.Println("命令行的参数有", len(os.Args))
  for i, v := range os.Args {
  fmt.Printf("args[%v]=%v\n", i, v)
  }
 }

flag包解析命令行参数

用os包的方式还是比较原生的, 对解析参数并不是特别方便,特别是有指定参数形式的命令行,Go设计者提供了flag包,可以方便的解析命令行参数,且参数的顺序可以随意flag package – flag – Go Packages

 package main
 import (
  "flag"
  "fmt"
 )
 func main(){
  var user string
  var pwd string
  var host string
  var port int
 ​
  //&user就是接受用户命令行输入-u后面的参数值
  //“u”就是-u指定参数
  flag.StringVar(&user, "u", "root", "数据库用户名,默认为 root")
  flag.StringVar(&pwd, "pwd", "123", "数据库用户名,默认为 123")
  flag.StringVar(&host, "h", "localhost", "数据库用户名,默认为 localhost")
  flag.IntVar(&port, "port", 3306, "数据库用户名,默认为 3306")
 ​
  //转换,必须执行这个方法
  flag.Parse()
  fmt.Printf("user=%s,pwd=%s,host=%s,port=%d\n", user, pwd, host, port)
 }

我们直接上案例不罗嗦。

暂无评论

发送评论 编辑评论


				
上一篇