当前位置: 首页 > news >正文

【Go】2、Go语言实战

前言

        本系列文章参考自稀土掘金上的 【字节内部课】公开课,做自我学习总结整理。


前置知识

        本篇将涉及到一些在命令行的输入输出操作,之前我们已经学习了fmt包用于输出内容,下面将介绍bufio包用于读入数据。

bufio包简介

        bufio 是 Go 标准库中一个强大的带缓冲 I/O 包,它通过提供缓冲机制来优化 I/O 操作性能,特别适合处理大量小数据块的读写场景。

        bufio包主要由三个组件构成:

  • Reader - 带缓冲的读取器

  • Writer - 带缓冲的写入器

  • Scanner - 高级扫描器

      下面将分别对三个组件中的关键方法进行解读。

    Reader

            使用场景:

    • 需要精确控制读取过程时

    • 处理二进制数据或特定分隔符

    • 需要预读(Peek)功能时

    | 方法签名                                                                 | 描述                     | 返回值          | 应用场景                 |
    |-------------------------------------------------------------------------|--------------------------|-----------------|--------------------------|
    | `func (b *Reader) Read(p []byte) (n int, err error)`                    | 读取数据到字节切片       | 字节数, 错误    | 读取二进制数据           |
    | `func (b *Reader) ReadByte() (byte, error)`                             | 读取单个字节             | 字节值, 错误    | 处理二进制格式           |
    | `func (b *Reader) ReadRune() (r rune, size int, err error)`             | 读取单个 Unicode 字符    | 字符, 字节数, 错误 | 处理多字节编码文本       |
    | `func (b *Reader) ReadLine() (line []byte, isPrefix bool, err error)`   | 读取一行文本             | 行内容, 是否截断, 错误 | 处理文本文件             |
    | `func (b *Reader) ReadString(delim byte) (string, error)`               | 读取直到分隔符           | 字符串, 错误    | 按分隔符读取内容         |
    | `func (b *Reader) ReadBytes(delim byte) ([]byte, error)`                | 读取字节直到分隔符       | 字节切片, 错误  | 需要原始字节数据         |
    | `func (b *Reader) Peek(n int) ([]byte, error)`                          | 预读而不移动指针         | 字节切片, 错误  | 查看后续内容             |
    | `func (b *Reader) Buffered() int`                                       | 获取缓冲区的字节数       | 字节数          | 检查缓冲区状态           |
    | `func (b *Reader) Reset(r io.Reader)`                                   | 重置 Reader 关联的源     | 无              | 重用 Reader 对象         |

    Writer

            使用场景:

    • 频繁写入小量数据

    • 需要高性能文件写入

    • 格式化输出到控制台或文件

    | 方法签名                                                                 | 描述                     | 返回值          | 应用场景                 |
    |-------------------------------------------------------------------------|--------------------------|-----------------|--------------------------|
    | `func (b *Writer) Write(p []byte) (nn int, err error)`                  | 写入字节切片             | 写入字节数, 错误 | 写入二进制数据           |
    | `func (b *Writer) WriteByte(c byte) error`                              | 写入单个字节             | 错误            | 写入特定字节值           |
    | `func (b *Writer) WriteRune(r rune) (size int, err error)`              | 写入 Unicode 字符        | 写入字节数, 错误 | 处理多语言文本           |
    | `func (b *Writer) WriteString(s string) (int, error)`                   | 写入字符串               | 写入字节数, 错误 | 写入文本内容             |
    | `func (b *Writer) Flush() error`                                        | 刷新缓冲区到底层         | 错误            | 确保数据写入完成         |
    | `func (b *Writer) Available() int`                                      | 获取剩余缓冲区大小       | 字节数          | 检查缓冲区容量           |
    | `func (b *Writer) Buffered() int`                                       | 获取已缓冲字节数         | 字节数          | 检查待刷新数据量         |
    | `func (b *Writer) Reset(w io.Writer)`                                   | 重置 Writer 关联目标     | 无              | 重用 Writer 对象         |

    Scanner

            使用场景:

    • 按行处理文本文件

    • 解析结构化文本数据

    • 需要简单分割输入内容时

    | 方法签名                                                                 | 描述                     | 返回值          | 应用场景                 |
    |-------------------------------------------------------------------------|--------------------------|-----------------|--------------------------|
    | `func (s *Scanner) Scan() bool`                                         | 扫描下一个 token         | 是否扫描成功    | 迭代处理输入             |
    | `func (s *Scanner) Text() string`                                       | 获取扫描到的文本         | 字符串          | 获取当前 token 内容      |
    | `func (s *Scanner) Bytes() []byte`                                      | 获取扫描到的字节         | 字节切片        | 需要原始字节数据         |
    | `func (s *Scanner) Err() error`                                         | 获取扫描错误             | 错误            | 检查扫描过程错误         |
    | `func (s *Scanner) Split(split SplitFunc)`                              | 设置分割函数             | 无              | 自定义扫描逻辑           |
    | `func (s *Scanner) Buffer(buf []byte, max int)`                         | 设置缓冲区               | 无              | 处理大 token             |

    案例一、猜数游戏

    概述

            系统在一定范围内随机一个数据,让用户进行猜数,统计用户猜测次数,直到用户猜到数字结束程序。

    设计思路

    1. 生成一个随机数,用于让用户猜测。

    2. 设置一个死循环,用于持续读取用户数据。

    3. 将用户数据进行解析判断,看看是否等于目标值,并给出一定提示。

    代码

            写了两种读入方式,分别用Reader进行读入和用Scanner进行读入。

    package mainimport ("bufio""fmt""math/rand""os""strconv""time"
    )func main() {maxNum := 100rand.Seed(time.Now().UnixNano())secretNumber := rand.Intn(maxNum)//reader := bufio.NewReader(os.Stdin)sc := bufio.NewScanner(os.Stdin)fmt.Println("---猜数游戏已就绪----")cnt := 0for {cnt++fmt.Printf("第%v次猜数,你的选择是:\n", cnt)//a, err := reader.ReadString('\n')//if err != nil {//    fmt.Println("不可预知的错误")//    cnt--//    continue//}//a = strings.TrimSpace(a)a := sc.Scan()if sc.Err() != nil {fmt.Println("读取输入错误:", sc.Err())cnt--continue}guess, err := strconv.Atoi(sc.Text())if err != nil {fmt.Printf("'%v' 不是有效数字,请重新输入\n", a)cnt--continue}if guess == secretNumber {fmt.Printf("恭喜你,第%v次猜对了!\n", cnt)break} else if guess > secretNumber {fmt.Println("没猜对,猜大了")} else {fmt.Println("没猜对,猜小了")}}fmt.Println("Game Over")
    }
    

            需要注意的是:

    • 在测试的时候,如果编译器用到是vscode的话,不能使用那个自带的调试控制器,需要用终端进行测试。

    课后作业

            修改猜谜游戏中的最终代码,使用fmt.Scanf来简化代码实现

            就是换了一种读入的方式,感觉Go的读入方式还是比较玄学的,后续会开个新的文章将各种读取方式进行详解。

    package mainimport ("fmt""math/rand""time"
    )func main() {maxNum := 100rand.Seed(time.Now().UnixNano())secretNumber := rand.Intn(maxNum)fmt.Println("---猜数游戏已就绪----")cnt := 0for {cnt++fmt.Printf("第%v次猜数,你的选择是:\n", cnt)var guess int_, err := fmt.Scanf("%d", &guess)if err != nil {fmt.Println("输入无效!请输入数字")var discard stringfmt.Scanln(&discard)continue}fmt.Scanln()if guess == secretNumber {fmt.Printf("恭喜你,第%v次猜对了!\n", cnt)break} else if guess > secretNumber {fmt.Println("没猜对,猜大了")} else {fmt.Println("没猜对,猜小了")}}fmt.Println("Game Over")
    }
    

    案例二、在线词典

    概述

            设计一个在线词典用于翻译。

    设计思路

    1. 通过抓包的方式,抓取到一个在线翻译网站的API。

    2. 因为自己编写请求头比较麻烦,我们使用 https://curlconverter.com/go/这个网站可以将我们复制的 cURL bash直接转化成Go的代码。

    3. 然后查看请求的响应,我们需要对响应和请求进行一个封装,可以通过https://tool.lvtao.net/json2go网站对请求快速转化为Go的结构体。

    4. 然后编写基础代码进行query请求即可。

    代码

    package mainimport ("bytes""encoding/json""fmt""io/ioutil""log""net/http""os"
    )type DictRequest struct {TransType string `json:"trans_type"`Source    string `json:"source"`UserID    string `json:"user_id"`
    }type DictResponse struct {Rc   int `json:"rc"`Wiki struct {KnownInLaguages int `json:"known_in_laguages"`Description     struct {Source string      `json:"source"`Target interface{} `json:"target"`} `json:"description"`ID   string `json:"id"`Item struct {Source string `json:"source"`Target string `json:"target"`} `json:"item"`ImageURL  string `json:"image_url"`IsSubject string `json:"is_subject"`Sitelink  string `json:"sitelink"`} `json:"wiki"`Dictionary struct {Prons struct {EnUs string `json:"en-us"`En   string `json:"en"`} `json:"prons"`Explanations []string      `json:"explanations"`Synonym      []string      `json:"synonym"`Antonym      []string      `json:"antonym"`WqxExample   [][]string    `json:"wqx_example"`Entry        string        `json:"entry"`Type         string        `json:"type"`Related      []interface{} `json:"related"`Source       string        `json:"source"`} `json:"dictionary"`
    }func query(word string) {client := &http.Client{}request := DictRequest{TransType: "en2zh", Source: word}buf, err := json.Marshal(request)if err != nil {log.Fatal(err)}var data = bytes.NewReader(buf)req, err := http.NewRequest("POST", "https://api.interpreter.caiyunai.com/v1/dict", data)if err != nil {log.Fatal(err)}req.Header.Set("Connection", "keep-alive")req.Header.Set("DNT", "1")req.Header.Set("os-version", "")req.Header.Set("sec-ch-ua-mobile", "?0")req.Header.Set("User-Agent", "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36")req.Header.Set("app-name", "xy")req.Header.Set("Content-Type", "application/json;charset=UTF-8")req.Header.Set("Accept", "application/json, text/plain, */*")req.Header.Set("device-id", "")req.Header.Set("os-type", "web")req.Header.Set("X-Authorization", "token:qgemv4jr1y38jyq6vhvi")req.Header.Set("Origin", "https://fanyi.caiyunapp.com")req.Header.Set("Sec-Fetch-Site", "cross-site")req.Header.Set("Sec-Fetch-Mode", "cors")req.Header.Set("Sec-Fetch-Dest", "empty")req.Header.Set("Referer", "https://fanyi.caiyunapp.com/")req.Header.Set("Accept-Language", "zh-CN,zh;q=0.9")req.Header.Set("Cookie", "_ym_uid=16456948721020430059; _ym_d=1645694872")resp, err := client.Do(req)if err != nil {log.Fatal(err)}defer resp.Body.Close()bodyText, err := ioutil.ReadAll(resp.Body)if err != nil {log.Fatal(err)}if resp.StatusCode != 200 {log.Fatal("bad StatusCode:", resp.StatusCode, "body", string(bodyText))}var dictResponse DictResponseerr = json.Unmarshal(bodyText, &dictResponse)if err != nil {log.Fatal(err)}fmt.Println(word, "UK:", dictResponse.Dictionary.Prons.En, "US:", dictResponse.Dictionary.Prons.EnUs)for _, item := range dictResponse.Dictionary.Explanations {fmt.Println(item)}
    }func main() {if len(os.Args) != 2 {fmt.Fprintf(os.Stderr, `usage: simpleDict WORD
    example: simpleDict hello`)os.Exit(1)}word := os.Args[1]query(word)
    }

    相关文章:

  • Rust 学习笔记:自定义构建和发布配置
  • AUTOSAR图解==>AUTOSAR_SWS_DIODriver
  • 【渲染】拆解三国:谋定天下场景渲染技术
  • linux 后记
  • GCC 下载安装
  • DAY18C语言笔记
  • day16 leetcode-hot100-31(链表10)
  • 【笔记】MSYS2 安装 Python 构建依赖记录Cython + Ninja + Meson + meson-python
  • 焦虑而烦躁的上午
  • LeetCode 1497. 检查数组对是否可以被 k 整除
  • nt!MiDispatchFault函数分析之nt!MiCompleteProtoPteFault函数的作用
  • InfluxDB 高级分析实战:预测、技术指标与异常检测全指南
  • 70.新增用户内容复制功能
  • 1-2 Dart SDK 安装
  • rl_sar功能包详解
  • PTA-根据已有类Worker,使用LinkedList编写一个WorkerList类,实现计算所有工人总工资的功能。
  • Python+MongoDb使用手册(精简)
  • Baklib加速企业AI数据治理实践
  • Flickr30k Entities短语定位评测指南
  • 基于大模型预测的寻常型天疱疮诊疗方案研究报告
  • 建设一个怎样的自己的网站/产品运营方案
  • 网站制作软件都是什么软件/磁力王
  • 服务区里可以做多少个网站/广东seo推广费用
  • python下载安装教程/优化网站的意思
  • 国外建筑设计网站/北京网站建设公司
  • 做淘客网站 名字/苏州优化排名seo