golang小程序试验(五)

1. golang的指针

可以将unsafe.Pointer转换成uintptr,然后变相做指针运算

package main

import "fmt"
import "unsafe"

type User struct {
	Id   int
	Name string
}

func main() {
	u := &User{1,"tom"}
	var up uintptr = uintptr(unsafe.Pointer(u)) + unsafe.Offsetof(u.Name)
	var name *string = (*string)(unsafe.Pointer(up))
	fmt.Println(*name)
}

2. package io

实现了io.Reader或io.Writer接口的类型(导出的类型):

  • os.File 同时实现了io.Reader和io.Writer
  • strings.Reader 实现了io.Reader
  • bufio.Reader/Writer 分别实现了io.Reader和io.Writer
  • bytes.Buffer 同时实现了io.Reader和io.Writer
  • bytes.Reader 实现了io.Reader
  • compress/gzip.Reader/Writer 分别实现了io.Reader和io.Writer
  • crypto/cipher.StreamReader/StreamWriter 分别实现了io.Reader和io.Writer
  • crypto/tls.Conn 同时实现了io.Reader和io.Writer
  • encoding/csv.Reader/Writer 分别实现了io.Reader和io.Writer
  • mime/multipart.Part 实现了io.Reader
以上类型中,常用的类型有:os.File、strings.Reader、bufio.Reader/Writer、bytes.Buffer、bytes.Reader

WriteAt()方法简单示例:

file,err := os.Create("writeAt.txt")
if err != nil {
    panic(err)
}
defer file.Close()
file.WriteString("Golang中文社区——这里是多余的")
n,err := file.WriteAt([]byte("Go语言学习园地"),24)
if err != nil {
    panic(err)
}
fmt.Println(n)

file.WriteString("Golang中文社区——这里是多余的")文件中写入Golang中文社区——这里是多余的;之后file.WriteAt([]byte("Go语言学习园地"),24)文件流的offset=24处写入Go语言学习园地(会覆盖该位置的内容)。

ReadFrom()方法简单示例:

file,err := os.Open("writeAt.txt")
if err != nil {
    panic(err)
}
defer file.Close()
writer := bufio.NewWriter(os.Stdout)
writer.ReadFrom(file)
writer.Flush()
WriteTo()方法示例:
reader := bytes.NewReader([]byte("Go语言学习园地"))
reader.WriteTo(os.Stdout)
Seek()方法示例:
reader := strings.NewReader("Go语言学习园地")
reader.Seek(-6,os.SEEK_END)
r,_,_ := reader.ReadRune()
fmt.Printf("%c\n",r)

在标准库中,有如下类型实现了io.ByteReader或io.ByteWriter:

  • bufio.Reader/Writer 分别实现了io.ByteReader和io.ByteWriter
  • bytes.Buffer 同时实现了io.ByteReader和io.ByteWriter
  • bytes.Reader 实现了io.ByteReader
  • strings.Reader 实现了io.ByteReader

接下来的示例中,我们通过bytes.Buffer来一次读取或写入一个字节(主要代码):

var ch byte
fmt.Scanf("%c\n",&ch)

buffer := new(bytes.Buffer)
err := buffer.WriteByte(ch)
if err == nil {
    fmt.Println("写入一个字节成功!准备读取该字节……")
    newCh,_ := buffer.ReadByte()
    fmt.Printf("读取的字节:%c\n",newCh)
} else {
    fmt.Println("写入错误")
}
Pipe()方法和PipeReader、PipeWriter结构的示例:
func main() {
    Pipe()
}

func Pipe() {
    pipeReader,pipeWriter := io.Pipe()
    go PipeWrite(pipeWriter)
    go PipeRead(pipeReader)
    time.Sleep(1e7)
}

func PipeWrite(pipeWriter *io.PipeWriter) {
    var (
        i   = 0
        err error
        n int
    )
    data := []byte("Go语言学习园地")
    for _,err = pipeWriter.Write(data); err == nil; n,err = pipeWriter.Write(data) {
        i++
        if i == 3 {
            pipeWriter.CloseWithError(errors.New("输出3次后结束"))
        }
    }
    fmt.Println("close 后输出的字节数:",n," error:",err)
}

func PipeRead(pipeReader *io.PipeReader) {
    var (
        err error
        n   int
    )
    data := make([]byte,1024)
    for n,err = pipeReader.Read(data); err == nil; n,err = pipeReader.Read(data) {
        fmt.Printf("%s\n",data[:n])
    }
    fmt.Println("writer 端 closewitherror后:",err)
}
Copy()方法示例:
package main

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

func main() {
    io.Copy(os.Stdout,strings.NewReader("Go语言学习园地"))
    io.Copy(os.Stdout,os.Stdin)
    fmt.Println("Got EOF -- bye")
}

3. 程序结构

假设有一个服务的大致实现是这样的:

type Service struct {
wg sync.WaitGroup
}
func (p *Service) Start() {
p.wg.Add(1)
go p.run()
}
func (p *Service) Close() {
// ...
p.wg.Wait()
}
func (p *Service) run() {
defer p.wg.Done()
// ....
}
其中run()是服务的主要逻辑实现。这样写法的问题是wg的Add和Done函数调用分离了,容易导致在以后的代码维护中不小心就导致Add和Done的调用不匹配从而Wait执行的结果不符合预期。从代码组织的方式上来说,应该把功能相关的代码尽可能地放在一起。

type Service struct {
wg sync.WaitGroup
}
func (p *Service) Start() {
p.wg.Add(1)
go func() {
p.run()
p.wg.Done()
}()
}
func (p *Service) Close() {
// ...
p.wg.Wait()
}
func (p *Service) run() {
// ....
}
这个其实也算常识了,写过些代码的人可能都知道。但是在实际的代码中,我们往往还会发现类似这样的一些不好的写法。因此,对于这些惯用法(或者升级代码规范),我觉得更重要的不是了解知道,而是执行和遵守。

相关文章

程序目录结构 简单实现,用户登录后返回一个jwt的token,下次请求带上token请求用户信息接口并返回信息...
本篇博客的主要内容是用go写一个简单的Proof-of-Work共识机制,不涉及到网络通信环节,只是一个本地的简...
简介 默克尔树(MerkleTree)是一种典型的二叉树结构,其主要特点为: 最下面的叶节点包含存储数据或其...
接下来学习并发编程, 并发编程是go语言最有特色的地方, go对并发编程是原生支持. goroutine是go中最近本...
先普及一下, 什么是广度优先搜索 广度优先搜索类似于树的层次遍历。从图中的某一顶点出发,遍历每一个顶...
第一天: 接口的定义和实现 第二天: 一. go语言是面向接口编程. 在学习继承的时候说过, go语言只有封装,...