Golang标准库学习 - fmt与os

192 阅读11分钟

fmt库

输出

func TestPrint(t *testing.T) {
   fmt.Print("我是控制台输出,不带换行\n")
   fmt.Println("我是控制台输出,带换行", "第二个", 5555)
   fmt.Printf("我是控制台输出, %s 占位符", "张三")
}
输出:
   我是控制台输出,不带换行
   我是控制台输出,带换行 第二个 5555
   我是控制台输出, 张三 占位符

注意点

  • Print输出不带换行
  • Println输出带换行
  • Printf为格式化输出

占位符

通用占位符

  • %v 值的默认格式表示
  • %+v 类似%v,但输出结构体时会添加字段名
  • %#v 值的Go语法表示,+包名+结构体名+字段名
  • %T 打印值的类型
  • %% 打印百分号
type User struct {
   Id int64
}

func TestPrintf1(t *testing.T) {
   user := &User{Id: 1}
   fmt.Printf("%v\n", user)
   fmt.Printf("%+v\n", user)
   fmt.Printf("%#v\n", user)
   fmt.Printf("%T\n", user)
   fmt.Printf("%%\n")
}
输出:
    &{1}
    &{Id:1}
    &fmt_test.User{Id:1}
    *fmt_test.User
    %

注意点

  • %+v会在输出结构体时输出字段名
  • %#v会输出包名+结构体名+字段名

布尔类型占位符

func TestPrintf2(t *testing.T) {
   fmt.Printf("%t\n", true)
}

注意点

布尔类型的占位符为%t

整型占位符

func TestPrintf3(t *testing.T) {
   n := 180
   fmt.Printf("%b\n", n) // 二进制表示
   fmt.Printf("%c\n", n) // 该值对应unicode的表示,即字符的表示
   fmt.Printf("%d\n", n) // 十进制表示
   fmt.Printf("%o\n", n) // 八进制表示
   fmt.Printf("%x\n", n) // 十六进制小写表示
   fmt.Printf("%X\n", n) // 十六进制大写表示
   fmt.Printf("%U\n", n) // 加Unicode前置Unicode格式表示
   a := 96
   fmt.Printf("%q\n", a) // 该值对应的单引号括起来的go语法字符字面值,必要时会采用安全的转义表示
   fmt.Printf("%q\n", 0x4E2D)
}
输出:
   10110100
   ´
   180
   264
   b4
   B4
   U+00B4
   '`'
   '中'

注意点

  • %b为转换为二进制输出的占位符
  • %c是转换为对应的unicode字符输出
  • %d是十进制表示
  • %o是八进制表示
  • %x是十六进制的小写表示
  • %X是十六进制的大写表示

浮点数与复数占位符

  • %b 无小数部分、二进制指数的科学计数法,如-123456p-78
  • %e 科学计数法,如-1234.456e+78
  • %E 科学计数法,如-1234.456E+78
  • %f 有小数部分但无指数部分,如123.456
  • %F 等价于%f
  • %g 根据实际情况采用%e或%f格式(以获得更简洁、准确的输出)
  • %G 根据实际情况采用%E或%F格式(以获得更简洁、准确的输出)
func TestPrintf4(t *testing.T) {
   f := 18.54
   fmt.Printf("%b\n", f)
   fmt.Printf("%e\n", f)
   fmt.Printf("%E\n", f)
   fmt.Printf("%f\n", f)
   fmt.Printf("%F\n", f)
   fmt.Printf("%g\n", f)
   fmt.Printf("%G\n", f)
}
输出:
   5218546068215562p-48
   1.854000e+01
   1.854000E+01
   18.540000
   18.540000
   18.54
   18.54

