【Go语言-Day 36】构建专业命令行工具:`flag` 包入门与实战

Langchain系列文章目录

01-玩转LangChain:从模型调用到Prompt模板与输出解析的完整指南
02-玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖
03-全面掌握 LangChain:从核心链条构建到动态任务分配的实战指南
04-玩转 LangChain:从文档加载到高效问答系统构建的全程实战
05-玩转 LangChain:深度评估问答系统的三种高效方法(示例生成、手动评估与LLM辅助评估)
06-从 0 到 1 掌握 LangChain Agents:自定义工具 + LLM 打造智能工作流!
07-【深度解析】从GPT-1到GPT-4:ChatGPT背后的核心原理全揭秘
08-【万字长文】MCP深度解析:打通AI与世界的“USB-C”,模型上下文协议原理、实践与未来

Python系列文章目录

PyTorch系列文章目录

机器学习系列文章目录

深度学习系列文章目录

Java系列文章目录

JavaScript系列文章目录

Python系列文章目录

Go语言系列文章目录

01-【Go语言-Day 1】扬帆起航:从零到一,精通 Go 语言环境搭建与首个程序
02-【Go语言-Day 2】代码的基石:深入解析Go变量(var, :=)与常量(const, iota)
03-【Go语言-Day 3】从零掌握 Go 基本数据类型:string, runestrconv 的实战技巧
04-【Go语言-Day 4】掌握标准 I/O:fmt 包 Print, Scan, Printf 核心用法详解
05-【Go语言-Day 5】掌握Go的运算脉络:算术、逻辑到位的全方位指南
06-【Go语言-Day 6】掌控代码流:if-else 条件判断的四种核心用法
07-【Go语言-Day 7】循环控制全解析:从 for 基础到 for-range 遍历与高级控制
08-【Go语言-Day 8】告别冗长if-else:深入解析 switch-case 的优雅之道
09-【Go语言-Day 9】指针基础:深入理解内存地址与值传递
10-【Go语言-Day 10】深入指针应用:解锁函数“引用传递”与内存分配的秘密
11-【Go语言-Day 11】深入浅出Go语言数组(Array):从基础到核心特性全解析
12-【Go语言-Day 12】解密动态数组:深入理解 Go 切片 (Slice) 的创建与核心原理
13-【Go语言-Day 13】切片操作终极指南:append、copy与内存陷阱解析
14-【Go语言-Day 14】深入解析 map:创建、增删改查与“键是否存在”的奥秘
15-【Go语言-Day 15】玩转 Go Map:从 for range 遍历到 delete 删除的终极指南
16-【Go语言-Day 16】从零掌握 Go 函数:参数、多返回值与命名返回值的妙用
17-【Go语言-Day 17】函数进阶三部曲:变参、匿名函数与闭包深度解析
18-【Go语言-Day 18】从入门到精通:defer、return 与 panic 的执行顺序全解析
19-【Go语言-Day 19】深入理解Go自定义类型:Type、Struct、嵌套与构造函数实战
20-【Go语言-Day 20】从理论到实践:Go基础知识点回顾与综合编程挑战
21-【Go语言-Day 21】从值到指针:一文搞懂 Go 方法 (Method) 的核心奥秘
22-【Go语言-Day 22】解耦与多态的基石:深入理解 Go 接口 (Interface) 的核心概念
23-【Go语言-Day 23】接口的进阶之道:空接口、类型断言与 Type Switch 详解
24-【Go语言-Day 24】从混乱到有序:Go 语言包 (Package) 管理实战指南
25-【Go语言-Day 25】从go.mod到go.sum:一文彻底搞懂Go Modules依赖管理
26-【Go语言-Day 26】深入解析error:从errors.New到errors.As的演进之路
27-【Go语言-Day 27】驾驭 Go 的异常处理:panic 与 recover 的实战指南与陷阱分析
28-【Go语言-Day 28】文本处理利器:strings 包函数全解析与实战
29-【Go语言-Day 29】从time.Now()到Ticker:Go语言time包实战指南
30-【Go语言-Day 30】深入探索Go文件读取:从os.ReadFile到bufio.Scanner的全方位指南
31-【Go语言-Day 31】精通文件写入与目录管理:osfilepath包实战指南
32-【Go语言-Day 32】从零精通 Go JSON:MarshalUnmarshal 与 Struct Tag 实战指南
33-【Go语言-Day 33】告别“能跑就行”:手把手教你用testing包写出高质量的单元测试
34-【Go语言-Day 34】告别凭感觉优化:手把手教你 Go Benchmark 性能测试
35-【Go语言-Day 35】Go 反射核心:reflect 包从入门到精通
36-【Go语言-Day 36】构建专业命令行工具:flag 包入门与实战


