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

Go小技巧易错点100例(二十七)

本期分享

1. Go语言中的Scan函数

2. debug.Stack()打印堆栈信息

3. Go条件编译


正文

Go语言中的Scan函数

在Go语言中,Scan函数是一个强大的工具,它主要用于从输入源(如标准输入、文件或网络连接)读取数据,并将其解析为指定的变量类型。

fmt.Scan

fmt.Scan是最基本的扫描函数,它从标准输入(通常是键盘)读取空格分隔的值,并将它们依次赋值给提供的变量。需要注意的是,fmt.Scan会自动忽略前导和尾随的空白字符(如空格、换行符等)。

package mainimport ("fmt"
)func main() {var a intvar b stringfmt.Print("Enter an integer and a string separated by space: ")fmt.Scan(&a, &b)fmt.Println("You entered:", a, b)
}

在这个例子中,用户需要输入一个整数和一个字符串,它们之间用空格分隔。fmt.Scan会读取这些输入,并将它们分别赋值给变量ab

fmt.Scanf

fmt.Scanf提供了更灵活的输入格式控制。它允许你指定一个格式字符串,该字符串定义了输入数据的格式。Scanf会根据这个格式字符串解析输入。

package mainimport ("fmt"
)func main() {var name stringvar age intfmt.Print("Enter your name and age (e.g., Alice 30): ")fmt.Scanf("%s %d", &name, &age)fmt.Println("Hello,", name, "you are", age, "years old.")
}

在这个例子中,%s表示一个字符串,%d表示一个整数。用户需要按照指定的格式输入数据,fmt.Scanf会根据格式字符串解析并赋值。

fmt.Scanln

fmt.Scanlnfmt.Scan类似,但它会在遇到换行符时停止读取。这意味着它更适合于逐行读取输入。

package mainimport ("fmt"
)func main() {var a intvar b stringfmt.Print("Enter an integer and a string on the same line: ")fmt.Scanln(&a, &b)fmt.Println("You entered:", a, b)
}

在这个例子中,用户需要在同一行输入一个整数和一个字符串,fmt.Scanln会在读取到换行符时停止,并将输入的数据赋值给变量ab

bufio.ScannerScan方法

bufio.Scanner提供了一个更高级、更灵活的扫描机制,它可以从一个io.Reader(如文件、网络连接等)读取数据。ScannerScan方法会读取数据直到遇到分隔符(默认为换行符\n),然后你可以使用Text方法获取读取的字符串。

package mainimport ("bufio""fmt""os""strings"
)func main() {scanner := bufio.NewScanner(os.Stdin)scanner.Split(bufio.ScanWords) // 设置分隔符为空格fmt.Println("Enter some words (type 'exit' to quit):")for scanner.Scan() {word := scanner.Text()if strings.ToLower(word) == "exit" {break}fmt.Println("You entered:", word)}if err := scanner.Err(); err != nil {fmt.Println("Error reading input:", err)}
}

在这个例子中,bufio.Scanner被用来逐词读取用户输入。通过设置分隔符为空格(bufio.ScanWords),Scanner会在每个空格处分割输入。用户可以通过输入exit来退出循环。

debug.Stack()打印堆栈信息

在Go语言中,debug.Stack() 函数是 runtime/debug 包提供的一个实用工具,用于在程序运行时生成并打印当前goroutine的堆栈跟踪信息。这个函数在调试和错误处理时非常有用。

使用场景

调试:当你试图理解程序中的某个问题或异常行为时,查看堆栈跟踪可以帮助你定位问题发生的上下文。

错误处理:在捕获到panic或严重错误时,打印堆栈跟踪可以提供关于错误发生时的调用栈的详细信息,这有助于后续的问题分析和修复。

函数签名
func Stack() []byte

Stack 函数不接受任何参数,并返回一个 []byte 类型的值,这个值包含了当前goroutine的堆栈跟踪信息。通常,你会将这个返回值直接输出到标准错误输出(os.Stderr)或日志文件中,以便查看。

示例代码
package mainimport ("fmt""runtime/debug"
)func main() {defer func() {if r := recover(); r != nil {fmt.Println("Recovered from panic:", r)fmt.Println("Stack trace:")debug.PrintStack() // 或者使用 debug.Stack() 并将结果输出}}()// 触发一个panic作为示例panic("something went wrong")// 这行代码将不会被执行fmt.Println("This will not be printed")
}

在上面的示例中,我们使用 deferrecover 来捕获panic,并在捕获到panic时打印堆栈跟踪。注意,这里我们使用了 debug.PrintStack() 而不是 debug.Stack(),因为 PrintStack() 直接将堆栈跟踪输出到标准错误输出,而 Stack() 返回堆栈跟踪的字节切片,需要自己处理这个返回值(比如,将其写入日志文件)。

如果想要使用 debug.Stack() 并手动处理堆栈跟踪信息,你可以这样做:

stack := debug.Stack()
// 将 stack 输出到日志系统、文件或其他目的地
fmt.Fprint(os.Stderr, string(stack))
注意事项

1)debug.Stack() 捕获的是调用它时的堆栈跟踪,因此如果你在延迟函数中调用它(如上面的示例所示),它将捕获到触发panic时的堆栈跟踪。

**2)**堆栈跟踪信息可能包含敏感信息,因此在生产环境中使用时需要谨慎处理。

