C#、Unity中正则表达式 Regex类的使用

起因:在项目中使用正则匹配一些网络协议,新增协议A, 表面上看和之前的书写格式一致,但原始的正则表达式始终无法匹配到A的内容,于是专程了解一下正则的使用和解析,最后找到原因是没有兼容 Windows 换行符 和 Linux 换行符。刚好这部分不熟,就记录一下自己的解惑过程。

关于正则,直接看官方介绍吧:正则表达式语言 - 快速参考 | Microsoft Learn
基本所有的问题都能从官方的描述中得到答案,但为了节约时间。
以下我通过一个例子来描述我遇到的问题和得到的结论,以及使用的方法。

下面这个部分是我需要正则匹配的字符串的结构/实际操作中我们在同一字符串中会包含多个以下的结构

// 创建请求 req
struct TestCreateRq {
    string Name;            // 名称
    i32 Icon;               // 图标
    i32 Type;               // 条件类型 
    i32 requirementParam;   // 条件参数
}

下面这个部分是我使用的正则表达式/我想要做的事情是把字符串中所有的上方的结构体找出来,然后把结构体的描述和结构体的名称打印出来

        //这里是我们使用的匹配规则
         Regex reg = new Regex(@"(//.*\r\n)?struct\s*(\w*)\s*{\r\n([\w\W]*?)}\s*", RegexOptions.Multiline);
         //这里content是我们用来匹配的字段,里面包含多个上面的结构体
        var matches = reg.Matches(content);
        foreach (Match item in matches)
        {
            Debug.Log(item.Result("$1").Replace("//", "").Replace("\r\n", "");
            Debug.Log(item.Result("$2"));
        }

我们直接看匹配规则部分

关于正则的问题
问题1:字符串前面的@是什么意思
答:@在c#中为强制不转义的符号,在里面的转义字符无效。
例:

Console.WriteLine("你好\t吗?");
//输出结果为:你好  吗?
Console.WriteLine(@"你好\t吗");
//输出结果为:你好\t吗?

问题2:正则中的( )是什么意思
答:正则表达式的分组结构正则表达式中的分组构造 | Microsoft Learn

问题3:RegexOptions.Multiline 是什么
答:正则匹配规则枚举RegexOptions 枚举 (System.Text.RegularExpressions) | Microsoft Learn

问题4: "$1" "$2" 是什么
答:正则表达式中的替代正则表达式中的替代 | Microsoft Learn
其实这里结合上下应该看的出来,这里表示的具体内容即为上面()内分组的内容,分别对应组1和组2
但注意实际描述为:$number 语言元素包括替换字符串中 number 捕获组所匹配的最后一个子字符串,其中 number 是捕获组的索引。 例如,替换模式 $1 指示匹配的子字符串将由捕获的第一个组替换。
所以实际你$1获取的内容应该是 第一个匹配组 (//.*\r\n) 最后匹配的那个字符串,$2获取的内容应该是 第二个匹配组 (\w*) 最后匹配的那个字符串。

以上是常见问题的一些解答。

关于正则的解析/使用
使用和解析其实都是一个东西,这里做解析只是把使用的过程反推过来。不过无论是使用还是反推都需要字符对照。


 

下面开始对照上面的释义来做解析

@"(//.*\r\n)?struct\s*(\w*)\s*{\r\n([\w\W]*?)}\s*"

1,@,参考上文描述
2,组1(//.*\r\n)解析,这部分匹配的内容为 // 创建请求 req

(1)//对应//
(2). 匹配除“\n”之外的任何单个字符
(3)* 匹配前面的零次或多次的子表达式
(4)\r\n匹配一个回车符,匹配一个换行符
(5)整体匹配的结构为// + 零或多个除“\n”之外的任何单个字符 + 回车换行 ,这就和// 创建请求 req 这个部分匹配上了
3,?匹配前面的零次或一次的子表达式
即前面这个 // 创建请求 req 结构最多匹配到一个,没有也无所谓。
4,struct\s*(\w*)\s* 解析,这部分匹配的内容为 struct TestCreateRq

(1)struct 对应 struct
(2)\s 匹配任何空白字符,包括空格、制表符、换页符等
(3)* 匹配前面的零次或多次的子表达式
(4)(\w*) 组2,表达式为 \w* 匹配零次或多次包括下划线的任何单词字符。
(5)\s* 匹配零次或多次任何空白字符,包括空格、制表符、换页符等
(6)整体匹配的结构为struct + 零次或多次任何空白字符,包括空格、制表符、换页符等 + 零次或多次包括下划线的任何单词字符 +零次或多次任何空白字符,包括空格、制表符、换页符等 ,这就和struct TestCreateRq这个部分匹配上了
5,{\r\n([\w\W]*?)}\s* 解析,这部分匹配的内容为
{ string Name; // 名称 i32 Icon; // 图标 i32 Type; // 条件类型 i32 requirementParam; // 条件参数 }

(1){ 对应 {
(2)\r\n匹配一个回车符,匹配一个换行符
(3)([\w\W]*?) 组3 尽可能少的字符串中(?当该字符紧跟在其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式尽可能少的匹配所搜索的字符串) 匹配零次或多次( *) 包括下划线的任何单词字符(\w)和非单词字符(\W)的字符集合([])。
(4)} 对应 }
(5)\s* 匹配零次或多次任何空白字符,包括空格、制表符、换页符等
(6)整体匹配的结构为 { + 回车换行 +尽可能少的字符串中匹配零次或多次 包括下划线的任何单词字符和非单词字符的字符集合+ } +零次或多次任何空白字符,包括空格、制表符、换页符等 ,也和我们的匹配内容对上了。
以上是对我所使用的正则表达式的解析,难点在于要理解*?产生的非贪婪匹配的一个执行顺序,在非贪婪匹配的情况下会优先匹配*?后面的规则。

反推我们只知道了含义。
那么如果是写呢,下面以// 创建请求 req 写一个例子

(1)首先 //
(2)中间有4个字符也可以使用.*
(3)后面有个req
(4)最后可能也有回车换行加上\r\n 如果是个组的话再加上()
(5)结果就是 (//.*req\r\n)
当然也可以匹配上,但为什么写法不一致呢?这就和使用的通用性相关。比如你想匹配 // 创建请求 res 或者 // 啦啦啦 或者 // 啦 // 啦 // 啦 使用现在的匹配方式就匹配不到。这里其实可以理解为模糊搜索,模糊程度越高通用性越强。在我们使用的过程中,在满足需求的情况下尽可能提高通用性最好。
以上例子抛砖引玉。现在回归到我们自己的问题。

我在使用@"(//.*\r\n)?struct\s*(\w*)\s*{\r\n([\w\W]*?)}\s*" 匹配下面结构的时候却匹配不到。

struct TestCreateRq {
    string Name;            // 名称
    i32 Icon;               // 图标
    i32 Type;               // 条件类型 
    i32 requirementParam;   // 条件参数
}

不卖关子,最后查到的结果是因为提供的代码格式不一致,因为 Windows /r/n换行符 和 Linux /n 换行符不同。导致虽然代码看起来一毛一样。但在匹配时却不能正常匹配。

那么我们怎么兼容两种换行符呢?

参照上面的释义,我们需要在判断/r/n的地方兼容判断 /n

所以可以改成如下写法

    //即在换行符中间增加`\r`和`\n`之间增加`?`  即`\r`最多匹配到一个,没有也无所谓。
     Regex reg = new Regex(@"(//.*\r?\n)?struct\s*(\w*)\s*{\r?\n([\w\W]*?)}\s*", RegexOptions.Multiline);

最后贴个大佬推荐的在线验证正则表达式的网站,方便验证和测试 在线验证正则表达式RegExr: Learn, Build, & Test RegEx

以上。
————————————————

原文链接:https://blog.csdn.net/qq_39860954/article/details/126784143

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值