文章目录

  • Langchain系列文章目录
  • Python系列文章目录
  • PyTorch系列文章目录
  • 机器学习系列文章目录
  • 深度学习系列文章目录
  • Java系列文章目录
  • JavaScript系列文章目录
  • Python系列文章目录
  • Go语言系列文章目录
  • 摘要
  • 一、为何需要命令行参数解析?
    • 1.1 命令行工具(CLI)的魅力
    • 1.2 手动解析 vs `flag` 包
  • 二、`flag` 包核心概念与工作流
    • 2.1 定义命令行标志 (Flags)
      • 2.1.1 `flag.Type()` 函数族:返回指针
      • 2.1.2 `flag.TypeVar()` 函数族:绑定到变量
      • 2.1.3 对比与选择
    • 2.2 解析命令行参数
      • 2.2.1 关键一步:`flag.Parse()`
    • 2.3 友好的帮助信息
  • 三、实战案例:构建一个简单的文件下载器
    • 3.1 需求分析
    • 3.2 代码实现
    • 3.3 运行与测试
  • 四、`flag` 包进阶与技巧
    • 4.1 处理非标志参数
    • 4.2 自定义 `FlagSet`
    • 4.3 常见问题与注意事项
  • 五、总结


摘要

本文是 Go 语言从入门到精通 系列的第 36 篇。在现代软件开发中,构建命令行工具(CLI)是一项至关重要的技能,无论是用于自动化脚本、开发辅助工具还是后端服务管理。Go 语言凭借其高效的编译速度和原生支持,成为编写 CLI 应用的绝佳选择。本文将深入探讨 Go 标准库中专门用于解析命令行参数的利器——flag 包。我们将从基础概念入手,系统讲解如何定义不同类型的标志、如何解析用户输入,并最终通过一个实战案例,手把手教你构建一个功能完备的命令行下载工具。无论你是 Go 初学者还是希望提升工具开发能力的进阶者,本文都将为你提供清晰、实用的指南。

一、为何需要命令行参数解析?

在深入 flag 包之前,我们首先要理解为什么在命令行程序中,一个健壮的参数解析机制是必不可少的。

1.1 命令行工具(CLI)的魅力

命令行界面(Command-Line Interface, CLI)是开发者和系统管理员的瑞士军刀。相比图形用户界面(GUI),CLI 具有以下不可替代的优势:

  • 高效与自动化:CLI 命令可以轻松地写入脚本,实现任务自动化、批量处理和持续集成/持续部署(CI/CD)流程。
  • 资源占用低:没有图形渲染的开销,CLI 工具通常更轻量,运行更快。
  • 可组合性强:遵循 Unix 哲学,简单的工具可以通过管道(pipe)和重定向组合起来,完成复杂的任务。
  • 环境普适性:在服务器、容器等无图形界面的环境中,CLI 是唯一的交互方式。

我们日常使用的许多强大工具都是 CLI,例如 git(版本控制)、docker(容器管理)、go(Go 工具链本身)等,它们都依赖于精确的命令行参数解析来接收用户的指令。

1.2 手动解析 vs flag

想象一下,如果没有专门的库,我们要如何解析命令行参数?最直接的方式是分析 os.Args 这个字符串切片。os.Args[0] 是程序本身的名称,后续元素则是用户输入的参数。

例如,我们想实现一个程序,接受一个端口号和一个服务名:myserver -port=8080 -service="user_api"

手动解析可能看起来像这样:

package mainimport ("fmt""os""strings"
)func main() {var port intvar serviceName stringargs := os.Args[1:] // 忽略程序名for _, arg := range args {if strings.HasPrefix(arg, "-port=") {// 解析端口...} else if strings.HasPrefix(arg, "-service=") {// 解析服务名...}}// ...需要大量的字符串处理和错误检查fmt.Printf("手动解析:将在端口 %d 启动服务 %s\n", port, serviceName)
}

这种方式的弊端显而易见:

  • 繁琐易错:需要手动处理各种情况,如 -key=value-key value、布尔标志 -verbose 等。
  • 缺乏健壮性:类型转换、默认值、缺失参数等都需要自己实现,代码很快会变得复杂且难以维护。
  • 没有标准帮助信息:无法自动生成 -h-help 这样的帮助文档,用户体验差。

