第1章:正则表达式与数据结构——用正则解析JSON、XML、YAML及自定义格式

各位同学,今天我们来聊一个很实在的话题——用正则表达式解析各种数据格式

说实话,我刚开始做嵌入式开发那会儿,遇到JSON、XML这些格式,第一反应就是去找现成的解析库。后来有一次在资源受限的MCU上做项目,库太大塞不进去,逼得我手写解析器。嗯,就是那次经历让我发现——正则表达式在某些场景下,比完整解析库更灵活、更轻量

当然,我不是说让你用正则去替代成熟的解析器。但当你需要快速提取数据、做日志分析、或者写一次性脚本时,正则绝对是一把利器。

1.1 为什么用正则解析结构化数据?

你想想看,JSON、XML、YAML这些格式,本质上都是文本。既然是文本,正则就能派上用场。

我个人的习惯是:

  • 生产环境:用成熟的解析库(如cJSON、libxml2)
  • 调试/日志分析:用正则快速提取关键字段
  • 资源受限环境:用正则实现轻量级解析器

核心原则:正则适合提取数据,不适合验证完整语法。别拿正则去校验JSON是否合法——那是解析器干的事。

1.2 用正则解析JSON

JSON的结构其实很简单:键值对,用花括号包裹。我们最常见的需求是提取某个字段的值

举个例子,假设有这段JSON:

{
  "name": "张三",
  "age": 28,
  "city": "北京"
}

我想提取name字段的值。正则怎么写?

"name"\s*:\s*"([^"]*)"

解释一下:

  • "name":匹配字段名
  • \s*:\s*:匹配冒号及可能的空格
  • "([^"]*)":匹配双引号内的值,用捕获组提取

我在项目中遇到过一个问题:JSON里的字符串值可能包含转义引号,比如"say: \"hello\""。这时候上面的正则就失效了。改进版:

"name"\s*:\s*"((?:[^"\\]|\\.)*)"

这个正则考虑了转义字符,能正确匹配带引号的值。

小技巧:如果JSON是压缩格式(没有空格),去掉\s*即可。我经常写两个版本,一个给格式化JSON用,一个给压缩JSON用。

1.3 用正则解析XML

XML比JSON麻烦一点,因为有嵌套标签和属性。但如果我们只是提取标签内的文本,正则依然好用。

比如这段XML:

<person>
  <name>李四</name>
  <age>32</age>
</person>

提取name标签内容的正则:

<name>(.*?)</name>

这里用了.*?(非贪婪匹配),确保只匹配到第一个</name>就停下。

如果要提取属性值呢?比如:

<person id="123">

提取id属性:

id\s*=\s*"([^"]*)"

曾经踩过的坑:XML里可能有命名空间,比如<ns:name>。我一开始没注意,写死了标签名,结果解析全失败。后来改成<[^:]*:name>(.*?)</[^:]*:name>才搞定。

1.4 用正则解析YAML

YAML的语法更自由,缩进敏感。但正则依然能处理简单的键值对

比如:

name: 王五
age: 25
city: 上海

提取name值的正则:

^name:\s*(.*)$

注意这里用了^$锚定行首行尾,因为YAML是逐行解析的。

YAML还支持列表:

hobbies:
  - 读书
  - 游泳

提取列表项:

^\s*-\s*(.*)$

个人经验:YAML的嵌套结构用正则解析很吃力。我一般只用来提取顶层键值对,深层嵌套还是交给解析库。说白了,正则适合浅层提取

1.5 用正则解析自定义格式

这才是正则真正的用武之地。很多老系统、日志文件、配置文件,用的都是非标准格式。这时候解析库帮不上忙,正则就是你的瑞士军刀。

举个例子,我曾经处理过这样的日志:

[2024-01-15 10:30:45] [INFO] 用户登录成功 - user_id=12345, ip=192.168.1.1

提取时间、级别、用户ID和IP:

\[(.*?)\]\s*\[(.*?)\]\s*.*?user_id=(\d+),\s*ip=([0-9.]+)

这个正则一次提取4个字段,效率很高。

再比如,自定义配置文件:

# 数据库配置
DB_HOST = localhost
DB_PORT = 3306
DB_USER = root

提取键值对(忽略注释):

^\s*([A-Z_]+)\s*=\s*(.*)$

建议:写自定义格式的正则时,先写注释说明每一部分匹配什么。不然过两周你自己都看不懂。我吃过这个亏。

1.6 知识体系总览

下面这张图总结了本章的核心逻辑:

正则表达式解析数据格式 — 知识体系 正则表达式 JSON 解析 XML 解析 YAML 解析 自定义格式 提取字段值 处理转义字符 压缩/格式化兼容 提取标签内容 提取属性值 处理命名空间 键值对提取 列表项提取 缩进处理(浅层) 日志格式解析 配置文件解析 非标准格式适配 适用场景:轻量提取、日志分析、资源受限环境

1.7 实战建议与避坑指南

最后,分享几条我这些年总结的经验:

  1. 先写测试用例:写正则前,先准备好输入和期望输出。我每次都是先写测试,再写正则。
  2. 注意贪婪与非贪婪:默认是贪婪匹配,容易匹配过多。用.*?代替.*能避免很多问题。
  3. 考虑边界情况:空值、特殊字符、换行符——这些最容易翻车。
  4. 性能问题:复杂的正则回溯可能很慢。如果数据量大,考虑分段解析。
  5. 不要过度使用:如果格式嵌套超过3层,或者需要验证语法正确性,请用解析库。

我曾经犯过的错:有一次用正则解析嵌套JSON,写了100多字符的正则,结果一跑就栈溢出。后来老老实实改成逐层解析,代码反而更清晰。记住:正则不是万能的

好了,这一章就到这里。正则解析数据格式,说白了就是用模式匹配去提取你关心的信息。掌握好这个思路,后面遇到什么格式都不慌。


公众号:蓝海资料掘金营,微信 deep3321