Go错误与日志处理—推荐实践
错误的分类
在 Go 语言中,错误是通过实现 error
接口的类型表示的,但不同场景下的错误可以按性质和用途进行分类。以下是 Go 语言错误的常见分类,以及每类错误的解释和示例:
标准错误类型
标准库中定义了许多常见的错误类型,用于表示各种常见的错误场景。以下是一些 Go 标准库中常见的错误类型和相关包:
errors.New
和 fmt.Errorf
-
用于创建自定义的错误。
-
标准库提供的最基础的错误类型。
示例:
import ( "errors" "fmt"
) err1 := errors.New("this is an error")
err2 := fmt.Errorf("formatted error: %d", 42)
IO 相关错误
io
包
-
包含基础 I/O 操作的错误类型。
常见错误:
-
io.EOF
:表示流结束(End Of File)。 -
io.ErrUnexpectedEOF
:在读取流时遇到意外的 EOF。 -
io.ErrClosedPipe
:操作已关闭的管道。
示例:
import "io"
if err == io.EOF { fmt.Println("Reached end of file")
}
文件操作相关错误
os
包
-
处理文件系统相关的错误。
常见错误:
-
os.ErrNotExist
:文件或目录不存在。 -
os.ErrExist
:文件或目录已经存在。 -
os.ErrPermission
:权限不足。 -
os.ErrInvalid
:无效操作。
示例:
import "os" if errors.Is(err, os.ErrNotExist) {fmt.Println("File does not exist")
}
网络相关错误
net
包
-
网络操作相关的错误。
常见错误:
-
net.InvalidAddrError
:无效地址错误。 -
net.UnknownNetworkError
:未知网络类型错误。 -
net.AddrError
:地址解析错误。 -
net.DNSError
:域名解析错误。
示例:
import "net"_, err := net.LookupHost("invalid_domain")
if dnsErr, ok := err.(*net.DNSError); ok {fmt.Println("DNS error:", dnsErr)
}
JSON 相关错误
encoding/json
包
-
JSON 编码和解码的错误。
常见错误:
-
json.InvalidUnmarshalError
:解码到无效的目标。 -
json.UnmarshalTypeError
:JSON 与目标类型不匹配。
示例:
import "encoding/json" var data interface{}
err := json.Unmarshal([]byte("invalid json"), &data) if syntaxErr, ok := err.(*json.SyntaxError); ok { fmt.Println("JSON Syntax Error at offset:", syntaxErr.Offset)
}
HTTP 相关错误
net/http
包
-
HTTP 请求与响应相关的错误。
常见错误:
-
http.ErrHandlerTimeout
:HTTP 处理程序超时。 -
http.ErrBodyNotAllowed
:HTTP 请求体不被允许。
示例:
import "net/http" if errors.Is(err, http.ErrHandlerTimeout) { fmt.Println("HTTP handler timeout")
}
时间解析相关错误
time
包
-
处理时间解析或格式化错误。
常见错误:
-
time.ErrBad
:时间字符串格式错误。
示例:
import "time"_, err := time.Parse("2006-01-02", "invalid-date")
if err != nil { fmt.Println("Time parsing error:", err)
}
数据库相关错误
database/sql
包
-
数据库操作相关的错误。
常见错误:
-
sql.ErrNoRows
:查询未返回结果。 -
sql.ErrTxDone
:事务已完成,不能再执行操作。
示例:
import "database/sql" if errors.Is(err, sql.ErrNoRows) { fmt.Println("No rows found")
}
压缩解压相关错误
compress/gzip
包
-
用于处理 gzip 格式的错误。
常见错误:
-
gzip.ErrHeader
:gzip 文件头错误。
加密解密相关错误
crypto
和 crypto/x509
包
-
加密或证书解析相关错误。
常见错误:
-
x509.IncorrectPasswordError
:密码错误。 -
x509.UnknownAuthorityError
:未知的证书颁发机构。
按错误来源分类
应用级错误
应用程序逻辑中定义的错误,如输入验证失败、业务规则不满足等。这些错误通常由程序员明确定义。
示例:
type ValidationError struct { Field string Msg string
} func (e ValidationError) Error() string { return fmt.Sprintf("validation failed on field %s: %s", e.Field, e.Msg)
}
系统级错误
系统资源相关的错误,包括文件访问、网络问题等。 示例:
func readConfig(filename string) error { _, err := os.ReadFile(filename) if err != nil {return fmt.Errorf("failed to read config: %w", err) } return nil
}
第三方库错误
使用第三方库时返回的错误,需要通过文档或代码了解这些错误的含义,并采取适当措施。 示例:
func sendMessageToKafka() error { err := producer.SendMessage(message) if err != nil { return fmt.Errorf("kafka producer error: %w", err) } return nil
}
按错误处理方式分类
可恢复错误
可以通过重新尝试或特定逻辑处理恢复的错误。 示例:
func retryOperation(attempts int) error { for i := 0; i < attempts; i++ {err := doSomething() if err == nil { return nil } time.Sleep(1 * time.Second) // 等待后重试 } return fmt.Errorf("operation failed after %d attempts", attempts)
}
不可恢复错误
表示程序的逻辑或系统的严重错误,无法通过重新尝试解决,如非法状态、编程错误等。
示例:
func mustDivide(a, b int) int { if b == 0 { panic("division by zero") } return a / b
}
按错误语义分类
用户输入错误
用户提供的输入不满足预期导致的错误。
示例:
func validateInput(input string) error { if input == "" {return fmt.Errorf("input cannot be empty") } return nil
}
数据处理错误
数据格式、解析、转换等问题。
示例:
func parseInt(value string) (int, error) { num, err := strconv.Atoi(value) if err != nil { return 0, fmt.Errorf("failed to parse integer: %w", err) } return num, nil
}
网络/IO 错误
网络连接失败、超时、文件系统操作失败等问题。
示例:
func fetchData(url string) ([]byte, error) { resp, err := http.Get(url) if err != nil { return nil, fmt.Errorf("failed to fetch data: %w", err) } defer resp.Body.Close() return io.ReadAll(resp.Body) }
业务逻辑错误
业务逻辑不满足需求导致的错误。
示例:
func checkAccountBalance(balance, withdrawAmount float64) error { if withdrawAmount > balance { return fmt.Errorf("insufficient balance") } return nil
}
按错误表现分类
明确错误
明确的错误通过 error
接口表示,并具有清晰的语义。
示例:
return fmt.Errorf("unable to connect to database: %w", err)
模糊错误
返回的错误缺乏上下文信息,不利于调试。
示例:
return errors.New("something went wrong") // 不清楚具体问题是什么
总结
Go 中的错误分类可以帮助开发者更清晰地理解错误的来源和性质,从而制定合理的处理策略。推荐:
-
使用明确的错误上下文。
-
尽量细化错误类型,尤其是应用级错误。
-
使用
errors.Is
和errors.As
对错误进行分类处理。 -
在必要的场景下记录日志,但不要重复记录错误信息。
错误处理规范
错误检查与优先处理
-
及时检查错误:不要忽略返回的错误值。
-
优先处理错误:如果发生错误,尽快中止当前流程或采取修复措施。
好的示例:
func readFile(filename string) ([]byte, error) { data, err := os.ReadFile(filename) if err != nil { return nil, fmt.Errorf("failed to read file %s: %w", filename, err) } return data, nil
}
坏的示例:
func readFile(filename string) ([]byte, error) { data, _ := os.ReadFile(filename) // 忽略错误,可能导致不可预见的问题 return data, nil
}
使用错误包装提供上下文信息
-
使用
fmt.Errorf
和%w
包装错误,保留错误链路。 -
错误信息应清晰表明发生错误的上下文。
好的示例:
func processFile(filename string) error { file, err := os.Open(filename) if err != nil { return fmt.Errorf("failed to open file %s: %w", filename, err) } defer file.Close() // 文件处理逻辑... return nil
}
坏的示例:
func processFile(filename string) error { _, err := os.Open(filename) if err != nil { return err // 丢失了错误上下文,难以追踪来源 } return nil
}
自定义错误类型
-
针对特定业务场景,创建自定义错误类型以提供丰富的上下文。
好的示例:
type ValidationError struct { Field string Message string
} func (e ValidationError) Error() string { return fmt.Sprintf("validation failed on field %s: %s", e.Field, e.Message)
} func validateInput(input string) error { if input == "" { return ValidationError{"input", "cannot be empty"} } return nil
}
坏的示例:
func validateInput(input string) error { if input == "" { return fmt.Errorf("invalid input") // 错误信息缺乏上下文 } return nil
}
使用 errors.Is
和 errors.As
检查错误
-
使用
errors.Is
检查错误是否是某种特定类型。 -
使用
errors.As
提取并处理特定的错误类型。
对比
特性 | errors.Is | errors.As |
用途 | 检查错误值是否相等或包装目标错误 | 检查错误是否为特定类型 |
参数 | 错误和目标错误值 | 错误和目标错误类型的指针 |
返回值 | 布尔值 | 布尔值,目标指针可能会被赋值 |
支持链式错误 | 是 | 是 |
适用场景 | 判断是否是某个特定错误 | 判断是否属于某个特定类型的错误 |
好的示例:
func handleError(err error) { if errors.Is(err, os.ErrNotExist) { fmt.Println("文件不存在") } var pathErr *os.PathError if errors.As(err, &pathErr) { fmt.Printf("路径错误: %s\n", pathErr.Path) }
}
避免滥用 panic
,使用显式错误返回
-
panic
仅用于不可恢复的错误,普通错误应返回error
。 -
提供有意义的错误信息。
好的示例:
func divide(a, b int) (int, error) { if b == 0 { return 0, fmt.Errorf("division by zero") } return a / b, nil
}
坏的示例:
func divide(a, b int) int { if b == 0 { panic("division by zero") // 滥用 panic,不建议用于常规错误处理 } return a / b
}
日志与错误分离
-
错误和日志分层:日志应由调用方处理,库函数仅返回错误。
-
日志通常在服务层或调用者处理,库函数不应记录日志。
好的示例:
func fetchData(url string) ([]byte, error) { resp, err := http.Get(url) if err != nil { return nil, fmt.Errorf("failed to fetch data from %s: %w", url, err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) } return io.ReadAll(resp.Body)
}
坏的示例:
func fetchData(url string) ([]byte, error) { resp, err := http.Get(url) if err != nil { log.Printf("error: %v", err) // 不必要的日志记录 return nil, err } defer resp.Body.Close() return io.ReadAll(resp.Body)
}
使用 defer
简化资源清理
-
使用
defer
保证资源在函数退出时被正确释放。
好的示例:
func processLargeFile(filename string) error { file, err := os.Open(filename) if err != nil { return fmt.Errorf("failed to open file: %w", err) } defer file.Close() // 确保资源释放 // 文件处理逻辑... return nil
}
坏的示例:
func processLargeFile(filename string) error { file, err := os.Open(filename) if err != nil { return err } // 如果忘记关闭文件,会导致资源泄露 file.Close() return nil
}
分层处理错误
-
在业务逻辑层返回错误,允许调用方决定是否记录日志。
-
在顶层捕获错误并进行统一处理。
好的实践:
// 库函数
func queryDatabase(query string) ([]Record, error) { rows, err := db.Query(query) if err != nil { return nil, fmt.Errorf("database query failed: %w", err) } defer rows.Close() // 解析数据... return records, nil
} // 应用层
func handleRequest(query string) { records, err := queryDatabase(query) if err != nil { log.Printf("query error: %v", err) return } fmt.Println(records)
}
错误与日志处理的推荐实践
不同层级对错误和日志的处理
数据访问层(DAL/Repository)
职责:直接与数据库或其他持久化存储交互。
-
错误处理:
-
返回具体的、易于处理的错误。例如:SQL 执行失败、数据未找到等。
-
尽量使用错误包装 (
fmt.Errorf
),为上层提供上下文。 -
不要记录日志,交由上层决定是否需要记录。
-
-
示例:
func GetUserByID(id int) (*User, error) { user := &User{} err := db.QueryRow("SELECT * FROM users WHERE id = ?", id).Scan(&user.ID, &user.Name) if errors.Is(err, sql.ErrNoRows) { return nil, fmt.Errorf("user with ID %d not found: %w", id, err) } if err != nil { return nil, fmt.Errorf("failed to fetch user: %w", err) } return user, nil
}
服务层(Service/Use Case)
职责:实现业务逻辑。
-
错误处理:
-
捕获底层错误并添加业务语义上下文。
-
根据需要返回特定业务错误或通用错误。
-
可对一些关键错误进行日志记录(如影响业务流程的错误)。
-
-
日志记录:
-
记录错误可能对调试或审计有价值的信息。
-
日志应包含业务上下文(如用户 ID、请求参数等)。
-
-
示例:
func ProcessOrder(orderID int) error { order, err := repo.GetOrderByID(orderID) if err != nil { return fmt.Errorf("failed to process order %d: %w", orderID, err) } if order.Status != "pending" { return fmt.Errorf("order %d is not in a pending state", orderID) } // 业务逻辑... return nil
}
控制器层(Controller/Handler)
职责:处理用户请求并返回响应。
-
错误处理:
-
将服务层的错误转换为用户友好的消息(HTTP 状态码或自定义响应)。
-
不应暴露底层实现细节。
-
-
日志记录:
-
在请求入口处记录重要的请求信息。
-
在错误返回时记录错误上下文和请求相关信息。
-
-
示例:
func OrderHandler(w http.ResponseWriter, r *http.Request) { orderID, err := strconv.Atoi(r.URL.Query().Get("id")) if err != nil { http.Error(w, "invalid order ID", http.StatusBadRequest) return } err = service.ProcessOrder(orderID) if err != nil { log.Printf("failed to process order: %v", err) http.Error(w, "failed to process order", http.StatusInternalServerError) return } w.WriteHeader(http.StatusOK)
}
错误与日志的推荐实践
错误返回层级
-
底层(如 DAL):
-
返回详细的上下文错误,方便上层理解问题。
-
不记录日志,避免重复记录。
-
-
中间层(如 Service):
-
包装底层错误,提供业务相关的上下文。
-
根据需要选择是否记录关键日志。
-
-
顶层(如 Controller):
-
转换错误为用户友好的消息。
-
记录完整的请求上下文和错误信息。
-
日志记录层级
-
入口点(Controller/Handler):
-
记录请求相关的信息(URL、参数、用户身份等)。
-
记录最终的响应状态。
-
-
服务层:
-
记录对业务有重要影响的错误或状态变化。
-
-
数据层:
-
尽量不记录日志,避免暴露内部实现细节。
-
常见反模式与改进
-
重复记录日志:
-
底层记录错误,上层再次记录相同错误,导致日志冗余。
-
改进:仅在一个明确的层级记录日志。
-
-
暴露内部错误细节:
-
直接将数据库错误返回到用户端。
-
改进:在顶层捕获并转换为用户友好的消息。
-
-
忽略日志上下文:
-
日志中缺乏关键信息(如用户 ID、操作参数等)。
-
改进:在日志中包含足够的上下文信息。
-
总结
错误处理应遵循逐层封装的原则,每一层专注于自身职责,避免信息泄漏或日志冗余。日志记录应关注调试和审计价值,并在错误信息中添加业务或操作上下文,从而提高系统的可维护性和可观测性。
相关文章:
Go错误与日志处理—推荐实践
错误的分类 在 Go 语言中,错误是通过实现 error 接口的类型表示的,但不同场景下的错误可以按性质和用途进行分类。以下是 Go 语言错误的常见分类,以及每类错误的解释和示例: 标准错误类型 标准库中定义了许多常见的错误类型&…...
Android 13 Aosp Settings Android Studio版本
Android 13 Aosp Settings Android Studio版本 Settings相关源码 Settings https://android.googlesource.com/platform/packages/apps/Settings/+/refs/heads/android13-release SettingsIntelligence https://android.googlesource.com/platform/packages/apps/SettingsIn…...

Jedis存储一个以byte[]的形式的对象到Redis
1.1 准备一个User实体类 import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor;import java.io.Serializable; import java.util.Date;Data NoArgsConstructor AllArgsConstructor public class User implements Serializable {private In…...

updatexml报错注入原理分析
《网络安全自学教程》 SQL注入时,经常利用updatexml()的报错特性来脱库。 updatexml报错原理 1、updatexml语法参数2、报错原理分析3、使用updatexml()脱库4、分割显示结果 updatexml() 的作用是修改xml文件的内容。 1、updatexml语法参数 updatexml(参数1&#x…...

蓝桥杯c++算法秒杀【6】之动态规划【上】(数字三角形、砝码称重(背包问题)、括号序列、组合数问题:::非常典型的必刷例题!!!)
下将以括号序列、组合数问题超级吧难的题为例子讲解动态规划 别忘了请点个赞收藏关注支持一下博主喵!!!! ! ! ! ! 关注博主,更多蓝桥杯nice题目静待更新:) 动态规划 一、数字三角形 【问题描述】 上图给出了…...

【Qt】重写QComboBox下拉展示多列数据
需求 点击QComboBox时,下拉列表以多行多列的表格展示出来。 实现 直接上代码: #include <QComboBox> #include <QTableWidget> #include <QVBoxLayout> #include <QWidget> #include <QEvent> #include <QMouseEve…...

【mac】终端左边太长处理,自定义显示名称(terminal路径显示特别长)
1、打开终端 2、步骤 (1)修改~/.zshrc文件 nano ~/.zshrc(2)添加或修改PS1,我是自定义了名字为“macminiPro” export PS1"macminiPro$ "(3)使用 nano: Ctrl o (字母…...

基于Springboot的流浪宠物管理系统
基于javaweb的流浪宠物管理系统 介绍 基于javaweb的流浪宠物管理系统的设计与实现,后端框架使用Springbootmybatis,前端框架使用Vuehrml,数据库使用mysql,使用B/S架构实现前台用户系统和后台管理员系统,和不同权限级别…...

web博客系统的自动化测试
目录 前言测试用例编写自动化脚本测试准备博客登录页相关测试用例登陆成功登录失败 博客首页相关测试用例登陆成功登录失败 博客详情页相关测试用例登录成功登录失败 博客编辑页相关测试用例登陆成功登录失败 编写测试文档测试类型内容 前言 本次测试是运用个人写的一个博客系…...

【论文阅读】Multi-level Semantic Feature Augmentation for One-shot Learning
用于单样本学习的多层语义特征增强 引用:Chen, Zitian, et al. “Multi-level semantic feature augmentation for one-shot learning.” IEEE Transactions on Image Processing 28.9 (2019): 4594-4605. 论文地址:下载地址 论文代码:https:…...
网络知识面试
1、http状态码 101: 切换请求协议 200:(请求成功)。服务器已成功处理了请求。 通常,这表示服务器提供了请求的网页。 301 : (永久移动,永久性重定向,会缓存) 请求的网页已永久移动到新位置。 服务器返回此响应(对 GET 或 HEAD 请求的响应)时,会自动将请求者转到新位置。…...
图片预览 图片上传到服务器
首先要明白 理解 multipart/form-data:multipart/form-data是一种在HTTP请求中使用的MIME类型,主要用于在客户端和服务器之间传输包含文件或二进制数据的表单数据。它通过一个边界(boundary)来分隔不同的表单字段和文件数据。…...
前端:base64的作用
背景 项目中发现,img标签中写src,读取一个png图片,只有16kb,速度特别慢。 解决办法,将图片转为base64,然后读取,速度特别快17ms就解决。 定义:base64是一种基于64个可打印字符(A-…...
Django在fitler过滤不等于的条件
提问 django 在API接口fitler的时候如何过滤 category 不等于6的 解答 为了在AoYuStudentFilter中设置过滤category不等于6的条件,需要使用django_filters库中的exclude方法。不过直接在FilterSet中使用exclude可能不那么直观,因为FilterSet主要设计用…...
Spring Boot英语知识分享网站:技术与实践
2相关技术 2.1 MYSQL数据库 MySQL是一个真正的多用户、多线程SQL数据库服务器。 是基于SQL的客户/服务器模式的关系数据库管理系统,它的有点有有功能强大、使用简单、管理方便、安全可靠性高、运行速度快、多线程、跨平台性、完全网络化、稳定性等,非常…...
京准电钟:NTP网络校时服务器从入门到精准
京准电钟:NTP网络校时服务器从入门到精准 京准电钟:NTP网络校时服务器从入门到精准 1.前言 由计算机网络系统组成的分布式系统,若想协调一致进行:IT行业的“整点开拍”、“秒杀”、“Leader选举”,通信行业的“同步…...

C++趣味编程玩转物联网:用树莓派Pico控制四位数码管
数码管是一种常用的数字显示器件,广泛应用于电子时钟、记分牌和智能设备显示界面。在本项目中,我们将通过树莓派Pico板控制一个四位数码管模块,展示从 0000 到 9999 的数字动态显示。这不仅是一次硬件和软件结合的实践,还可以帮助…...

DRM(数字权限管理技术)防截屏录屏----视频转hls流加密、web解密播放
提示:视频转hls流加密、web解密播放 需求:研究视频截屏时,播放器变黑,所以先研究的视频转hls流加密 文章目录 [TOC](文章目录) 前言一、工具ffmpeg、openssl二、后端nodeexpress三、web播放四、文档总结 前言 HLS流媒体协议&a…...

实验三 z变换及离散时间LTI系统的z域分析
实验原理 有理函数z 变换的部分分式展开 【实例2-1】试用Matlab 命令对函数 X ( z ) 18 18 3 − 1 − 4 z − 2 − z − 3 X\left(z\right)\frac{18}{183^{-1} -4z^{-2} -z^{-3} } X(z)183−1−4z−2−z−318 进行部分分式展开,并求出其z 反变换。 B[18]; A…...
Python中的DrissionPage详解
文章目录 Python中的DrissionPage详解一、引言二、DrissionPage的基本使用1、安装与启动2、元素定位与操作 三、高级功能1、截图功能2、数据提取3、与其他库的集成 四、具体使用示例五、总结 Python中的DrissionPage详解 一、引言 DrissionPage是一个强大的Python库ÿ…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...

7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...
Frozen-Flask :将 Flask 应用“冻结”为静态文件
Frozen-Flask 是一个用于将 Flask 应用“冻结”为静态文件的 Python 扩展。它的核心用途是:将一个 Flask Web 应用生成成纯静态 HTML 文件,从而可以部署到静态网站托管服务上,如 GitHub Pages、Netlify 或任何支持静态文件的网站服务器。 &am…...
Qt Http Server模块功能及架构
Qt Http Server 是 Qt 6.0 中引入的一个新模块,它提供了一个轻量级的 HTTP 服务器实现,主要用于构建基于 HTTP 的应用程序和服务。 功能介绍: 主要功能 HTTP服务器功能: 支持 HTTP/1.1 协议 简单的请求/响应处理模型 支持 GET…...
【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】
1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件(System Property Definition File),用于声明和管理 Bluetooth 模块相…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...
从面试角度回答Android中ContentProvider启动原理
Android中ContentProvider原理的面试角度解析,分为已启动和未启动两种场景: 一、ContentProvider已启动的情况 1. 核心流程 触发条件:当其他组件(如Activity、Service)通过ContentR…...

破解路内监管盲区:免布线低位视频桩重塑停车管理新标准
城市路内停车管理常因行道树遮挡、高位设备盲区等问题,导致车牌识别率低、逃费率高,传统模式在复杂路段束手无策。免布线低位视频桩凭借超低视角部署与智能算法,正成为破局关键。该设备安装于车位侧方0.5-0.7米高度,直接规避树枝遮…...

Visual Studio Code 扩展
Visual Studio Code 扩展 change-case 大小写转换EmmyLua for VSCode 调试插件Bookmarks 书签 change-case 大小写转换 https://marketplace.visualstudio.com/items?itemNamewmaurer.change-case 选中单词后,命令 changeCase.commands 可预览转换效果 EmmyLua…...
[USACO23FEB] Bakery S
题目描述 Bessie 开了一家面包店! 在她的面包店里,Bessie 有一个烤箱,可以在 t C t_C tC 的时间内生产一块饼干或在 t M t_M tM 单位时间内生产一块松糕。 ( 1 ≤ t C , t M ≤ 10 9 ) (1 \le t_C,t_M \le 10^9) (1≤tC,tM≤109)。由于空间…...