这时,Go 标准库的 flag 包就应运而生了。它为我们提供了一套标准化、功能强大且易于使用的框架来解决上述所有问题。

二、flag 包核心概念与工作流

flag 包的使用遵循一个简单而清晰的流程:定义 -> 解析 -> 使用

2.1 定义命令行标志 (Flags)

flag 包提供了两种主要的方式来定义命令行标志。

2.1.1 flag.Type() 函数族:返回指针

这是最直接的方式。flag 包为每种基本类型都提供了相应的函数,如 flag.String(), flag.Int(), flag.Bool(), flag.Duration() 等。这些函数会返回一个指向该类型值的指针。

函数签名通用格式:
flag.Type(name string, defaultValue Type, usage string) *Type

  • name: 标志的名称,如 “port”。
  • defaultValue: 如果用户未提供该标志,则使用的默认值。
  • usage: 描述该标志用途的字符串,会在显示帮助信息时展示。

代码示例:

package mainimport ("flag""fmt""time"
)func main() {// 定义一个字符串标志 "name",默认值为 "guest",并提供描述namePtr := flag.String("name", "guest", "Your name")// 定义一个整型标志 "port",默认值为 8080portPtr := flag.Int("port", 8080, "Service port number")// 定义一个布尔型标志 "verbose",默认为 false// 布尔标志在命令行中出现即为 true,如: ./my_app -verboseverbosePtr := flag.Bool("verbose", false, "Enable verbose output")// 定义一个时间段标志 "timeout",默认为 30秒timeoutPtr := flag.Duration("timeout", 30*time.Second, "Request timeout duration")// ... 解析和使用将在后面介绍 ...// 为了演示,我们先手动设置一些值(实际应由 flag.Parse() 完成)// 此处仅为说明指针如何工作fmt.Printf("初始指针值: Name: %s, Port: %d, Verbose: %v, Timeout: %v\n", *namePtr, *portPtr, *verbosePtr, *timeoutPtr)
}

2.1.2 flag.TypeVar() 函数族:绑定到变量

有时,我们可能希望将标志的值直接绑定到一个已有的变量上,而不是通过指针来访问。flag.TypeVar() 系列函数就是为此设计的。

函数签名通用格式:
flag.TypeVar(p *Type, name string, defaultValue Type, usage string)

  • p: 一个指向已定义变量的指针。
  • name, defaultValue, usage: 与 flag.Type() 系列函数相同。

代码示例:

package mainimport ("flag""fmt""time"
)// 提前定义好变量
var (name    stringport    intverbose booltimeout time.Duration
)func init() {// 将命令行标志绑定到已有的变量上flag.StringVar(&name, "name", "guest", "Your name")flag.IntVar(&port, "port", 8080, "Service port number")flag.BoolVar(&verbose, "verbose", false, "Enable verbose output")flag.DurationVar(&timeout, "timeout", 30*time.Second, "Request timeout duration")
}func main() {// ... 解析和使用将在后面介绍 ...// 直接访问变量fmt.Printf("初始变量值: Name: %s, Port: %d, Verbose: %v, Timeout: %v\n", name, port, verbose, timeout)
}

2.1.3 对比与选择

特性flag.Type() (例如 flag.String)flag.TypeVar() (例如 flag.StringVar)
返回值返回一个指向新分配值的指针无返回值
使用方式ptr := flag.String(...), 使用时需解引用 *ptrvar v string; flag.StringVar(&v, ...),直接使用变量 v
变量声明无需提前声明变量必须提前声明变量,并将地址传给函数
适用场景简单直接,适用于在函数局部定义和使用标志。当标志与一个结构体字段或全局配置变量关联时,非常方便。

选择建议:对于简单的应用,flag.Type() 更快捷。对于需要将配置集中管理或与现有结构体绑定的复杂应用,flag.TypeVar() 更具可读性和维护性。

2.2 解析命令行参数

定义完所有标志后,最关键的一步就是调用 flag.Parse()

2.2.1 关键一步:flag.Parse()

flag.Parse() 会扫描 os.Args[1:],解析所有定义的标志。这个函数必须在所有标志定义之后,但在使用这些标志的值之前调用。

