go 语言中默认的正则表达式 . 元字符不匹配换行符,即使启用 (?m) 多行模式也无效;需使用 (?s) 单行模式(dotall 模式)让 . 匹配包括 \n 在内的任意字符。
go 语言中默认的正则表达式 . 元字符不匹配换行符,即使启用 (?m) 多行模式也无效;需使用 (?s) 单行模式(dotall 模式)让 . 匹配包括 \n 在内的任意字符。
在 Go 中处理跨多行的 HTML 或自定义标签(如 <think>...</think>)时,常见误区是误以为 (?m)(多行模式)能让 . 匹配换行符。实际上,(?m) 仅影响 ^ 和 $ 的行为(使其分别匹配每行起始/结尾),并不改变 . 的语义。
要使 . 匹配换行符,必须启用 (?s) 标志(即 single-line 或 dotall 模式)。该标志让 . 可匹配任意 Unicode 字符,包括 \n、\r 等空白符。
✅ 正确写法如下:
func main() {
// 使用 (?s) 而非 (?m):s 表示 dotall,允许 . 匹配换行符
r := regexp.MustCompile(`(?s)<think>(.*?)</think>`)
const s = `That is
<think>
FOOBAR
</think>`
matches := r.FindStringSubmatch([]byte(s))
if len(matches) > 0 {
fmt.Printf("Matched: %s\n", strings.TrimSpace(string(matches)))
// 输出: Matched: <think>
// FOOBAR
// </think>
// 若只提取标签内内容(不含标签本身),用子组:
submatches := r.FindSubmatch([]byte(s))
if len(submatches) >= 2 {
content := strings.TrimSpace(string(submatches[1]))
fmt.Printf("Content only: %q\n", content) // "FOOBAR"
}
}
}⚠️ 注意事项:
立即学习“go语言免费学习笔记(深入)”;
- (?s) 和 (?m) 可组合使用(如 (?sm)),但本例中仅 (?s) 必需;
- 建议使用非贪婪量词 .*? 替代 .*,避免跨标签匹配(例如当文本含多个 <think> 时,.* 会从第一个开始一直匹配到最后一个 </think>);
- regexp.MustCompile 接收 string,但 FindStringSubmatch 返回 []byte;若需字符串结果,可配合 string() 转换并用 strings.TrimSpace 清理首尾空白;
- 对于结构化内容(如 XML/HTML),生产环境推荐使用专用解析器(如 golang.org/x/net/html),正则仅适用于简单、可控的模板场景。
总结:Go 正则中的 . 默认为“除换行符外任意字符”,启用 (?s) 是实现真正多行捕获的关键;理解 (?s) 与 (?m) 的职责分离,能避免大量调试陷阱。


















