Go-06| 豆包MarsCode AI 刷题”

63 阅读16分钟

这段笔记展示了如何在Go语言中开发一个简易词典程序,并通过逐版本演进来增强程序的功能性和用户交互性。

  • V1特点:使用标准输入输出和随机数来实现猜数字游戏的基本框架。

  • V2特点:通过HTTP请求到在线API,实现词典查询功能,使用JSON进行数据序列化和请求设置。

  • V3特点:引入复杂的JSON解析,通过定义详尽的Go结构体来处理API响应,优化数据处理和展示。

  • V4特点:整合命令行用户输入,动态查询单词,展示查询结果的详细信息如发音和释义。

每个版本的改进都使得程序不仅提供了基本的查询功能,还增强了与外部API的交互能力,改善了数据处理和用户界面的互动性。最终版本提供实用的查询功能、结构化输出和优化的用户体验。请根据目录快速检索和入门。

Go语言的实战案例

01-simpledict V1

package main

import (
	"bufio"
	"fmt"
	"math/rand"
	"os"
	"strconv"
	"strings"
	"time"
)

func main() {
	maxNum := 100
	rand.Seed(time.Now().UnixNano())
	secretNumber := rand.Intn(maxNum)
	// fmt.Println("The secret number is ", secretNumber)

	fmt.Println("Please input your guess")
	reader := bufio.NewReader(os.Stdin)
	for {
		input, err := reader.ReadString('\n')
		if err != nil {
			fmt.Println("An error occured while reading input. Please try again", err)
			continue
		}
		input = strings.Trim(input, "\r\n")

		guess, err := strconv.Atoi(input)
		if err != nil {
			fmt.Println("Invalid input. Please enter an integer value")
			continue
		}
		fmt.Println("You guess is", guess)
		if guess > secretNumber {
			fmt.Println("Your guess is bigger than the secret number. Please try again")
		} else if guess < secretNumber {
			fmt.Println("Your guess is smaller than the secret number. Please try again")
		} else {
			fmt.Println("Correct, you Legend!")
			break
		}
	}
}

SimpleDict V1 核心知识点:基础输入输出和随机数交互模板

核心知识点:

  1. 基础交互模板的重用

    • 这个版本的代码实际上是基于之前“猜数字游戏”的代码模板,但它即将被改造成“简单词典”工具。这表明很多基础的交互逻辑(如用户输入、数据验证、输出反馈)可以重用于不同的应用场景,比如游戏或工具。
  2. 用户输入的读取和处理

    • 使用 bufio.NewReader(os.Stdin) 读取用户输入,利用 reader.ReadString('\n') 接受用户的输入内容。
    • strings.Trim(input, "\r\n") 用于去除输入中的换行符,以便对输入进行后续的处理。
  3. 数据校验和反馈

    • 尝试将用户输入转换为整数,借助 strconv.Atoi(input) 实现,如果无法转换为整数,则提示用户输入有效的整数值。尽管这个逻辑在猜数字游戏中很合理,但在词典程序中可能需要处理字符串,而非只接受数字。
  4. 随机数的生成与逻辑实现

    • 目前代码仍然保留了生成随机数的逻辑(rand.Intn(maxNum)),但是这种逻辑在词典程序中并不适用。后续版本中应移除这个随机数逻辑,代之以词典相关的功能,例如查询词汇定义等。

应用场景:

  • 基础输入输出逻辑的模板:本代码展示了如何建立与用户的基本交互,这种交互模板可用于游戏、工具、配置程序等需要用户输入的应用场景。
  • 错误处理:在用户输入无效数据时,通过 continue 让用户重新输入,这是处理用户输入的一种友好方式,适合用于需要用户多次尝试的情景。

改进方向:

  • 去掉与猜数字无关的逻辑:未来版本中应移除不相关的随机数生成和猜测逻辑,改为处理与词典相关的功能,例如接受用户输入查询词汇含义。
  • 引入词典数据:下一步需要引入实际的词典数据(例如一个包含词汇和解释的映射),使得用户可以输入单词并获得其解释。
  • 实现简单的查询功能:将用户输入与词典数据匹配,如果存在该单词则返回其定义,如果不存在则提示用户找不到该单词。

未来增强:

  • 词典功能:后续版本应将“猜数字”的逻辑转换为“单词查询”的逻辑。可以通过引入词典数据库或使用简单的哈希表来实现查询。
  • 多次查询的交互:为了让用户能够方便地查询多个单词,可以使用循环,让用户在单次查询后可以继续输入其他单词,直到他们选择退出。

02-simpledict V2

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

type DictRequest struct {
	TransType string `json:"trans_type"`
	Source    string `json:"source"`
	UserID    string `json:"user_id"`
}

func main() {
	client := &http.Client{}
	request := DictRequest{TransType: "en2zh", Source: "good"}
	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)
	}
	fmt.Printf("%s\n", bodyText)
}

SimpleDict V2 核心知识点:HTTP请求与API调用实现在线词典功能