package mainimport ("flag""fmt"
)func main() {// 1. 定义标志namePtr := flag.String("name", "guest", "Your name")portPtr := flag.Int("port", 8080, "Service port number")// 2. 解析!flag.Parse()// 3. 使用解析后的值fmt.Printf("Hello, %s!\n", *namePtr)fmt.Printf("Starting service on port %d...\n", *portPtr)
}

运行示例:

# 编译程序
go build -o myapp# 1. 使用默认值
# > ./myapp
# 输出:
# Hello, guest!
# Starting service on port 8080...# 2. 提供自定义值
# > ./myapp -name="Alice" -port=9000
# 输出:
# Hello, Alice!
# Starting service on port 9000...# 3. 也支持 -key value 的形式
# > ./myapp -name Alice -port 9000
# 输出:
# Hello, Alice!
# Starting service on port 9000...

2.3 友好的帮助信息

flag 包的一大优点是能自动生成帮助信息。当用户提供 -h-help 标志时,程序会打印所有已定义标志的名称、默认值和用途描述,然后退出。

运行示例:

# > ./myapp -h
# Usage of ./myapp:
#   -name string
#     	Your name (default "guest")
#   -port int
#     	Service port number (default 8080)

这个功能极大地提升了命令行工具的用户友好性。你也可以通过给 flag.Usage 变量赋一个自定义函数来覆盖默认的帮助信息,以提供更详细的说明或示例。

flag.Usage = func() {fmt.Fprintf(os.Stderr, "这是一个自定义的帮助信息。\n")fmt.Fprintf(os.Stderr, "用法: %s [options]\n", os.Args[0])fmt.Fprintf(os.Stderr, "选项:\n")flag.PrintDefaults() // 打印所有定义的标志
}

三、实战案例:构建一个简单的文件下载器

现在,让我们综合运用所学知识,构建一个实用的命令行工具:一个简单的文件下载器。

3.1 需求分析

我们的工具 downloader 需要满足以下需求:

  1. 接受一个文件 URL 作为参数 (-url)。
  2. 接受一个可选的输出文件名 (-o),如果未提供,则从 URL 中自动推断。
  3. 接受一个可选的超时时间(秒)(-timeout)。
  4. 提供清晰的帮助信息。

使用示例:
./downloader -url "https://golang.org/dl/go1.18.1.linux-amd64.tar.gz" -o "go_installer.tar.gz" -timeout 60

3.2 代码实现

// downloader.go
package mainimport ("flag""fmt""io""net/http""os""path/filepath""time"
)func main() {// --- 1. 定义命令行标志 ---url := flag.String("url", "", "The URL of the file to download (required)")output := flag.String("o", "", "The output filename (optional, defaults to file name from URL)")timeout := flag.Int("timeout", 30, "Request timeout in seconds")// 自定义帮助信息flag.Usage = func() {fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])fmt.Fprintf(os.Stderr, "  A simple command-line file downloader.\n")flag.PrintDefaults()fmt.Fprintf(os.Stderr, "\nExample:\n  %s -url \"http://example.com/file.zip\" -o \"my_file.zip\"\n", os.Args[0])}// --- 2. 解析参数 ---flag.Parse()// --- 3. 校验参数 ---if *url == "" {fmt.Fprintln(os.Stderr, "Error: -url flag is required.")flag.Usage() // 显示帮助信息并退出os.Exit(1)}// 如果输出文件名为空,则从 URL 推断outputFilename := *outputif outputFilename == "" {outputFilename = filepath.Base(*url)}// --- 4. 执行核心逻辑 ---fmt.Printf("Downloading from %s to %s...\n", *url, outputFilename)// 创建 HTTP 客户端并设置超时client := &http.Client{Timeout: time.Duration(*timeout) * time.Second,}// 发起 GET 请求resp, err := client.Get(*url)if err != nil {fmt.Fprintf(os.Stderr, "Error making request: %v\n", err)os.Exit(1)}defer resp.Body.Close()if resp.StatusCode != http.StatusOK {fmt.Fprintf(os.Stderr, "Error: server returned status %s\n", resp.Status)os.Exit(1)}// 创建输出文件outFile, err := os.Create(outputFilename)if err != nil {fmt.Fprintf(os.Stderr, "Error creating file: %v\n", err)os.Exit(1)}defer outFile.Close()// 将响应体内容拷贝到文件// io.Copy 会高效地处理大文件size, err := io.Copy(outFile, resp.Body)if err != nil {fmt.Fprintf(os.Stderr, "Error writing to file: %v\n", err)os.Exit(1)}fmt.Printf("Download completed successfully! Wrote %d bytes to %s.\n", size, outputFilename)
}

