完成一个base64命令行工具,主要为了编写每行多少个字符就换行的逻辑

jan-bar · · 575 次点击 · 开始浏览    置顶

当我们使用base64时可以通过`-w`指定每行多少个字符,因此我想实现这个功能,核心方法如下: ```go package iox import ( "io" ) type wrapWriter struct { w io.Writer cur int // 记录当前行已经写入数量 wrap int // 每行限制长度 write []byte // 每行结尾追加数据,一般都是换行符 } func NewWrapWriter(w io.Writer, wrap int, write []byte) io.Writer { return &wrapWriter{w: w, wrap: wrap, write: write} } func (w *wrapWriter) writeLine(b []byte) (n int, err error) { for tn, lb := 0, len(b); n < lb && err == nil; { tn, err = w.w.Write(b[n:]) n += tn // 确保b中字节全部写入成功 } if err == nil { _, err = w.w.Write(w.write) } return } func (w *wrapWriter) Write(b []byte) (n int, err error) { var nn int if now := w.wrap - w.cur; len(b) > now { nn, err = w.writeLine(b[:now]) if n += nn; err != nil { return } b, w.cur = b[now:], 0 for len(b) > w.wrap { nn, err = w.writeLine(b[:w.wrap]) if n += nn; err != nil { return } b = b[w.wrap:] } } nn, err = w.w.Write(b) if n += nn; err != nil { return } w.cur += len(b) return } ``` 用于测试的`test.go`文件: ```go package iox import ( rand2 "crypto/rand" "encoding/base64" "math/rand" "os" "testing" "time" ) // go test -v -run TestWrapWriter func TestWrapWriter(t *testing.T) { lw := base64.NewEncoder(base64.StdEncoding, NewWrapWriter(os.Stdout, 76, []byte{'\n'})) buf := make([]byte, 512) rand.Seed(time.Now().UnixNano()) for { n := rand.Intn(80) _, _ = rand2.Read(buf[:n]) // 随机写入长度数据,确保输出在wrap长度时追加write数据即可 // 一般用来追加换行符 _, err := lw.Write(buf[:n]) if err != nil { t.Fatal(err) } time.Sleep(time.Second) } } ``` `base64`命令行工具: ```go package main import ( "encoding/base64" "flag" "io" "os" "interesting/tools/iox" ) func main() { dec := flag.Bool("d", false, "decode data") ignore := flag.Bool("i", false, "when decoding, ignore non-alphabet characters") wrap := flag.Int("w", 76, "wrap encoded lines after COLS character (default 76).\n Use 0 to disable line wrapping") srcFile := flag.String("f", "-", "input file,\"-\" is stdin") dstFile := flag.String("o", "-", "output file,\"-\" is stdout") flag.Parse() var br io.Reader if *srcFile != "-" { fr, err := os.Open(*srcFile) if err != nil { panic(err) } //goland:noinspection GoUnhandledErrorResult defer fr.Close() br = fr } else { br = os.Stdin // 其他情况都从标准输入读取 } var bo io.Writer if *dstFile != "-" { fw, err := os.Create(*dstFile) if err != nil { panic(err) } //goland:noinspection GoUnhandledErrorResult defer fw.Close() bo = fw } else { bo = os.Stdout // 其他情况都输出到标准输出 } if *dec { if err := decode(br, bo, *ignore); err != nil { panic(err) } } else { if err := encode(br, bo, *wrap); err != nil { panic(err) } } } type ignoreRead struct { r io.Reader mustMap map[byte]struct{} } func newIgnoreRead(r io.Reader, ig []byte) io.Reader { igr := &ignoreRead{ r: r, mustMap: make(map[byte]struct{}, len(ig)), } for _, b := range ig { igr.mustMap[b] = struct{}{} } return igr } func (i *ignoreRead) Read(b []byte) (n int, err error) { n, err = i.r.Read(b) if err != nil { return } b = b[:n] n = 0 for _, v := range b { if _, ok := i.mustMap[v]; ok { b[n] = v // 只保留必须的字符 n++ } } return } func decode(r io.Reader, w io.Writer, ignore bool) error { if ignore { //goland:noinspection SpellCheckingInspection,忽略非base64以外的字符,拼接(base64.encodeStd + base64.StdPadding) igb := []byte("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=") r = newIgnoreRead(r, igb) } _, err := io.Copy(w, base64.NewDecoder(base64.StdEncoding, r)) return err } func encode(r io.Reader, w io.Writer, wrap int) (err error) { if wrap > 0 { newLine := []byte{'\n'} w = iox.NewWrapWriter(w, wrap, newLine) defer func() { if err == nil { _, err = w.Write(newLine) } }() } bw := base64.NewEncoder(base64.StdEncoding, w) if _, err = io.Copy(bw, r); err != nil { return err } return bw.Close() } ``` 具体项目可以查看: [github](https://github.com/jan-bar/interesting/blob/master/tools/iox/examples/base64.go)

有疑问加站长微信联系(非本文作者)

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

575 次点击  
加入收藏 微博
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传