- 编程语言
- 编译器
- 云原生
- 语言运行时
【免费下载链接】wing
A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡
导读
Wing 语言内置了对正则表达式(regular expressions)的一等支持,开发者无需引入任何外部库,即可通过全局regex命名空间完成模式编译、匹配检测、捕获组提取、索引定位与全局替换等常见任务。本文以 docs/by-example/22-regex.md 的官方示例为主体,结合 SDK 源码(packages/@winglang/sdk/src/std/regex.ts)与完整测试用例(tests/sdk_tests/std/regex.test.w),逐方法剖析Regex类型的行为细节与底层实现,帮助你掌握在 Wing 中编写、运行和验证正则表达式的完整实战能力。
Wing 内置正则支持概览
Wing 把正则表达式作为标准库中的一等类型提供,对应 SDK 中的Regex类,类型名称为regex。与 JavaScript 的RegExp类似,使用前需要先把模式字符串编译为Regex对象,之后即可反复调用匹配、查找、替换等实例方法。
在 SDK 中,Regex类被标记为@wingType regex,并实现了ILiftable接口(见 packages/@winglang/sdk/src/std/regex.ts),这意味着编译后的正则对象既可以用于 preflight 阶段,也可以通过 lift 机制进入 inflight 代码——_toInflight()会生成对应的运行时构造代码(new (require(...).Regex)(...)),保证模式在不同执行环境下行为一致。完整的 API 签名清单见标准库参考文档 docs/api/04-standard-library/std/regex.md。
Regex对外暴露的接口共 9 个:1 个静态编译方法compile,以及 8 个实例方法——test、find、findIndex、findAll、findAllIndex、findSubmatch、findSubmatchIndex、replaceAll。
官方示例:从编译到输出的完整演示
下面这段代码是官方 by-example 文档(docs/by-example/22-regex.md)中的完整示例。它编译了模式"p([a-z]+)ch"——即以p开头、中间为至少一个[a-z]小写字母捕获组、以ch结尾——然后依次演示全部 8 个实例方法:
let r = regex.compile("p([a-z]+)ch"); // Checks if the regular expression matches the provided text. log(r.test("peach")); // Finds the first occurrence of the pattern within the text. log(r.find("peach peach") ?? ""); // Finds all non-overlapping occurrences of the pattern within the text. log(Json.stringify(r.findAll("peach punch pinch"))); // Finds the start and end index of all matches within the text. log(Json.stringify(r.findAllIndex("peach punch pinch"))); // Finds the start and end index of the first match within the text. log(Json.stringify(r.findIndex("peach"))); // Finds the first match and its submatches. log(Json.stringify(r.findSubmatch("peach punch"))); // Finds the start and end index of the match and all submatches. log(Json.stringify(r.findSubmatchIndex("peach punch"))); // Replaces all occurrences of the match with a replacement string. log(r.replaceAll("a peach", "<fruit>"));在 Wing 控制台中运行(wing it)后,输出如下:
# Run locally with wing console wing it true peach ["peach","punch","pinch"] [[0,5],[6,11],[12,17]] [0,5] ["peach","ea"] [[0,5],[1,3]] a注意:find、findIndex、findSubmatch、findSubmatchIndex均可能没有匹配结果,返回值是可选类型(str?/MutArray<num>?/MutArray<str>?等),因此示例中用?? ""提供兜底值;而findAll、findAllIndex在没有匹配时返回空数组,不会返回nil。这一点在 API 参考文档 docs/api/04-standard-library/std/regex.md 中有明确标注。
该示例在仓库中被自动抽取为可执行的 Wing 文件 tests/doc_examples/valid/22-regex.md_example_1/main.w,属于 doc_examples 有效性测试的一部分,可以直接运行验证。
编译模式:regex.compile
所有正则使用都从编译开始:
let r = regex.compile("p([a-z]+)ch");compile是Regex的静态方法,签名与说明如下(见 docs/api/04-standard-library/std/regex.md):
| 项目 | 说明 |
|---|---|
| 签名 | regex.compile(pattern: str); |
参数pattern | 要编译的正则模式字符串(必填) |
| 返回值 | 新的Regex对象 |
底层实现非常轻量:compile直接new Regex(pattern),构造器内部把模式交给 JavaScript 原生new RegExp(pattern)(见 packages/@winglang/sdk/src/std/regex.ts)。也就是说,Wing 的正则语法与 JavaScript 的RegExp语法一致,熟悉 JS 正则的开发者可以无缝迁移。
regex也是一个可用于类型注解的类型。在 tests/valid/std_type_annotation.test.w 中可以看到标准用法:
let decimal: regex = regex.compile("^[0-9]*$"); let testString = inflight (re: regex, s: str) => { return re.test(s); }; test "regex is valid type" { let binary: regex = regex.compile("^[0-1]*$"); expect.equal(testString(decimal, "24"), true); expect.equal(testString(decimal, "a23"), false); expect.equal(testString(binary, "120010"), false); }该测试同时验证了两点:regex是合法的注解类型;Regex对象可以通过inflight闭包被 lift 到 inflight 阶段使用(前文提到的ILiftable实现)。
逐个解析 8 个实例方法
下面按"功能—签名—语义—示例"的结构逐个展开。所有签名均以 docs/api/04-standard-library/std/regex.md 为准。
1.test:检查是否匹配
test(text: str): bool返回布尔值,指示整个或部分文本是否命中模式。test是最常用的"存在性"判断,也常被用于条件分支。
log(r.test("peach")); // true log(r.test("apple")); // false底层实现直接委托给 JS 的RegExp.prototype.test(packages/@winglang/sdk/src/std/regex.ts)。
2.find:查找第一个匹配
find(text: str): str?返回文本中第一个匹配到的子串;若没有匹配则返回nil,因此调用方通常需要配合??处理:
log(r.find("peach peach") ?? ""); // peach源码中通过text.match(this._regex)取result[0]实现(packages/@winglang/sdk/src/std/regex.ts)。
3.findIndex:第一个匹配的起止索引
findIndex(text: str): MutArray<num>?返回[start, end]形式的零基索引数组,标识第一个匹配子串在原文中的位置范围;无匹配时返回nil。注意结束索引是开区间(end不包含在匹配内),切片语义为text[start..end)。
log(Json.stringify(r.findIndex("peach"))); // [0,5]实现为[result.index, result.index + result[0].length](packages/@winglang/sdk/src/std/regex.ts)。
4.findAll:查找所有不重叠匹配
findAll(text: str): MutArray<str>返回模式在文本中所有不重叠匹配组成的数组;无匹配时返回空数组(而非nil)。"不重叠"意味着一旦找到一个匹配,下一次搜索从它的结束位置继续。
log(Json.stringify(r.findAll("peach punch pinch"))); // ["peach","punch","pinch"]源码中先把正则转换为全局(g标志)版本,再用String.prototype.matchAll收集结果(packages/@winglang/sdk/src/std/regex.ts)。
5.findAllIndex:所有匹配的起止索引
findAllIndex(text: str): MutArray<MutArray<num>>返回所有匹配的[start, end]索引数组的数组,零基索引,顺序与findAll一一对应:
log(Json.stringify(r.findAllIndex("peach punch pinch"))); // [[0,5],[6,11],[12,17]]示例中三个词依次占位[0,5]、[6,11]、[12,17],注意空格被跳过(索引基于原始文本)。
6.findSubmatch:第一个匹配及其捕获组
findSubmatch(text: str): MutArray<str>?返回一个数组:第一项是整个匹配,随后依次是每个捕获组(括号分组)匹配到的内容。若整体无匹配则返回nil。
log(Json.stringify(r.findSubmatch("peach punch"))); // ["peach","ea"]对模式"p([a-z]+)ch"而言,"peach"中"ea"正好落入([a-z]+)捕获组,所以第二项是"ea"。实现为[result[0], ...result.slice(1)],即 JSmatch返回数组的展开(packages/@winglang/sdk/src/std/regex.ts)。
7.findSubmatchIndex:匹配与捕获组的起止索引
findSubmatchIndex(text: str): MutArray<MutArray<num>>?在findSubmatch基础上进一步给出每个部分(整体匹配 + 各捕获组)在原文中的[start, end]索引:
log(Json.stringify(r.findSubmatchIndex("peach punch"))); // [[0,5],[1,3]]这里第一项[0,5]是整体匹配"peach"的范围,第二项[1,3]是捕获组"ea"的范围("peach"中第 1~2 个字符)。实现上先取exec结果的整体范围,再用text.indexOf(submatch, 上次起始位置)逐个定位各捕获组(packages/@winglang/sdk/src/std/regex.ts)。若某个捕获组未参与匹配,其索引定位结果需要结合实际文本判断。
8.replaceAll:全局替换
replaceAll(text: str, replacement: str): str将文本中所有匹配替换为replacement字符串并返回新文本。replacement中可以使用$1、$2等引用捕获组(与 JSString.prototype.replace规则一致)。
log(r.replaceAll("a peach", "<fruit>")); // a <fruit>注意replaceAll是"正则全局替换",内部先把正则转换为全局版本再调用text.replace(globalRegex, replacement)(packages/@winglang/sdk/src/std/regex.ts)。
源码深读:getGlobalRegex与"全局化"策略
细心的读者会发现:findAll、findAllIndex、replaceAll三个方法都调用了私有辅助方法getGlobalRegex():
private getGlobalRegex(): RegExp { return this._regex.global ? this._regex : new RegExp(this._regex, "g"); }(见 packages/@winglang/sdk/src/std/regex.ts)
其逻辑是:如果原始模式本身带有g标志(编译时传入,例如regex.compile("p([a-z]+)chg")这种写法在 JS 语法中等价于加g标志的写法),直接复用;否则基于原模式派生一个带g标志的新RegExp。这样既保证了"匹配所有"类方法的全局遍历语义,又不修改用户编译的原始对象。同时,test与find等单次匹配方法使用原始_regex,不受g标志状态影响(JS 中带g的exec/test有 lastIndex 副作用,Wing 通过这一分离设计规避了状态污染问题)。
在测试与断言中使用正则
Regex不仅可以直接在业务逻辑中使用,还内嵌于 Wing 的 expect 库。SDK 的expect模块提供了expect.match(actual, expected)与expect.doesNotMatch(actual, expected)两个断言,内部正是调用Regex.compile(expected).test(actual)实现的(见 packages/@winglang/sdk/src/expect/expect.ts):
bring expect; test "regex" { let r = regex.compile("p([a-z]+)ch"); expect.equal(r.test("peach"), true); expect.equal(r.find("peach peach"), "peach"); expect.equal(r.findAll("peach punch pinch"), ["peach", "punch", "pinch"]); expect.equal(r.findIndex("peach punch"), [0, 5]); expect.equal(r.findAllIndex("peach punch pinch"), [[0, 5], [6, 11], [12, 17]]); expect.equal(r.findSubmatch("peach punch"), ["peach", "ea"]); expect.equal(r.findSubmatchIndex("peach punch"), [[0, 5], [1, 3]]); expect.equal(r.replaceAll("a peach", "<fruit>"), "a <fruit>"); }上面是官方 SDK 测试 tests/sdk_tests/std/regex.test.w 的完整内容,它把本文介绍的全部 8 个方法逐一用expect.equal断言,是验证各方法返回值类型与取值的最佳参考用例(覆盖了顶层代码与test块两种使用位置)。
与字符串方法replace/replaceAll的区分
Wing 的字符串类型也自带replace与replaceAll方法(packages/@winglang/sdk/src/std/string.ts),但二者与Regex.replaceAll有本质区别:
str.replace(search, replace)/str.replaceAll(search, replace):把search当作普通子串做首处 / 全局替换(SDK 中以宏方式编译到 JS 的同名方法);Regex.replaceAll(text, replacement):把text当作被搜索文本,按正则模式做全局替换。
选择规则很简单:替换目标含通配符、捕获组等正则语法时用regex.compile(...).replaceAll(...);纯字面子串替换用str.replaceAll(...)。同时字符串侧没有"按正则替换"的便捷方法,这也是Regex类型存在的重要价值之一。
实战场景:从仓库测试看典型用法
在仓库的真实测试中,regex被广泛用于错误信息匹配、格式校验等场景,可作为实战参考:
- 错误消息断言:
tests/sdk_tests/fs/系列测试用regex.compile("^EEXIST: file already exists")、regex.compile("^ENOENT: no such file or directory")、regex.compile("^EISDIR: illegal operation on a directory")校验文件系统操作的报错前缀(见 tests/sdk_tests/fs/directory.test.w、tests/sdk_tests/fs/options.test.w);tests/sdk_tests/fs/json.test.w用regex.compile("^Unexpected token")校验 JSON 解析错误。 - 权限位校验:
tests/sdk_tests/fs/stat.test.w用regex.compile("[0-7]\{3\}")校验 Unix 权限位是否为三位八进制数字(注意 Wing 中\{的转义写法)。 - 复杂 URL 模式:tests/valid/phase_independent_method_on_string.test.w 演示了编译一个完整的 URL 匹配正则,验证长模式字符串的书写方式(转义
\/与\{1,256\}等)。
这些用例共同说明:Wing 的regex集成覆盖了"编译—匹配—断言"的完整链路,既可直接服务于业务逻辑,也能支撑测试断言,是一个开箱即用的生产级工具。
快速上手小结
| 需求 | 方法 | 返回类型 | 无匹配时 |
|---|---|---|---|
| 是否存在匹配 | test | bool | false |
| 第一个匹配 | find | str? | nil |
| 第一个匹配索引 | findIndex | MutArray<num>? | nil |
| 所有匹配 | findAll | MutArray<str> | 空数组 |
| 所有匹配索引 | findAllIndex | MutArray<MutArray<num>> | 空数组 |
| 匹配 + 捕获组 | findSubmatch | MutArray<str>? | nil |
| 匹配 + 捕获组索引 | findSubmatchIndex | MutArray<MutArray<num>>? | nil |
| 全局替换 | replaceAll | str | 返回原文本 |
把main.w写入项目后执行wing it即可在 Wing 控制台看到与官方示例一致的输出;需要自动化断言时,可直接复用 tests/sdk_tests/std/regex.test.w 的写法。更多细节与完整签名可查阅 docs/api/04-standard-library/std/regex.md。
- 编程语言
- 编译器
- 云原生
- 语言运行时
【免费下载链接】wing
A programming language for the cloud ☁️ A unified programming model, combining infrastructure and runtime code into one language ⚡
相关推荐
Wing 标准库正则表达式 Regex 完全指南:compile、find、findAll、findSubmatch 与 replaceAll 实战详解
Wing 标准库正则表达式 Regex 完全指南:compile、find、findAll、findSubmatch 与 replaceAll 实战详解 本文是
编程语言编译器云原生语言运行时Godot 正则表达式完全指南:RegEx 类的编译、搜索、捕获与替换实战
Godot 正则表达式完全指南:RegEx 类的编译、搜索、捕获与替换实战 正则表达式(Regular Expression,简称 RegEx)是 Godot
文档教程游戏开发Swift Regex Builder DSL 完全指南:用 result builder 构建类型安全的正则表达式(SE-0351)
Swift Regex Builder DSL 完全指南:用 result builder 构建类型安全的正则表达式(SE 0351) 本文基于 swift e
文档
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考