字符串与byte数组占位符

  • %s 直接输出字符串或者[]byte
  • %q 该值对应的双引号括起来的go语法字符串字面值,必要时会采用安全的转义表示
  • %x 每个字节用两字符十六进制数表示(使用a-f
  • %X 每个字节用两字符十六进制数表示(使用A-F)
func TestPrintf5(t *testing.T) {
   s := "我是字符串"
   b := []byte{65, 66, 67}
   fmt.Printf("%s\n", s)
   fmt.Printf("%s\n", b)
   fmt.Printf("%q\n", s)
   fmt.Printf("%x\n", s)
   fmt.Printf("%X\n", s)
}
输出:
   我是字符串
   ABC
   "我是字符串"
   e68891e698afe5ad97e7aca6e4b8b2
   E68891E698AFE5AD97E7ACA6E4B8B2

宽度标识符

  • 宽度通过一个紧跟在百分号后面的十进制数指定,如果未指定宽度,则表示值时除必需之外不作填充。

  • 精度通过(可选的)宽度后跟点号后跟的十进制数指定。如果未指定精度,会使用默认精度;如果点号后没有跟数字,表示精度为0。

  • %f 默认宽度,默认精度

  • %10f 宽度9,默认精度

  • %.2f 默认宽度,精度2(会四舍五入)

  • %10.2f 宽度9,精度2

  • %10.f 宽度9,精度0

func TestPrintf6(t *testing.T) {
	n := 13.14
	fmt.Printf("%f\n", n)
	fmt.Printf("%10f\n", n)
	fmt.Printf("%10s\n", "我是字符串")
	fmt.Printf("%.2f\n", n)
	fmt.Printf("%10.2f\n", n)
	fmt.Printf("%10.f\n", n)
}
输出:
13.140000
 13.140000
     我是字符串
13.14
     13.14
        13

一个重要的内容:文件权限

  • 对于一个perm为0644时,其中0表示8进制 644表示权限 os.FileMode(0777).String()进行打印
    • rwx rwx rwx -表示普通文件
  • r表示可读
  • w表示可写
  • x表示可执行
  • 第1位:文件属性,一般常用的是"-",表示是普通文件;"d"表示是一个目录。
  • 第2~4位:文件所有者的权限rwx (可读/可写/可执行)。
  • 第5~7位:文件所属用户组的权限rwx (可读/可写/可执行)。
  • 第8~10位:其他人的权限rwx (可读/可写/可执行)。
  • 在golang中,可以使用os.FileMode(perm).String()来查看权限标识:
  • os.FileMode(0777).String() //返回 -rwxrwxrwx 111 111 111
  • os.FileMode(0666).String() //返回 -rw-rw-rw- 110 110 110
  • os.FileMode(0644).String() //返回 -rw-r--r--
  • 0777表示:创建了一个普通文件,所有人拥有所有的读、写、执行权限
  • 0666表示:创建了一个普通文件,所有人拥有对该文件的读、写权限,但是都不可执行
  • 0644表示:创建了一个普通文件,文件所有者对该文件有读写权限,用户组和其他人只有读权限,都没有执行权限

Fprint

go源码

func Fprint(w io.Writer, a ...any) (n int, err error) {
	p := newPrinter()
	p.doPrint(a)
	n, err = w.Write(p.buf)
	p.free()
	return
}
func Fprintf(w io.Writer, format string, a ...any) (n int, err error) {
	p := newPrinter()
	p.doPrintf(format, a)
	n, err = w.Write(p.buf)
	p.free()
	return
}
func Fprintln(w io.Writer, a ...any) (n int, err error) {
	p := newPrinter()
	p.doPrintln(a)
	n, err = w.Write(p.buf)
	p.free()
	return
}
`n`是写入的字节数量,`err`是返回的错误

一般用于写文件时,追加写入。

func TestFPrint(t *testing.T) {
    fmt.Fprintln(os.Stdout, "向标准输出写入字符串")
}
func TestFPrint1(t *testing.T) {
	file, _ := os.OpenFile("test.txt", os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0644)
	fmt.Fprintln(file, "追加写入")
	file.Close()
}

Sprint

把传入的数据生成,并返回一个字符串。
常用于解决字符串不能拼接非字符串类型的数据。

func TestSprint(t *testing.T) {
   host := "localhost"
   port := 6379
   // 解决字符串不能拼接非字符串类型的数据
   addr := fmt.Sprintf("%s:%d", host, port)
   fmt.Println(addr)
}

Errorf

根据format参数生成格式化字符串并返回一个包含该字符串的错误。
用于自定义错误信息。

func TestErrorf(t *testing.T) {
   err := fmt.Errorf("用户名格式不正确: %s", "哈哈")
   if err != nil {
      panic(err)
   }
}

输入

Scan

go源码

func Scan(a ...any) (n int, err error) {
	return Fscan(os.Stdin, a...)
}

从标准输入扫描文本,读取由空白符分隔的值保存到传递给本函数的参数中,换行符视为空白符。
本函数返回成功扫描的数据个数(n)和遇到的任何错误(error)。

Scanf

go源码

func Scanf(format string, a ...any) (n int, err error) {
	return Fscanf(os.Stdin, format, a...)
}

实际使用的是Fscanf

以format定义的格式来进行输入

func main() {
	var (
		name    string
		age     int
		married bool
	)
	fmt.Scanf("1:%s 2:%d 3:%t", &name, &age, &married)
	fmt.Printf("扫描结果 name:%s age:%d married:%t \n", name, age, married)
}

Scanln

go源码

func Scanln(a ...any) (n int, err error) {
	return Fscanln(os.Stdin, a...)
}
func main() {
	var (
		name    string
		age     int
		married bool
	)
	fmt.Scanln(&name, &age, &married)
	fmt.Printf("扫描结果 name:%s age:%d married:%t \n", name, age, married)
}

遇到回车就结束扫描。

Fsanf

go源码

func Fscanf(r io.Reader, format string, a ...any) (n int, err error) {
	s, old := newScanState(r, false, false)
	n, err = s.doScanf(format, a)
	s.free(old)
	return
}
func Fscan(r io.Reader, a ...any) (n int, err error) {
	s, old := newScanState(r, true, false)
	n, err = s.doScan(a)
	s.free(old)
	return
}
func Fscanln(r io.Reader, a ...any) (n int, err error) {
	s, old := newScanState(r, false, true)
	n, err = s.doScan(a)
	s.free(old)
	return
}

将内容从一个io.Reader接口类型的变量r中读取出来,将连续的以空格分隔的值存储到由格式确定的连续的参数中。

  • r io.Reader:此参数包含扫描的指定文本。
  • format string:此参数包含用于接收元素的不同格式。
  • a …any:此参数是每个元素的指定变量。

返回值:它返回成功解析的项目数和错误。

os库

关于权限的重要常量

go中包含了很多常量,用于在os操作时设置文件的权限与进行操作。

const (
   // Exactly one of O_RDONLY, O_WRONLY, or O_RDWR must be specified.
   O_RDONLY int = syscall.O_RDONLY // open the file read-only.
   O_WRONLY int = syscall.O_WRONLY // open the file write-only.
   O_RDWR   int = syscall.O_RDWR   // open the file read-write.
   // The remaining values may be or'ed in to control behavior.
   O_APPEND int = syscall.O_APPEND // append data to the file when writing.
   O_CREATE int = syscall.O_CREAT  // create a new file if none exists.
   O_EXCL   int = syscall.O_EXCL   // used with O_CREATE, file must not exist.
   O_SYNC   int = syscall.O_SYNC   // open for synchronous I/O.
   O_TRUNC  int = syscall.O_TRUNC  // truncate regular writable file when opened.
)

os.Create - 创建文件

// 创建文件
func TestCreate(t *testing.T) {
   file, err := os.Create("a.txt")
   if err != nil {
      panic(err)
   }
   defer file.Close()
}

os.Mkdir - 创建单个目录

参数为目录名以及权限。

// 创建单个目录
func TestMkdir(t *testing.T) {
   err := os.Mkdir("dir1", os.ModePerm)
   if err != nil {
      panic(err)
   }
}

os.MkdirAll - 创建多级目录

func TestMkdirAll(t *testing.T) {
   err := os.MkdirAll("dir1/a/b", os.ModePerm)
   if err != nil {
      panic(err)
   }
}

os.Remove - 删除一个文件或目录

// 删除一个文件或目录
func TestRemove(t *testing.T) {
   err := os.Remove("dir1/a/b")
   if err != nil {
      panic(err)
   }
}

os.RemoveAll - 删除整个目录

// 删除整个目录
func TestRemoveAll(t *testing.T) {
   err := os.RemoveAll("dir1")
   if err != nil {
      panic(err)
   }
}

os.Getwd - 获取工作目录

获取工作目录
func TestGetwd(t *testing.T) {
   dir, err := os.Getwd()
   if err != nil {
      panic(err)
   } else {
      fmt.Print(dir)
   }
}

os.Chdir - 修改工作目录

// 修改工作目录
func TestChdir(t *testing.T) {
   err := os.Chdir("")
   if err != nil {
      panic(err)
   }
}

os.TempDir - 获取临时目录

获取临时目录可以在其中存一些缓存或者中间值。

// 获取临时目录
func TestTempDir(t *testing.T) {
   tempDir := os.TempDir()
   fmt.Println(tempDir)
}

os.Rename - 重命名文件

// 重命名文件
func TestRename(t *testing.T) {
   err := os.Rename("a.txt", "text1.txt")
   if err != nil {
      fmt.Println(err)
   }
}

os.Chmod - 修改文件的权限

这个似乎得跑在linux上。

// 修改文件的权限
func TestChmod(t *testing.T) {
   //err := os.Chmod("text1.txt", 0666)
}

os.Chown - 改变文件的所有者

这个似乎也得跑在linux上。

// 改变文件的所有者
func TestChown(t *testing.T) {
   err := os.Chown("a.txt", 10, 100)
   if err != nil {
      panic(err)
   }
}

进程相关

func osInfo() {
   // 获取当前正在运行的进程id
   fmt.Println("-----------------")
   fmt.Printf("os.Getpid(): %v\n", os.Getpid())
   // 父id
   fmt.Printf("os.Getppid: %v\n", os.Getppid())

   // 设置新进程的属性
   attr := &os.ProcAttr{
      // files指定新进程继承的活动文件对象
      // 前三个分别为,标准输入、标准输出、标准错误输出
      Files: []*os.File{os.Stdin, os.Stdout, os.Stderr},
      // 新进程的环境变量
      Env: os.Environ(),
   }

   // 开启一个新进程
   p, err := os.StartProcess("C:\Program Files (x86)\Notepad++\notepad++.exe",
      []string{"C:\Program Files (x86)\Notepad++\notepad++.exe"}, attr)

   if err != nil {
      panic(err)
   }

   fmt.Println(p)
   fmt.Println("进程ID: ", p.Pid)

   // 通过进程ID查找进程
   p2, _ := os.FindProcess(p.Pid)
   fmt.Println(p2)

   // 等待10s,执行函数
   time.AfterFunc(time.Second*10, func() {
      // 向p进程发出退出信号
      p.Signal(os.Kill)
   })

   // 等待进程p的退出,返回进程状态
   ps, _ := p.Wait()
   fmt.Println(ps.String())
}

注意点

  • os.Getpid - 获取当前进程的pid
  • os.Getppid - 获取当前进程的父进程的pid
  • os.ProcAttr结构体的字段有Files和Env
    • Files: Files指定新进程继承的活动文件对象 前三个分别为,标准输入、标准输出、标准错误输出
    • Env: 新进程的环境变量
  • os.StartProcess - 开启新进程,要传入进程的路径、字符串路径以及属性
  • os.FindProcess - 通过pid查找进程

信号量

信号量可以帮助解决开启多个阻塞操作的问题。

type MyHandler struct {
}

func (m MyHandler) ServeHTTP(writer http.ResponseWriter, request *http.Request) {
   //TODO implement me
   panic("implement me")
}

func a() {
   http.ListenAndServe(":8088", &MyHandler{})
}

func b() {
   http.ListenAndServe(":8089", &MyHandler{})
}

func main() {
   // 实现开启多个阻塞操作
   go a()
   go b()
   ch := make(chan os.Signal)
   signal.Notify(ch, os.Interrupt, os.Kill)
   c := <-ch
   fmt.Println(c)
}
  • signal.Notify - 传入的是管道,和终止的条件

文件相关

读取文件信息 - Stat

读取文件的信息

func fileStat() {
   f, err := os.OpenFile("text1.txt", os.O_RDWR|os.O_CREATE, 0755)
   if err != nil {
      panic(err)
   }
   defer f.Close()
   fileInfo, err := f.Stat()
   if err != nil {
      panic(err)
   }
   fmt.Printf("FileInfo:%v", fileInfo)
}

信息包括这些字段:

type FileInfo interface {
   Name() string       // base name of the file
   Size() int64        // length in bytes for regular files; system-dependent for others
   Mode() FileMode     // file mode bits
   ModTime() time.Time // modification time
   IsDir() bool        // abbreviation for Mode().IsDir()
   Sys() any           // underlying data source (can return nil)
}

读取文件内容 - Read

可在创建byte切片时设定一次性读入的长度,并append给body,body存入每次读入的内容。

func fileRead() {
   f, err := os.OpenFile("text1.txt", os.O_RDWR|os.O_CREATE, 0755)
   if err != nil {
      panic(err)
   }
   defer f.Close()
   var body []byte
   for {
      buf := make([]byte, 4)
      n, err := f.Read(buf)
      if err == io.EOF {          // 读完了
         break
      }
      body = append(body, buf[:n]...)
   }
   fmt.Printf("Read Content:%v", body)
}

从指定位置读取文件内容 - ReadAt

从指定的位置开始读。

func fileReadAt() {
   f, err := os.OpenFile("text1.txt", os.O_RDWR|os.O_CREATE, 0755)
   if err != nil {
      panic(err)
   }
   defer f.Close()
   buf := make([]byte, 5)     // 读5个
   n, err := f.ReadAt(buf, 6) // 从第六位开始读
   fmt.Printf("Read Content:%v", buf[:n])
}

读取目录 - ReadDir

读取目录并返回排好序的文件以及子目录名切片。

dirs, err := f.ReadDir(-1) // -1代表读取所有
for _, v := range dirs {

}

设置读/写位置 - Seek

  • seek设置下一次读/写的位置,offset为相对偏移量,而whence决定相对位置,0位相对文件开头,1为相对当前位置,2位相对文件结尾
  • 它返回新的偏移量(相对开头)和可能的错误
func FileSeek() {
   f, err := os.OpenFile("text1.txt", os.O_RDWR|os.O_CREATE, 0755)
   if err != nil {
      panic(err)
   }

   defer f.Close()
   f.Seek(3, 0)
   buf := make([]byte, 10)
   n, _ := f.Read(buf)
   fmt.Printf("读取内容:%s\n", buf[:n])
}

文件写入 - Write与WriteString

  • Write向文件中写入len(b)字节数据,它返回写入的字节数和可能遇到的任何错误,如果返回值n!=len(b),本方法会返回一个非nil的错误
  • WriteString和Write类似,但可以直接写入字符串,更方便些。
func fileWrite() {
   f, err := os.OpenFile("text1.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0755)
   if err != nil {
      panic(err)
   }
   defer f.Close()
   _, err1 := f.Write([]byte("hello\n"))
   if err1 != nil {
      panic(err1)
   }
   f.WriteString("world!\n")
}

指定位置写入 - WriteAt

从某个位置开始写入,注意写入的位置如果有原内容则会覆盖原内容。

func fileWriteAt() {
   f, err := os.OpenFile("text1.txt", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0755)
   if err != nil {
      panic(err)
   }
   defer f.Close()
   _, err = f.WriteAt([]byte("GOLANG\n"), 5)
   fmt.Println(err)
}

环境变量相关

func osEnv() {
   // 获取所有的环境变量
   s := os.Environ()
   fmt.Printf("evviron: %v\n", s)
   // 获取某个环境变量
   s2 := os.Getenv("GOPATH")
   fmt.Printf("GOPATH: %v\n", s2)
   // 设置环境变量
   os.Setenv("env1", "env1")
   s2 = os.Getenv("aaa")
   fmt.Printf("aaa: %v\n", s2)
   fmt.Println("-------------------")

   // 查找
   s3, b := os.LookupEnv("env1")
   fmt.Printf("env1: %v\n", s3)
   fmt.Printf("是否存在: %v\n", b)

   // 清空环境变量
   //os.Clearenv()

}

注意点

  • os.Environ - 获取所有的环境变量
  • os.Getenv - 获取某个环境变量
  • os.Setenv - 设置某个环境变量
  • os.LookupEnv - 查找某个环境变量,返回的是环境变量的值与是否存在
  • os.Clearenv - 清空环境变量