ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

yq 除法运算符(/)完全指南:字符串分割与数值除法详解

2026/9/14 17:37:58 拓冰建站 浏览量
yq 除法运算符(/)完全指南:字符串分割与数值除法详解 yq 除法运算符/完全指南字符串分割与数值除法详解【免费下载链接】yqyq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor项目地址: https://gitcode.com/GitHub_Trending/yq/yq导读/Divide是 yq 中一个一词两用的二元运算符当左侧操作数是字符串时它执行字符串分割当左侧操作数是整数或浮点数时它执行算术除法。本文以 pkg/yqlib/doc/operators/divide.md 为主线结合 operator_divide.go 的源码实现与 operator_divide_test.go 的测试用例完整讲解/的三种典型场景字符串分割、数值除法、除以零、类型分派规则、边界错误场景与底层实现原理让你在 YAML / JSON / XML / TOML 等任意格式的数据处理中准确驾驭这一运算符。核心规则行为取决于 LHS左侧操作数类型Divide 运算符的行为由左侧操作数LHS的类型决定字符串!!str按右侧操作数分隔符进行字符串分割结果为数组序列数字!!int/!!float进行算术除法结果始终按浮点数计算其他类型或类型不匹配抛出错误。这一点在源码中有清晰对应divide函数首先检查 LHS 是否为!!nullnull 不可参与除法随后要求 LHS 与 RHS 均为标量ScalarNode再进入divideScalars依据具体标签分派operator_divide.go。场景一字符串分割String split基本用法给定sample.ymla: cat_meow b: _执行yq .c .a / .b sample.yml输出a: cat_meow b: _ c: - cat - meow即cat_meow按分隔符_被拆分为[cat, meow]并写入字段c。整个表达式可以理解为把.a / .b的分割结果赋值给.c。底层实现strings.Split字符串分割的实现在 operator_strings.go 的split函数中直接调用 Go 标准库strings.Split(value, spltStr)func split(value string, spltStr string) (Kind, string, []*CandidateNode) { var contents []*CandidateNode if value ! { var newStrings strings.Split(value, spltStr) contents make([]*CandidateNode, len(newStrings)) for index, str : range newStrings { contents[index] CandidateNode{Kind: ScalarNode, Tag: !!str, Value: str} } } return SequenceNode, !!seq, contents }值得注意的实现细节分割结果始终是一个!!seq序列/数组每个元素都是!!str字符串标量即使分割出来的片段看起来像数字也不会被隐式转换类型若被分割的字符串为空则返回空序列无任何子节点由于底层是strings.Split其语义与 Go 标准库一致空分隔符会把字符串拆成单个字符序列分隔符不出现时返回包含整个原字符串的单元素序列分隔符本身可以是任意表达式求值出的字符串因此.a / .b中.b可以来自 YAML 字段、环境变量或函数调用结果。快速实践# 直接输出分割结果不写入字段 yq .a / - sample.yml # 按字符拆解字符串 yq .name / sample.yml场景二数值除法Number division基本用法数值除法过程中结果始终按浮点数计算。给定sample.ymla: 12 b: 2.5执行yq .a .a / .b sample.yml输出a: 4.8 b: 2.5整数 12 除以浮点数 2.5得到浮点结果 4.8写入.a。底层实现统一走 float64在divideScalars中operator_divide.go只要 LHS 与 RHS 的标签属于!!int或!!float组合就会把两侧都通过strconv.ParseFloat(value, 64)解析为float64后相除lhsNum, err : strconv.ParseFloat(lhs.Value, 64) rhsNum, err : strconv.ParseFloat(rhs.Value, 64) quotient : lhsNum / rhsNum target.Tag !!float target.Value fmt.Sprintf(%v, quotient)由此可以总结出几条规则结果标签恒为!!float即使12 / 3得到整数 4结果在 yq 中也会被标记为浮点类型输出时可能显示为4但类型语义是 float除法结果不进行四舍五入或精度修饰直接以fmt.Sprintf(%v, quotient)输出 float64 的自然表示整数、浮点可以任意混合int/int、int/float、float/int、float/float四种组合全部支持。场景三除以零Division by zero基本用法除以零不会报错而是得到Inf或-Inf正负无穷。给定sample.ymla: 1 b: -1执行yq .a .a / 0 | .b .b / 0 sample.yml输出a: Inf b: -Inf1 / 0得到Inf-1 / 0得到-Inf。这是 float64 除法的标准 IEEE 754 语义分子为正数得正无穷分子为负数得负无穷。底层实现该行为同样来自strconv.ParseFloat float64 除法Go 中浮点数除以零不会触发 panic而是产生Inf/-Inf当分子不为零时。随后fmt.Sprintf(%v, quotient)会将其格式化为Inf/-Inf字面量yq 将其作为浮点标量输出。使用时的注意点若你的下游工具不接受Inf/-Inf需要自行在表达式中用select或条件判断规避除零场景0 / 0在 float64 语义下会产生NaNNot a Number在编写健壮表达式时同样需要考虑。源码级原理类型分派与错误处理分派逻辑总览divide的核心逻辑operator_divide.go可以概括为LHS 标签为!!null→ 报错!!null (path) cannot be divided by ...LHS 与 RHS 必须都是标量节点否则报错map、seq 等容器类型不可参与除法两个标量进入divideScalars依据标签分派!!str/!!str→ 字符串分割split!!int/!!float组合 → 浮点除法其余组合 → 报错例如!!int cannot be divided by !!str。自定义标签Custom Tags的处理divideScalars对带自定义标签如!horse、!goat的节点做了特殊处理operator_divide.go通过guessTagFromCustomType()猜测自定义标签底层的真实类型若底层是字符串则按字符串分割若底层是数字则按数值除法数值除法时若 LHS 带自定义标签结果的标签会保留为 LHS 的自定义标签如!horse否则统一为!!float。对应测试operator_divide_test.go验证了!horse cat_meow / !goat _按字符串分割、!horse 1.2 / !goat 2.3按数值除法且结果保留!horse标签的行为。运算符注册优先级与词法从 operation.go 可以看到/的注册定义var divideOpType operationType{Type: DIVIDE, NumArgs: 2, Precedence: 42, Handler: divideOperator}二元运算符NumArgs: 2优先级 42与MULTIPLY、MODULO、ADD、SUBTRACT等算术运算符同级operation.go词法层面由 lexer_participle.go 的 token 规则{Divide,/, opToken(divideOpType), 0}识别/符号并映射到该运算类型。多节点组合语义从divideOperator的实现看它通过crossFunction将 LHS 与 RHS 两侧所有匹配节点进行组合运算operator_divide.gofunc divideOperator(d *dataTreeNavigator, context Context, expressionNode *ExpressionNode) (Context, error) { return crossFunction(d, context.ReadOnlyClone(), expressionNode, divide, false) }这意味着当表达式左侧或右侧匹配到多个节点如数组元素、递归遍历结果时/会对每一对 LHS/RHS 组合分别执行除法或分割——从源码结构看这与 multiply 等算术运算符保持一致的分发模型。边界场景与易错点结合 operator_divide_test.go 的测试用例以下场景会直接报错场景示例输入错误信息整数除以字符串a: 123,b: 2表达式.a / .b!!int cannot be divided by !!str字符串除以整数a: 2,b: 123表达式.b / .a!!str cannot be divided by !!intmap 除以数字a: {a:1},b: 2!!map (a) cannot be divided by !!int (b)数组除以字符串a: [1,2],b: 2!!seq (a) cannot be divided by !!str (b)null 参与除法LHS 为!!null!!null (path) cannot be divided by ...实践建议字符串分割只接受字符串分隔符数字分隔符需要先用tostring之类的转换或使用split相关的其他操作数值除法不接受字符串操作数即使字符串内容看起来是数字如2也不会自动转换必须先转为数值类型容器类型map / seq不能直接参与除法除法运算不会改变原文档的其他字段赋值只更新指定目标测试中{a: 12, b: 2.5}除法后b保持不变。测试验证与运行方式该运算符的完整行为由 operator_divide_test.go 中的divideOperatorScenarios表驱动测试覆盖运行入口为TestDivideOperatorScenariosoperator_divide_test.go涵盖字符串分割、数值除法、除以零、自定义标签、锚点保留如a: horse [1]场景下除法后锚点仍被保留、各类类型错误。测试同时通过documentOperatorScenarios机制生成并校验文档中的示例保证 divide.md 与实现行为一致。在仓库根目录运行以下命令即可复现验证go test ./pkg/yqlib/ -run TestDivideOperatorScenarios -v延伸阅读multiply-merge.md同为高优先级算术运算符的*乘/合并行为modulo.md优先级相同的%取模运算符string-operators.md字符串相关的其他操作如拼接、split相关能力operators.md完整运算符参考索引。小结yq 的/运算符是一个按左侧类型自动分派的双面操作符对字符串执行分割底层为strings.Split产出!!seq对数字执行 float64 算术除法结果恒为!!float除零时按 IEEE 754 语义返回Inf/-Inf而非报错。理解它的类型分派规则与边界错误场景能帮助你在复杂数据管道中安全、准确地使用字符串拆分与数值计算能力。【免费下载链接】yqyq is a portable command-line YAML, JSON, XML, CSV, TOML, HCL and properties processor项目地址: https://gitcode.com/GitHub_Trending/yq/yq创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考