3.3 运行与测试

  1. 编译程序

    go build -o downloader downloader.go
    
  2. 查看帮助信息

    ./downloader -h
    

    输出将会是你自定义的 Usage 信息。

  3. 执行下载 (请替换为有效的 URL):

    # 提供所有参数
    ./downloader -url "https://proof.ovh.net/files/100Mio.dat" -o "testfile.dat" -timeout 60# 不提供输出文件名,自动推断为 100Mio.dat
    ./downloader -url "https://proof.ovh.net/files/100Mio.dat"
    
  4. 测试错误情况

    # 不提供 URL
    ./downloader
    # 输出: Error: -url flag is required. 并显示帮助信息
    

四、flag 包进阶与技巧

4.1 处理非标志参数

有时,命令行除了标志外,还可能包含其他参数,如 go build main.go 中的 main.go。这些不带 - 前缀的参数被称为非标志参数。可以使用 flag.Args() 获取它们。

  • flag.Args(): 返回一个包含所有非标志参数的字符串切片。
  • flag.NArg(): 返回非标志参数的数量。

这两个函数必须在 flag.Parse() 调用之后使用。

示例:

// go run main.go -v arg1 arg2
func main() {verbose := flag.Bool("v", false, "verbose")flag.Parse()fmt.Printf("Verbose: %v\n", *verbose)fmt.Printf("Non-flag arguments: %v\n", flag.Args()) // 输出: [arg1 arg2]fmt.Printf("Number of non-flag arguments: %d\n", flag.NArg()) // 输出: 2
}

4.2 自定义 FlagSet

flag 包的全局函数(如 flag.String, flag.Parse)实际上是在操作一个名为 CommandLine 的全局 FlagSet 实例。对于更复杂的应用,比如实现子命令(如 git commitgit push 有各自不同的选项),你可以创建自己的 FlagSet 实例。

这允许你为程序的不同部分独立地解析参数,避免了全局状态的混乱。

概念示例:

