分隔符模式(Sep Pattern)
本文档介绍 WPL 中 {...} 分隔符模式语法,用于在字段分隔位置使用通配符和特殊匹配进行灵活切分。
适用于快捷分隔符(\,、\s 等)无法满足的复杂场景,例如“跳过任意内容直到关键字““空白 + 特定前缀“等。
提示:如果你的分隔需求可以用固定字符串满足,优先使用快捷分隔符(参考 WPL 基础 - 分隔符优先级与合并)。
📚 文档导航
| 主题 | 内容 |
|---|---|
| 基本语法 | {...} 写法、通配符、转义字符 |
| 语法表 | 所有支持的记法一览 |
通配符 * 与 ? | 非贪婪匹配、单字符匹配 |
| 空白匹配 | \s 连续空白、\h 水平空白 |
保留标记 () | 匹配但不消费,留给下一阶段 |
| 使用约束 | * 和 () 的限制 |
| 实战示例 | 常见场景与完整规则 |
基本语法
在字段的分隔符位置使用花括号 {...} 包裹模式表达式:
# 快捷分隔符
chars\,
# 模式分隔符
chars{*=}
模式分隔符与快捷分隔符遵循相同的优先级规则(字段级 > 组级 > 上游)。
语法表
| 记法 | 说明 | 示例 |
|---|---|---|
a | 字面字符 a | {abc} 匹配 "abc" |
* | 零或多个任意字符(非贪婪,最短匹配) | {*=} 匹配到第一个 = |
? | 恰好一个任意字符 | {field?:} 匹配 "fieldA:" |
\0 | 空字节 | |
\n | 换行 | |
\t | 制表符 | |
\r | 回车 | |
\s | 一个或多个连续空白 [ \t\r\n]+ | {\s=} 跳过空白后匹配 = |
\h | 一个或多个水平空白 [ \t]+ | {\h:\h} 匹配 " : " |
\\ \* \? \{ \} \( \) | 字面转义 | {\*} 匹配字面 * |
(...) | 匹配但不消费(仅限模式末尾) | {*(key=)} 保留 key= |
注意:
\s和\h在{}模式内部匹配一个或多个连续空白;{}之外的\s沿用原有语义,旧配置不受影响。
通配符 * 与 ?
* — 非贪婪匹配
* 匹配零或多个任意字符,采用最短匹配策略。
输入:a=b=c
模式:{*=}
匹配:a= ← 找到第一个 "=" 即停止,而非 "a=b="
典型场景: 消费到某个关键字符。
# 匹配到第一个等号
chars{*=}
# 匹配到第一个冒号加空格
chars{*:\s}
? — 单字符匹配
? 恰好匹配一个任意字符。
输入:field1: value
模式:{field?:\s}
匹配:field1: ← "?" 匹配了 "1"
约束: 模式中 * 最多出现一次,超过则报配置错误。? 无数量限制。
空白匹配 \s 与 \h
\s — 连续空白
匹配一个或多个空白字符(空格、制表符、回车、换行)。
输入:key =value
模式:{\s=}
匹配: = ← 三个空格 + "="
\h — 水平空白
匹配一个或多个水平空白字符(仅空格和制表符,不含换行)。
输入:name : value
模式:{\h:\h}
匹配: : ← 制表/空格 + ":" + 制表/空格
\S 与 \H
与上面相反:
| 记法 | 说明 |
|---|---|
\S | 一个或多个连续非空白字符 |
\H | 一个或多个连续非水平空白字符 |
保留标记(Preserve)
() 包裹的内容参与匹配以确认分隔位置,但不会从输入流中消费。下一阶段从 () 内容的起始处继续读取。
输入:hello key=value
模式:{*\s(key=)}
消费:hello ← 被截断,作为当前字段的值
保留:key=value ← 留在输入流中,下一个字段从这里开始
使用方式
# 消费到 "command=" 之前,保留 "command=" 给下一个字段
chars{*(command=)}
# 消费任意内容 + 空白区域,保留 "next" 给下一个字段
chars{*\s(next)}
约束
()只能出现在模式末尾。{*(key=)}合法,{(key)*=}不合法。()不允许嵌套。()内允许字面量、\s\h\0\n\t\r、?及转义字符。()内不允许*——保留部分必须是确定长度。
使用约束
| 约束 | 说明 |
|---|---|
* 最多一个 | 整个模式中 * 最多出现一次,多于一个报错 |
() 仅末尾 | 保留标记只能出现在模式的最后 |
() 内无 * | 保留段不允许不确定长度的通配 |
() 无嵌套 | 不允许 ((...)) |
| 不可与 ups_val 混用 | 同时配置 {} 与次级结束符时,解析阶段报错 |
实战示例
场景 1:Key=Value 风格日志
日志格式中,字段以 key=value 排列,每个 value 后面跟空白和下一个 key。
输入:src=192.168.1.1 dst=10.0.0.1 action=accept proto=TCP
rule kv_log {
(
ip{*\s(dst=)}:src,
ip{*\s(action=)}:dst,
chars{*\s(proto=)}:action,
chars:proto
)
}
# 输出:src=192.168.1.1, dst=10.0.0.1, action=accept, proto=TCP
说明:每个字段使用 {*\s(next_key=)} 消费到下一个 key 之前的空白,同时保留下一个 key 给后续字段。
场景 2:分隔符中包含空白
日志中字段之间用 |(空格 + 竖线 + 空格)分隔。
输入:192.168.1.1 | admin | 2024-01-01 10:00:00
rule pipe_sep {
(ip:client, chars:user, time:ts){\h|\h}
}
# 输出:client=192.168.1.1, user=admin, ts=2024-01-01 10:00:00
场景 3:匹配到特定关键字
消费到 command= 出现的位置。
输入:user=admin role=root command=ls -la
rule cmd_log {
(
chars{*(command=)}:prefix,
chars:command
)
}
# prefix = "user=admin role=root "
# command = "ls -la"(前面 "command=" 被 consume_sep 消费)
场景 4:字段名后跟变化字符
某些日志的字段名格式为 fieldN:,N 是一个可变字符。
输入:field1: hello field2: world
rule field_var {
(chars{field?:\s}:f1, chars:f2)
}
# f1 = "hello", f2 = "world"
场景 5:纯字面量模式
当 {} 内不含通配符时,等价于快捷字面量分隔符,但可以表达多字符序列。
# 以下两种写法效果相同
chars{::}
# 等价于
chars\:\:
场景 6:结合管道函数
模式分隔符可与字段级管道组合使用。
rule combined {
(
chars{*\s(src=)}:header,
kvarr(\s):payload
) |(take src)
}
与快捷分隔符的对比
| 特性 | 快捷分隔符 | 模式分隔符 |
|---|---|---|
| 语法 | \, \; \s 等 | {...} |
| 匹配能力 | 固定字符串 | 通配符、空白区域、保留标记 |
| 性能 | 最优(memchr) | 接近(纯字面退化为 memchr) |
| 适用场景 | 分隔符是确定字符 | 分隔逻辑含变化部分 |
| 优先级 | 与字段/组级一致 | 与字段/组级一致 |
选择建议:
- 分隔符是固定字符(逗号、空格、竖线等)→ 使用快捷分隔符
- 分隔符包含空白区域、需要跳过到关键字、需要保留部分内容 → 使用模式分隔符
常见问题
Q: {...} 内的 \s 和外面的 \s 一样吗?
不完全一样。{} 内的 \s 匹配一个或多个连续空白字符;{} 外面的 \s 沿用原有语义(表示空格分隔符)。旧配置不受影响。
Q: 可以在组级使用模式分隔符吗?
可以。与快捷分隔符一样,模式分隔符支持字段级和组级:
# 字段级
(chars{*=}, digit)
# 组级
(chars, digit){*=}
Q: * 匹配到行尾怎么办?
如果 * 后续没有其他匹配内容,* 会匹配到输入的末尾。如果没找到后续内容的匹配,则该模式整体不匹配,字段将读取到行尾。
Q: 可以只用 () 不带 * 吗?
可以。() 不依赖 *,例如 {\s(key=)} 表示:匹配空白区域后确认 key= 存在,消费空白部分,保留 key=。
相关资源
- WPL 基础:01-wpl_basics.md — 字段、分组、分隔符基础
- 核心概念:02-core-concepts.md — 分隔符优先级详解
- 管道函数:03-wpl_pipe_functions.md — 字段级管道
- 语言参考:04-language-reference.md — 完整类型列表