**3)**堆栈跟踪信息对于理解程序的执行流程非常有帮助,但在性能敏感的场景中频繁调用 debug.Stack() 可能会对性能产生负面影响。

Go条件编译

在Go语言中,条件编译是一种根据编译时的条件来选择性地编译代码片段的机制。这种机制允许开发者为不同的平台、操作系统或构建配置编写特定的代码,而无需修改代码逻辑或创建多个代码库。Go语言的条件编译主要通过构建标签(build tags)和文件后缀(如 _linux.go, _windows.go)来实现。

构建标签(Build Tags)

构建标签是注释中的特殊指令,用于指示go buildgo test命令在哪些条件下应该包含或排除特定的文件。构建标签位于文件顶部的注释中,并且以// +build开头,后面跟着一个或多个以空格分隔的标签。

例如,如果有一个只在Linux系统上编译的文件,我们可以这样标记它:

// +build linuxpackage mainimport "fmt"func main() {fmt.Println("This is a Linux-specific build.")
}

当你运行go buildgo run命令时,Go工具链会检查每个文件的构建标签,并根据当前的环境(如操作系统、GOOS和GOARCH环境变量的值)来决定是否包含该文件。

我们也可以使用否定标签来排除某些构建环境。例如,如果想要排除Windows平台,可以这样写:

// +build !windows

还可以组合多个标签,使用逗号分隔它们,表示“与”的关系(所有标签都必须匹配),或者使用空格分隔它们(在某些上下文中,这表示“或”的关系,但在构建标签中通常不这么用,因为构建标签不支持直接的“或”逻辑)。

文件后缀

除了构建标签外,Go还允许通过为文件指定特定的后缀来实现条件编译。这些后缀通常是操作系统或架构的名称,如_linux.go_windows.go_amd64.go等。当有一个针对特定平台或架构的文件时,可以通过添加相应的后缀来命名它。

例如,如果有一个只在Windows上使用的函数,就可以将它放在一个名为something_windows.go的文件中。Go工具链会根据当前的目标操作系统来选择性地编译这个文件。

使用条件编译的注意事项

1)条件编译应该谨慎使用,因为它会使代码库变得更加复杂,并增加维护成本。

2)在使用构建标签时,要确保它们正确无误,并且不会意外地排除或包含错误的文件。

3)条件编译通常用于处理与平台相关的差异,如系统调用、文件路径格式等。对于跨平台的通用逻辑,应该避免使用条件编译。

4)在编写条件编译的代码时,要考虑到未来可能的扩展性和兼容性。例如,如果你为某个新平台添加了特定的代码,要确保当该平台不再受支持时,可以轻松地移除这些代码。

本节完成~

http://www.xdnf.cn/news/265195.html

相关文章:

  • 数据分析与可视化实战:从鸢尾花到乳腺癌数据集
  • 数据库基础-库,表的操作
  • 人工智能——层次聚类算法
  • JVM 一文详解
  • 普通IT的股票交易成长史--20250502 突破(2)
  • 【Net】DPDK
  • Vscode/Code-Server 安装中文包——CI/CD
  • PCIe | TLP 报头 / 包格式 / 地址转换 / 配置空间 / 命令寄存器 / 配置类型
  • JMM 与 JVM 运行时数据区有什么区别和联系?
  • 数字化转型进阶:26页华为数字化转型实践分享【附全文阅读】
  • 传奇各职业/战士/法师/道士/头盔爆率及出处产出地/圣战/法神/天尊/祈祷/斗笠/精灵
  • 面向对象编程简介
  • list的两种设计
  • MySQL 比较运算符详解
  • 穿越数据森林与网络迷宫:树与图上动态规划实战指南
  • 深拷贝与浅拷贝的核心区别
  • 【unity游戏开发——Animator动画】Animation动画资源节约、优化、编辑修改小技巧
  • 人工智能:如何快速筛选出excel中某列存在跳号的单元格位置?
  • Manus联合创始人:公司产品基于Claude和阿里千问大模型开发
  • Java开发经验——ali编码规范经验总结
  • java面向对象编程【高级篇】之特殊类
  • 【Java多线程】计时器Timer/ScheduledExecutorService的使用
  • mysql主从复制搭建,并基于‌Keepalived + VIP实现高可用
  • MARM:推荐系统中的记忆增强突破
  • C++ - 数据容器之 forward_list(创建与初始化、元素访问、容量判断、元素遍历、添加元素、删除元素)
  • Python爬虫实战:获取企信网指定公司基本工商数据并分析,为客户选择公司做参考
  • 封装pinia并引入pinia持久化工具(pinia-plugin-persistedstate)
  • HarmonyOS NEXT——DevEco Studio的使用(还没写完)
  • 如何基于HAL库进行STM32开发
  • 华为云Flexus+DeepSeek征文|DeepSeek-V3商用服务开通教程