// 模拟 'app subcommand -flag'
func main() {if len(os.Args) < 2 {// ... show help ...return}subcommand := os.Args[1]switch subcommand {case "add":addCmd := flag.NewFlagSet("add", flag.ExitOnError)num1 := addCmd.Int("n1", 0, "first number")num2 := addCmd.Int("n2", 0, "second number")addCmd.Parse(os.Args[2:]) // 只解析子命令后的参数fmt.Printf("Sum: %d\n", *num1 + *num2)case "greet":// ... 定义和解析 greet 子命令的标志 ...}
}

4.3 常见问题与注意事项

  1. flag.Parse() 的位置:务必在所有标志定义之后、使用之前调用。
  2. 参数顺序:按照惯例,命令行中所有标志 (-key=value) 都应出现在非标志参数之前。
  3. 布尔标志:对于 flag.Bool() 定义的标志,如 -verbose,在命令行中只需出现标志名即可,其值会被设为 true。如 ./app -verbose。你也可以显式设置,如 ./app -verbose=false
  4. 短名称flag 包本身不直接支持 Unix 风格的短名称(如 -v 对应 -verbose),但可以通过定义两个标志并检查哪个被设置来实现类似效果,或者使用像 spf13/pflagspf13/cobra 这样的第三方库,它们提供了更丰富的功能。

五、总结

通过本文的学习,我们系统地掌握了 Go 语言中用于构建命令行工具的核心 flag 包。

  1. 核心价值flag 包提供了一个标准化、健壮的框架来解析命令行参数,避免了手动处理 os.Args 的繁琐与易错,并能自动生成帮助信息。
  2. 基本流程:工作流非常清晰,即 定义标志 -> 解析参数 -> 使用值
  3. 定义方式:我们学习了两种定义标志的方法:flag.Type() 系列函数返回一个指针,适合快速简单的场景;flag.TypeVar() 系列函数将标志绑定到现有变量,适合配置与代码分离的复杂场景。
  4. 关键函数flag.Parse() 是整个流程的枢纽,它触发对命令行输入的实际解析。
  5. 实战能力:通过构建一个命令行文件下载器,我们不仅实践了 flag 包的使用,还融合了 net/httpioos 等包的知识,展示了如何将参数解析与实际业务逻辑结合。
  6. 进阶知识:了解了如何使用 flag.Args() 处理非标志参数,以及 FlagSet 在构建复杂子命令结构中的作用,为开发更专业的 CLI 工具打下了基础。

熟练掌握 flag 包是每一位 Go 开发者必备的技能。它能让你轻松地为你的应用、脚本或微服务创建强大而用户友好的命令行接口。


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/diannao/95588.shtml
繁体地址,请注明出处:http://hk.pswp.cn/diannao/95588.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

C语言——深入理解指针(四)

C语言——深入理解指针&#xff08;四&#xff09; 数组名的意义sizeof&#xff08;数组名&#xff09;&#xff0c;且数组名单独放在sizeof内部&#xff0c;则这里的数组名表示整个数组&#xff0c;计算的是整个数组的大小&数组名&#xff0c;这里的数组名表示的是整个数组…

LeetCode 刷题【42. 接雨水】

42. 接雨水 自己做 解&#xff1a;双指针左右分割容器 class Solution { public:int trap(vector<int>& height) {int res 0;int len height.size();if(len < 2) //构不成一个容器了&#xff0c;直接返回return res;int end len - 1; //右边界int…

网络的基本概念、通信原理以及网络安全问题

目录 1、 什么是网络&#xff1f; &#xff08;1&#xff09;网络的概念与本质 &#xff08;2&#xff09;电压信号的合并与抵消 &#xff08;3&#xff09;电压的本质 2、中转设备 &#xff08;1&#xff09;背景 &#xff08;2&#xff09;中转设备的处理能力与编程能…

Windows下使用WSL2创建Ubuntu子系统(更改安装位置与启动图形桌面)

Windows下使用WSL2创建Ubuntu子系统&#xff08;更改安装位置与启动图形桌面&#xff09; 本文介绍如何使用WSL2创建Ubuntu子系统&#xff0c;并更改安装位置到其他磁盘&#xff0c;并启动图形桌面Xfce4。 WSL 版本: 2.5.7.0 系统版本: Windows11 23H2 相关工具&#xff1a;Mo…

时间泄漏 TemporalLeakage

时间泄漏 TemporalLeakage: 就是后续有事件发生&#xff0c;然后才有了这个结果&#xff0c;但是在该事件发生之前&#xff0c;不应该预测该结果。 Temporal Leakage 问题是往往导致纵向Planning不“果断”。 解决方案&#xff1a;人工标注出时间发生的时刻 真值只监督时间发生…

独立书店数字化转型:绝版书修复档案系统与读者阅读行为分析营销平台

在电商冲击与阅读习惯变迁的双重压力下&#xff0c;独立书店正遭遇 “旧书修复难、新书卖不动” 的生存困境。传统模式中&#xff0c;绝版书修复依赖老师傅经验&#xff0c;单本修复周期长达 2 周&#xff0c;损耗率超 30%&#xff1b;营销缺乏数据支撑&#xff0c;导致客流年均…

const修饰指针用法详解

目录 一、const修饰变量 绕过const限制的问题 二、const修饰指针变量 1、无const修饰的指针 2、const放在*左边 3、const放在*右边 4、*两边都有const 三、使用建议 四、记忆技巧 一、const修饰变量 在C语言中&#xff0c;变量默认是可修改的。如果我们希望某个变量不能…

pcl法线估计的踩坑

1&#xff0c;normalestimation对点云法线的评估&#xff0c;只输出法线向量&#xff0c;并不输出xyz值。如果输出类型是pointnormal&#xff0c;那么这点云的法向量有值&#xff0c;xyz值都是02&#xff0c;添加点云xyz数据。可以使用 pcl::concatenatefields(*a,*b,*c)函数p…

利用Minicsv库解析csv文件的c程序及读入测试

上午的c程序写入xlsx较快但不正确&#xff0c;python程序虽正确但过慢。所以找了一个全部源程序加起来不到4K字节的C语言csv解析库Minicsv&#xff0c;来改写&#xff0c;改写结果如下&#xff1a; #include <stdio.h> #include <stdlib.h> #include <string.h…

企微用户部门同步HRS系统

企微用户导入HR系统流程说明 概述 本文档详细说明了WechatUserImportServiceImpl.importWechatUsersToHrs()方法的业务流程和实现逻辑。该方法负责将企业微信用户数据同步导入到HR管理系统中&#xff0c;包括员工信息、工作信息和任职记录的创建与更新。 主要功能 数据同步…

告别传统SEO!拥抱下一代流量密码:生成式引擎优化(GEO)实战指南

前言&#xff1a;为什么你的“最佳实践”SEO正在失效&#xff1f;你是否发现&#xff0c;即使严格遵循了谷歌自2019年以来的所有“最佳实践”&#xff0c;你的技术博客或产品文档的流量依旧增长乏力&#xff0c;甚至不升反降&#xff1f;你不是一个人。问题在于&#xff0c;游戏…

week1-[一维数组]传送

week1-[一维数组]传送 题目描述 有 nnn 个传送门&#xff0c;从第 iii 个传送门进去后会被传送到第 aia_iai​ 个传送门&#xff0c;进而被传送到第 aaia_{a_i}aai​​ 个传送门&#xff0c;如此一直下去……小 A 想知道从第 kkk 个传送门进去后&#xff0c;能不能回到第 kkk 个…

【18】目心智能——目心智能 嵌入式一面 ,校招,面试问答记录

目心智能——目心智能 嵌入式一面 &#xff0c;校招&#xff0c;面试问答记录 1 简单自我介绍2 你做了这么多算法&#xff0c;为什么不找算法的&#xff1f;3 我们主要还是软件开发&#xff0c;不做结构设计4 模电知识6 CSDN应该附链接在简历上&#xff0c;稍后发给我&#xff…

C++第二十课:快递运费计算器 / 黑白配+石头剪刀布小游戏

快递运费计算器帮一家快递站点开发一个快递运费计算器&#xff0c;快递站点人员只需要输入包裹重量和地点编号即可计算出对应的运费。假设快递费计算规则如下&#xff1a;首重&#xff1a;3公斤 3公斤以内&#xff1a;1.东三省/宁夏/青海/海南&#xff1a;12元&#xff0c;2.新…

网络安全蓝队常用工具全景与实战指南

摘要 在现代信息系统的安全防护中&#xff0c;蓝队承担着防御、检测、响应和持续改进的核心职责。要实现高效、可持续的防御能力&#xff0c;蓝队需要一整套成熟、可靠的工具集来进行威胁情报收集、日志分析、入侵检测、漏洞评估、端点防护、网络流量监控、事件响应与取证等工作…

基于 Flink 的淘宝实时数据管道设计:商品详情流式处理与异构存储

引言在电子商务领域&#xff0c;实时数据处理能力已成为企业核心竞争力的重要组成部分。淘宝作为中国领先的电商平台&#xff0c;每天产生海量的商品数据&#xff0c;这些数据需要被实时处理、分析并分发到各种存储系统中&#xff0c;以支持搜索、推荐、库存管理等关键业务。本…

面试题:【多线程问题,三个线程A,B,C;C线程依赖B线程的结果执行,怎么控制】

在 Java 中&#xff0c;若需要控制线程间的依赖关系&#xff08;如 C 线程依赖 B 线程的结果&#xff09;&#xff0c;可以通过以下几种方式实现&#xff1a; 方案 1&#xff1a;使用 CountDownLatch CountDownLatch 是一个同步工具类&#xff0c;允许一个或多个线程等待其他线…

React useMemo 深度指南:原理、误区、实战与 2025 最佳实践

把“为什么用、怎么用、用错了怎么办”一次讲透&#xff0c;附 React 19 自动优化前瞻。一、useMemo 是什么&#xff1f; 一句话&#xff1a; useMemo 记住&#xff08;缓存&#xff09;昂贵计算结果&#xff0c;只在依赖变化时重新计算。 const memoValue useMemo(() > {…

[ HTML 前端 ] 语法介绍和HBuilderX安装

目录 一. HTML 1.概述 2. 安装前端开发工具 (1)HBuilderX下载 (2)创建html项目和使用 3. HTML基础 1.标签 (1).标签定义: (2).标签结构: (3).标签属性: 2.常用标签: 3.特殊符号: 4.表格(table) (1)基本标签: (2)基本结构: (3)表格属性: 5.表单(form) (1). 表单概述…

Spring Cloud系列—Alibaba Sentinel熔断降级

上篇文章&#xff1a; Spring Cloud系列— Alibaba Sentinel限流https://blog.csdn.net/sniper_fandc/article/details/149944260?fromshareblogdetail&sharetypeblogdetail&sharerId149944260&sharereferPC&sharesourcesniper_fandc&sharefromfrom_link…