💎一站式轻松地调用各大LLM模型接口,支持GPT4、智谱、星火、月之暗面及文生图 广告
# 1、struct的序列化 struct中的变量命名必须以大写字母开头,否则在使用以下方法序列化时候,其值不会被序列化到[]byte之中,也就是说,反序列化后,其值会丢失。 ~~~ func (b  *Block) Serialize() []byte { varresult bytes.Buffer //定义一个buffer存储序列化后的数据 //初始化一个encoder,gob是标准库的一部分 //encoder根据参数的类型来创建,这里将编码为字节数组 encoder := gob.NewEncoder(&result) err := encoder.Encode(b) //编码 if err != nil {         log.Panic(err) //如果出错,将记录log后,Panic调用,立即终止当前函数的执行     } return result.Bytes() } ~~~ # 2、VS-CODE的Debug配置 ![](https://img.kancloud.cn/4f/6e/4f6ee46317cd193ec9bc3c8ad06b212d_653x625.png) 如果程序是通过命令行参数运行的,配置文件参考如下: ~~~ { "version": "0.2.0", "configurations": [         { "name": "Launch", "type": "go", "request": "launch", "mode": "auto", "remotePath": "", "port": 5546, "program": "${fileDirname}", "env": {}, "args": ["createblockchain","-address","Ivan"]//main的执行命令:main createblockchain -address Ivan //"args": ["printchain"]//main的执行命令:main printchain         }     ] } ~~~ # 3、flag包是解析命令行参数输入的很好的工具 (1)定义CLI处理器 ~~~ typeCLIstruct{} ~~~ (2)检测命令的合法性(普通检查) ~~~ //validateArgs 校验命令,如果无效,打印使用说明 //参数是否符合命令要求,不在这里进行检查,而在执行命令前检查 func (cli \*CLI) validateArgs() { iflen(os.Args) < 2 { //所有命令至少有两个参数,第一个是程序名称,第二个是命名名称         cli.printUsage()         os.Exit(1)     } } ~~~ (3)命令行使用说明 ~~~ //printUsage 打印命令行帮助信息 func (cli \*CLI) printUsage() {     fmt.Println("Usage:")     fmt.Println("   getbalance -address ADDRESS  - 获得某个地址的余额")     fmt.Println("   createblockchain -address ADDRESS - 创建一个新的区块链并发送创始区块奖励给到address")     fmt.Println("   printchain - 打印区块链中的所有区块")     fmt.Println("   send -from FROM -to To -amount - 发送amount数量的币,从地址FROM到TO") } ~~~ (4)Run 读取命令行参数,执行相应的命令 ~~~ //使用标准库里面的 flag 包来解析命令行参数: func (cli *CLI) Run() {     cli.validateArgs() //定义名称为"getbalance"的空的flagset集合 getBalanceCmd := flag.NewFlagSet("getbalance", flag.ExitOnError) //定义名称为"createBlockchainCmd"的空的flagset集合 createBlockchainCmd := flag.NewFlagSet("createblockchain", flag.ExitOnError) //定义名称为"sendCmd"的空的flagset集合 sendCmd := flag.NewFlagSet("send", flag.ExitOnError) //定义名称为"printchain"的空的flagset集合 printChainCmd := flag.NewFlagSet("printchain", flag.ExitOnError) //String用指定的名称给getBalanceAddress 新增一个字符串flag //以指针的形式返回getBalanceAddress getBalanceAddress := getBalanceCmd.String("address", "", "获得金钱的地址") createBlockchainAddress := createBlockchainCmd.String("address", "", "接受挖出创始区块奖励的的地址") sendFrom := sendCmd.String("from", "", "钱包源地址") sendTo := sendCmd.String("to", "", "钱包目的地址") sendAmount := sendCmd.Int("amount", 0, "转移资金的数量") //os.Args包含以程序名称开始的命令行参数 switch os.Args[1] { os.Args[0]为程序名称,真正传递的参数index从1开始,一般而言Args\[1\]为命令名称 case"getbalance": //Parse调用之前,必须保证getBalanceCmd所有的flag都已经定义在其中 err := getBalanceCmd.Parse(os.Args\[2:\]) //仅解析参数,不含命令 if err != nil {             log.Panic(err)         } case"createblockchain": err := createBlockchainCmd.Parse(os.Args\[2:\]) if err != nil {             log.Panic(err)         } case"printchain": //Parse调用之前,必须保证addBlockCmd所有的flag都已经定义在其中 //根据命令设计,这里将返回nil,所以在前面没有定义接收解析后数据的flag //但printChainCmd的parsed=true err := printChainCmd.Parse(os.Args\[2:\]) //仅仅解析参数,不含命令 if err != nil {             log.Panic(err)         } case"send": err := sendCmd.Parse(os.Args\[2:\]) if err != nil {             log.Panic(err)         } default:         cli.printUsage()         os.Exit(1)     } if getBalanceCmd.Parsed() { if *getBalanceAddress == "" {             getBalanceCmd.Usage()             os.Exit(1)         }         cli.getBalance(\*getBalanceAddress)     } if createBlockchainCmd.Parsed() { if *createBlockchainAddress == "" {             createBlockchainCmd.Usage()             os.Exit(1)         }         cli.createBlockchain(\*createBlockchainAddress)     } if printChainCmd.Parsed() {         cli.printChain()     } if sendCmd.Parsed() { if *sendFrom == "" || *sendTo == "" || *sendAmount <= 0 {             sendCmd.Usage()             os.Exit(1)         }         cli.send(\*sendFrom, \*sendTo, \*sendAmount)     } } ~~~ ## 4、github与vs-code集成,同时打开多个github控制的项目 有时候,我们需要在一个vs-code界面上,同时打开多个受github控制的项目(每个项目为独立的github仓库),其做法如下: (1)在本地磁盘建立一个顶级文件夹; (2)打开VS-CODE,克隆存储库 ![](https://img.kancloud.cn/6a/6d/6a6d0914b006932cce14f482c2d501cb_901x547.png) 刚刚建立的本地顶级文件夹,输入项目github地址。 (3)VS-CODE关闭文件夹,然后打开刚建立的顶级文件夹 这时候,所有的子文件夹会自动导入进来了。 顶级文件夹下的各子文件夹对应于子项目,独立进行github更新。 平时工作,除非建立分支,一般操作三板斧:Add(+)、Commit、Push,将本地修改同步到云端。 ## 5、map类型的使用 map是go里面非常强大的类型,对于值为struct类型、数组或其他非常复杂的结构的key-value列表,非常适合使用它来进行存储处理。 ## 6、append的两种用法 ~~~ x := []int {1,2,3} y := []int {4,5,6} //注意下面这两个区别 fmt.Println(append(x,4,5,6)) fmt.Println(append(x,y...)); ~~~ 第一种用法中,第一个参数为slice,后面可以添加多个参数,这种情况一般是将一个或多个元素加入到现有的数组之中: x=append(x,4,5,6) 第二种用法,是将两个slice拼接在一起,在第二个slice的名称后面加三个点,构建一个新的数组,这时候append只支持两个参数,不支持任意个数的参数。 z:=append(x,y...) ## 7、数组的切片引用方式:复用 我们一般在构建了数组实例后,通过函数或方法返回给调用者使用,一般不会直接返回数组指针,而是以切片形式,返回给调用者。这种方式是Go里面的普遍方式。函数或方法内部也是这么使用。 ~~~ func checksum(payload []byte) []byte { firstSHA := sha256.Sum256(payload) secondSHA := sha256.Sum256(firstSHA[:])//引用firstSHA 数组的全部切片 return secondSHA[:addressChecksumLen]//返回用secondSHA的部分切片 } ~~~ ## 8、判断文件是否存在 ~~~ if_, err := os.Stat(walletFile); os.IsNotExist(err) { return err   } ~~~ ## 9、[]byte转为字符串: hex.EncodeToString(prevTX.ID)//prevTX.ID为[]byte类型 ## 10、break语句 (1)break//直接退出当前层次的循环 (2)break WORK//退出WORK标签对应的代码块,标签要求必须定义在对应的 for、switch 和 select 的代码块上 ## 11、vs-code多命令窗口运行 vs-code可以多窗口运行。 ![](https://img.kancloud.cn/59/27/59279a49d1aeae04d667c05b016fab17_2091x916.png) 每一个窗口都可以独立设置环境变量 如第一个窗口设置:set NODE_ID=3000,第二个窗口设置环境变量:set NODE_ID=3001,第三个窗口设置为set NODE_ID=3002。 在命令行窗口运行服务后,如希望暂停,通过键盘上ctrl+c组合键执行。 ## 12、go env 可以使用命令行查看 Go 开发包的环境变量配置信息,这些配置信息里可以查看到当前的 GOPATH 路径设置情况。在命令行中运行`go env`。 ## 13、gobEncode go内置的gobEncode,可以将一个stuct转换成一个[]byte数组,一个典型的用途是传输远程过程调用(RPC)的参数和结果: ~~~ data := block{nodeAddress, b.Serialize()} payload := gobEncode(data) ~~~ ~~~ func gobEncode(data interface{}) []byte { varbuff bytes.Buffer enc := gob.NewEncoder(&buff) err := enc.Encode(data) if err != nil {         log.Panic(err)     } return buff.Bytes() } ~~~ 与此对应的是## GobDecoder。 ## 14、通道chan chan 可以理解为队列,遵循先进先出的规则,用于在不同goroutine之间通信。 ``` // 声明不带缓冲的通道 ch1 := make(chan string) // 声明带10个缓冲的通道 ch2 := make(chan string, 10) // 声明只读通道 ch3 := make(<-chan string) // 声明只写通道 ch4 := make(chan<- string) ``` 注意: 不带缓冲的通道,进和出都会阻塞。 带缓冲的通道,进一次长度 +1,出一次长度 -1,如果长度等于缓冲长度时,再进就会阻塞。 ### 写入 chan ``` ch1 := make(chan string, 10) ch1 <- "a" ``` ### 读取 chan ``` val, ok := <- ch1 // 或 val := <- ch1 ``` ### 关闭 chan ``` close(chan) ``` 注意: * close 以后不能再写入,写入会出现 panic * 重复 close 会出现 panic * 只读的 chan 不能 close * close 以后还可以读取数据