核心知识点:

  1. HTTP客户端和请求构造

    • 本版本通过 net/http 包实现了对外部API接口的调用,使得应用可以利用在线词典服务。使用 http.Client{} 创建客户端,能够发送请求和接收响应。
    • 通过 http.NewRequest("POST", url, data) 创建了一个 HTTP POST 请求,将数据发送到指定的 API 地址。
  2. 结构体的定义与JSON编码

    • 定义了 DictRequest 结构体,用于表示请求体的数据格式,包括翻译类型(TransType)、待翻译的源文本(Source)和用户ID(UserID)。
    • 使用 json.Marshal(request)DictRequest 对象序列化为 JSON 格式,便于作为请求体发送至服务器。这种方式确保了数据在发送前可以被正确编码。
  3. HTTP请求头的设置

    • 使用 req.Header.Set() 设置多个 HTTP 请求头,模拟浏览器请求,包括 User-AgentContent-TypeAccept 等。
    • 请求头信息在调用外部 API 时非常重要,特别是在模拟浏览器访问或需要身份验证时,可以使请求看起来更像一个合法客户端,从而避免被拒绝访问。
  4. API响应的处理

    • 使用 client.Do(req) 发送请求,并通过 resp.Body 获取服务器响应。利用 ioutil.ReadAll(resp.Body) 读取响应体,将其转换为字节数组并输出为字符串格式。
    • defer resp.Body.Close() 确保响应体在函数结束时被正确关闭,避免资源泄露。

应用场景:

  • 在线词典查询:通过调用在线 API 服务,实现了从英文到中文的翻译功能。适用于需要快速构建一个查询单词或翻译词汇的工具的场景。
  • HTTP API 调用:通过构造 HTTP 请求、设置请求头和处理响应,实现对外部服务的调用,适用于需要集成第三方服务的应用,如天气查询、股票数据获取等。
  • API与JSON:通过 JSON 格式发送请求体和解析响应,适合在现代 Web 应用开发中使用的常见数据格式,方便与外部系统进行交互。

改进方向:

  1. 用户交互

    • 当前版本中的待翻译词汇是硬编码的字符串 "good"。可以改进为让用户输入一个词汇,程序再进行翻译查询,使其更具交互性和实用性。
  2. 响应解析

    • 当前程序只是简单地输出响应体,没有进行进一步的解析。可以改进为将响应 JSON 解析为结构体,并输出更加清晰的信息(例如输出翻译结果)。
  3. API Key与安全性

    • 代码中包含了硬编码的授权令牌(X-Authorization),这种方式不安全,尤其在公开代码时容易泄露敏感信息。可以通过环境变量或配置文件来存储这些敏感数据,提高安全性。
  4. 错误处理与改进

    • 当前代码中,任何错误都会调用 log.Fatal() 终止程序。这种错误处理方式不太友好,可以改进为友好的提示信息,让用户了解出错原因而不是直接退出程序。

游戏体验和使用体验的提升:

  • 更好的人机交互:通过让用户输入单词并返回翻译结果,程序的互动性会更高,更像是一个实用的词典工具。
  • 解析结果展示:响应内容通过解析可以更加直观地呈现给用户,而不仅仅是打印整个 JSON 响应,提供更好的使用体验。

03-simpledict V3

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
)

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 main() {
	client := &http.Client{}
	request := DictRequest{TransType: "en2zh", Source: "good"}
	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)
	}
	var dictResponse DictResponse
	err = json.Unmarshal(bodyText, &dictResponse)
	if err != nil {
		log.Fatal(err)
	}
	fmt.Printf("%#v\n", dictResponse)
}

SimpleDict V3 核心知识点:API响应解析与结构化输出

核心知识点:

  1. 定义响应结构体 (DictResponse)

    • 本版本增加了对 API 响应的结构体定义 (DictResponse),用于将 JSON 响应转换为 Go 语言的结构体。这使得程序可以更容易地访问和处理 API 返回的数据。
    • 结构体 DictResponse 中包含了嵌套的字段(例如 DictionaryWiki),用于准确映射 API 响应中的各个部分。通过这种方式,可以避免直接操作 JSON 数据的复杂性。
  2. HTTP请求和响应处理

    • 与前一版本类似,通过 http.Clienthttp.NewRequest 创建 HTTP POST 请求,向在线词典服务发送翻译请求。
    • 利用 json.Marshal()DictRequest 序列化为 JSON 数据,进行 POST 请求的发送。
    • 使用 defer resp.Body.Close() 确保响应体在使用完成后被关闭,避免内存泄露。
  3. JSON解析 (json.Unmarshal())

    • 通过 json.Unmarshal(bodyText, &dictResponse) 将 JSON 格式的响应数据解析为 DictResponse 结构体。这样就能方便地通过结构体字段访问具体的数据。
    • 这一步将原本不太直观的 JSON 数据转换为易于理解和操作的 Go 结构体,便于后续处理和展示。
  4. 改进的数据展示

    • 输出使用 fmt.Printf("%#v\n", dictResponse) 来展示结构体的完整内容。这种展示方式便于调试,能够看到所有解析后的字段和它们的值。

应用场景:

  • API响应的解析与操作:通过定义结构体来解析复杂的 JSON 响应,适用于需要从 API 返回的数据中提取详细信息的场景,例如获取天气数据、汇率、词典查询等。
  • 复杂数据处理:通过嵌套结构体映射复杂的 JSON 结构,有助于简化处理逻辑,尤其是在对多层嵌套数据的操作中,能够显著提高代码可读性和易维护性。

改进方向:

  1. 优化输出展示

    • 当前的输出只是将结构体的内容打印出来,略显杂乱。可以改进为根据需要打印结构体中的特定字段,例如词的定义、同义词、反义词等,让输出更为简洁和有意义。
  2. 用户交互增强

    • 目前请求的源词汇(Source)仍然是硬编码的。可以通过让用户输入要查询的词汇,增强工具的实际使用价值。
  3. 错误处理的改进

    • JSON解析错误直接终止程序不太友好,可以改进为用户友好的错误提示信息,甚至可以通过重新输入来进行新的尝试。
  4. 多次查询和自动化

    • 当前程序每次只能查询一个词汇,运行一次即结束。可以引入一个循环,让用户能够多次输入不同的词汇,连续进行查询,直到用户选择退出。

用户体验的提升:

  • 更加结构化和清晰的输出:通过将 API 响应解析为结构体,可以更直观地展示信息,例如输出单词的发音、释义、例句等,用户体验会更好。
  • 更高的交互性:未来版本可以通过引入用户输入来支持不同的词汇查询,使得工具从单一用途变得更为灵活和实用,这会让用户更加愿意使用和探索这个小工具。

04-simpledict V4

package main

import (
	"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 DictResponse
	err = 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)
}

SimpleDict V4 核心知识点:用户输入和查询功能的整合

核心知识点:

  1. 用户输入作为查询词汇

    • 在本版本中,用户可以通过命令行参数输入一个单词来进行查询,增加了程序的交互性和灵活性。程序的启动命令格式为:simpleDict WORD
    • 通过 os.Args[1] 获取命令行传入的单词,这样用户可以动态输入不同的词汇,而不是依赖硬编码的词汇。
  2. 封装查询功能

    • 将词典查询逻辑封装为 query() 函数,参数为用户输入的单词。这种封装使代码结构更加清晰,增加了代码的重用性和维护性。
    • query(word) 函数负责向 API 发送请求,处理响应,并将结果打印出来。
  3. HTTP 请求的改进

    • 和前一版本相似,构造了 HTTP POST 请求,但增加了响应状态码的检查。
    • 检查 resp.StatusCode 确保 HTTP 请求的状态是 200,如果不是,则打印错误消息并终止程序。这种改进有助于更好地处理服务端响应中的异常情况。
  4. 解析并输出关键信息

    • 通过结构体 DictResponse 将返回的 JSON 数据解析后,程序能够输出详细的词典信息。
    • 程序打印单词的英式发音和美式发音 (UKUS),以及翻译解释部分(Explanations)。这样用户可以直观地看到查询单词的读音和含义。

应用场景:

  • 命令行工具:本版本实现了一个命令行词典工具的基本功能,适用于需要快速查询单词释义的场景,例如开发者在写代码时查单词,或者需要了解某个英文单词的释义。
  • 封装和可重用性:通过将查询功能封装为 query() 函数,可以很容易将该功能嵌入其他应用中,或者在后续版本中进行功能拓展和维护。

改进方向:

  1. 更友好的错误处理

    • 目前程序中,当遇到网络错误或解析错误时会直接终止程序并打印日志 (log.Fatal)。可以改进为向用户输出友好的错误提示,并允许用户重新输入单词继续查询。
  2. 多单词查询支持

    • 当前版本一次只能查询一个单词,可以通过循环或增加对多单词输入的支持,让用户在一次执行中查询多个单词,以提升用户体验。
  3. 改进输出格式

    • 增加对单词含义的分组展示,让用户可以更方便地阅读解释部分,例如分为“动词”、“名词”等部分,而不是简单地列出所有释义。
    • 增加对其他信息的展示,例如词的例句(wqx_example)、同义词(synonym)等,以帮助用户更全面地理解词汇。
  4. 授权和安全性

    • 将 API 的授权令牌 (X-Authorization) 移动到配置文件或环境变量中,这样在代码发布时不会泄露敏感信息。可以通过 os.Getenv() 来读取环境变量中的授权信息。

用户体验的提升:

  • 动态查询:用户可以通过命令行输入不同的单词进行查询,而不是硬编码查询词汇,增加了程序的实用性和交互性。
  • 更加详细的输出:本版本中增加了对单词的英美发音的显示,以及释义的展示,使用户对单词的了解更加全面和直观。结合进一步的输出改进和友好的错误处理,可以让用户更加轻松地使用这个工具。