使用正则匹配去除重复字段

正则表达式这个语法以简单高效著称,但是有一点i就是比较复杂。

近期有个功能需要去除字符串中重复的部分,由于使用正常的传统的那种舒服拆分成数组在一个一个匹配的方式感觉有点效率过低浪费性能,于是便想到了正则表达式来匹配重复的字符串,并利用String自导的replaceAll方法来将相应的字符串给删除掉。

目标字符串:

"10110,10254,10554,11025,10254,11002,11532,10110,";

这是一个数字数组,但是这里面有某些ID是重复的,我必须将之去除掉。由于我对正则语法实在不熟悉,都是临时对着说明文档在使用的阶段。没有比较好的思路,所以去网上查了些思路。

发现一个类似的解决方案,去除重复的字母具体代码如下:

String str = "aaaaaaahdbkjsdnfkjsndjkfsdfsdf";   
        str = str.replaceAll("(?s)(.)(?=.*\\1)", "");   
        System.out.println(str);
大致看了下说明意思。

(?s) 开启单行模式 DOTALL 让. 号匹配任意字符 
(.) 任意字符 并捕获在第一组 
(?=.*\1) 这是断言, 表示后面内容将是 任意个字符加上第一组所捕获的内容 
 

第一条:开启正则的某些模式,感觉跟我没多少关系,我这是要去除重复的字符串,他这是去字母。

从第二条看过去来看,关键应该是分组,大致是思路我有点印象了,还是说匹配某个字符串,然后添加了个断言,第三条就是那个断言,也就是说将当前字符串做一个分组,放到断言里去了之后,匹配重复的部分。然后我对着将我的需求修改了一下。

String reg = "(\\d{5},)(?=(\\d{5},)*\\1)";

具体代码如下

		String str = "abcdeabcdeabcdeaaaaaadddddceeeeabcccccccacadaeec"; 
		String str1 = "10110,10254,10554,11025,10254,11002,11532,10110,";
		String reg = "(\\d{5},)(?=(\\d{5},)*\\1)";
		str = str.replaceAll("(?s)(.)(?=.*\\1)", ""); 
		str1 = str1.replaceAll(reg, "");
//		str = str.replaceAll(reg, ""); 
		System.out.println(str1); 
一开始是断言里面没有加 * 导致匹配失败,我猜测如果不加的话将会只匹配一组了。

但是后来我发现这个里面有个限制,就是该字符串数组就会被我限制在5个里面,如果其中有某一条字符串超过了5位数,则会导致匹配失败,这可能是个不足,因为我现在在讲6个作为一组,然后一组一组的匹配的,所以才会有这个问题,如果是其他的话,应该就不会有这个问题了。

OK,暂时算是解决问题了。这个断言还是有点不好理解,感觉有点预言性质,但是又不是进入实际的匹配对象里面去,主要是用处大概是对当前是匹配对象做进一步的限制和条件,以增强匹配的正确性。这个可能需要做进一步的测试才能得知。先做到这了,断言还有很多用途(?<=exp)、(?=exp)、(?!exp)、(?<!exp)等多种方式,每一种还会有各种细微的差别,这个先下去研究,等有个靠谱的结论了在说好了。

<think> 根据用户需求,我们需要在Unity中编写一个工具方法,该方法接收两个参数:一个JSON字符串和一个字段名(字符串)。然后使用正则表达式从JSON字符串中提取该字段对应的值,并返回这个值。 需要注意的是,JSON字符串中字段名和值之间用冒号分隔,值可能是字符串、数字、布尔值、对象、数组等。但这里我们假设用户想要提取的是基本类型(字符串、数字、布尔值)的值,并且我们只考虑字段名出现在双引号中,后面紧跟冒号,然后是要提取的值。 但是,由于JSON的结构可能很复杂(比如嵌套对象、数组等),使用正则表达式完全解析JSON是不推荐的,因为正则表达式不能处理递归结构。不过,如果用户能保证要提取的字段出现在最外层,并且没有重复字段名,那么使用正则表达式可能是可行的。 然而,用户明确要求使用正则表达式,所以我们先按照这个要求来实现。 设计思路: 1. 构造一个正则表达式匹配:`"字段名": 后面跟着的值`。值可能是: - 字符串: 由双引号包围的内容,例如 `"value"` - 数字: 例如 123, 45.67 - 布尔值: true或false - null 2. 注意JSON中的字符串值可能包含转义字符,但为了简化,我们可以先不考虑复杂的转义情况(比如字符串内包含双引号),或者我们可以匹配非转义的双引号之间的内容。 但是,为了处理字符串值中的转义双引号(例如`\"`),我们需要更复杂的处理。不过,由于我们的需求是提取指定字段,且假设该字段的值是基本类型,我们可以尝试匹配到下一个逗号、大括号或中括号(根据JSON的结构)之前的内容。 考虑到这些,我们可以设计如下的正则表达式正则模式:`\"字段名\"\s*:\s*(?<value>[^,\]}]+)` 这个模式尝试匹配: - 双引号括起来的字段名 - 后面可能有空白(\s*) - 冒号 - 后面可能有空白(\s*) - 然后匹配一个分组(命名为value),这个分组匹配除了逗号、]和}以外的任意字符(直到遇到这些符号,我们认为一个字段的值结束了) 但是,这个模式不能正确匹配字符串值(因为字符串值中可能包含逗号、空格等),也不能匹配对象和数组。 因此,我们需要更强大的正则表达式来处理字符串值。我们可以使用这样的模式:先匹配字段名,然后匹配冒号,然后匹配一个JSON值(但用正则匹配任意JSON值很难)。 另一种思路:如果我们知道要提取的字段的值是字符串,那么我们可以专门匹配字符串值。但用户可能想要提取其他类型。 考虑到复杂度,我们可能只处理基本类型(字符串、数字、布尔值、null),并且对于字符串,我们使用匹配双引号字符串的模式(考虑转义双引号)。 这里我们参考一个常见的匹配双引号字符串的正则:`"(?:[^"\\]|\\.)*"`,它可以匹配带有转义双引号的字符串。 因此,我们可以构造一个正则表达式匹配字段名,然后匹配冒号,然后匹配以下之一: - 字符串:`"(?:[^"\\]|\\.)*"` - 数字:`-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?` - 布尔值:`true|false` - null: `null` 然后我们将这些组合起来。 正则表达式模式:`"字段名"\s*:\s*(?:"(?:[^"\\]|\\.)*"|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null)` 注意:字段名需要转义,因为用户提供的字段名可能有正则特殊字符,所以我们需要对字段名进行转义。 但是,这个正则表达式可能会匹配到嵌套在对象或数组中的同名字段,所以它可能不是完全准确的。但用户如果知道字段在最外层,就可以使用。 步骤: 1. 对传入的字段名进行正则转义(主要是转义特殊字符)。 2. 构造正则表达式字符串。 3. 使用正则表达式匹配,如果匹配到,则返回匹配到的值字符串(注意:字符串值会包括双引号,我们可以去掉双引号;其他类型直接返回字符串表示,比如数字、布尔值等)。 4. 如果匹配多个?我们只取第一个匹配(或者用户可能希望?)。根据需求,我们返回第一个匹配。 但是,用户可能希望返回的是解析后的值(比如字符串去掉双引号,数字转为数字等),但方法返回类型是字符串,所以我们返回匹配到的原始字符串,或者我们可以根据匹配到的内容进行简单处理(例如,如果是字符串,去掉双引号并处理转义字符?)。但是,处理转义字符可能会复杂,所以我们可以先返回原始匹配的字符串,然后让调用者去解析,或者提供选项。 根据用户需求,我们只返回该字段对应的值的字符串表示(如果是字符串,包括双引号)。为了便于使用,我们可以去掉字符串值两端的双引号,并处理转义字符?但是,由于时间关系,我们先不处理转义,只去掉双引号,对于其他类型直接返回。 我们这样设计: 如果匹配到的值是以双引号开头和结尾,那么我们就去掉双引号,并返回这个字符串(不处理转义,因为转义处理需要额外步骤)。 如果不是,则直接返回字符串(例如数字、布尔值等)。 但是,用户可能希望得到原始JSON中的表示。所以,我们可以提供两种方式,但根据需求,我们先按照去掉双引号的方式返回字符串值,其他类型不变。 另一种做法:返回匹配到的整个值的字符串(包括双引号等),由调用者自行解析。 这里我们选择返回去掉外层双引号的字符串(如果是字符串的话),其他类型(数字、布尔值、null)直接返回其字符串形式(不含引号)。这样,调用者可以直接使用字符串,或者将数字字符串转换为数字。 具体步骤: 1. 使用构造的正则表达式进行匹配,获取第一个匹配项中的value组(注意:我们上面没有分组捕获值,而是整个匹配到了值的部分)。所以我们需要调整正则表达式,将整个值捕获到一个分组中。 调整后的正则:`"字段名"\s*:\s*(?<value>"(?:[^"\\]|\\.)*"|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null)` 2. 如果匹配成功,从分组"value"中取出字符串。 3. 判断这个字符串是否以双引号开头和结尾,如果是,则去掉双引号,并返回(注意:这里我们不对转义字符进行处理,所以字符串内的转义字符(如\")会原样保留,调用者可能需要进一步处理)。 4. 如果不是,直接返回该字符串。 但是,如果字符串内部有转义的双引号,我们这样简单处理会不会有问题?例如:`"field": "a\"b"`,我们匹配到的值是`"a\"b"`,去掉双引号后变成`a\"b`,这仍然是一个转义的字符串,调用者可以使用`System.Text.RegularExpressions.Regex.Unescape`或者`JsonUtility`等来解析,但这不是我们的责任了。 考虑到简单性,我们就按上述方式实现。 另外,我们还需要考虑字段名可能出现在字符串值中?为了避免这种情况,我们可以要求传入的JSON字符串是格式良好的,并且字段名不会出现在除了键以外的位置。正则表达式无法完全避免这种情况,但通常格式良好的JSON中,键都是在双引号内,并且后面有冒号。 实现代码: 注意:在C#中,我们需要对字段名进行转义,使用`Regex.Escape`方法,但注意转义后的字段名中可能会包含反斜杠,而JSON中键是不带反斜杠的,所以我们需要将字段名中的特殊字符转义成普通字符,但是注意:在JSON键中,只有双引号和反斜杠需要转义,而正则表达式中也需要转义,所以我们在构造正则时,对字段名进行正则转义,这样就能匹配JSON字符串中键的原样。 例如:字段名为`a.b`,正则转义后是`a\.b`,这样在正则表达式中就能匹配字符串`"a.b"`。 代码结构: ```csharp using System.Text.RegularExpressions; public static class JsonFieldExtractor { public static string ExtractFieldValue(string json, string fieldName) { // 转义字段名,用于正则表达式 string escapedFieldName = Regex.Escape(fieldName); // 构建正则表达式模式 string pattern = @"""" + escapedFieldName + @"""\s*:\s*(?<value>""(?:[^""\\]|\\.)*""|-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null)"; Regex regex = new Regex(pattern); Match match = regex.Match(json); if (match.Success) { string valueStr = match.Groups["value"].Value; // 如果匹配的值是字符串(以双引号开头和结尾) if (valueStr.StartsWith("\"") && valueStr.EndsWith("\"")) { // 去掉双引号 return valueStr.Substring(1, valueStr.Length - 2); } else { return valueStr; } } return null; // 或者抛出异常,根据需求 } } ``` 但是,这个正则表达式可能会匹配到嵌套结构中的字段,而且如果字段重复,它只会返回第一个匹配。另外,如果JSON字符串很大,正则表达式可能效率不高。 测试用例: 输入:`{"name": "John", "age": 30}` 提取字段"name":返回`John` 提取字段"age":返回`30` 输入:`{"message": "Hello, \"World\"!"}`,提取"message":返回`Hello, \"World\"!`(注意,这里返回的字符串中包含了转义双引号,调用者需要知道这个字符串是转义过的) 如果用户需要处理转义字符,可以在调用此方法后对返回的字符串进行反转义,或者我们在这个方法中处理。我们可以使用`Regex.Unescape`?但是注意,JSON中的转义规则和正则表达式的不完全一样。所以最好使用JSON解析器来处理。但这里我们按照简单的方式。 另外,如果字段不存在,返回null。 考虑到Unity中可能使用Newtonsoft.Json等库,使用正则表达式并不是最好的方式。但是用户明确要求正则表达式,所以我们这样实现。 注意:这个方法有局限性,只适用于简单的、最外层的字段,并且字段值只能是基本类型(字符串、数字、布尔值、null)。对于复杂类型(对象、数组)无法正确匹配。 如果用户需要提取复杂类型,建议使用完整的JSON解析库(如Unity自带的JsonUtility,或Newtonsoft.Json)。 因此,我们在方法注释中需要说明这些限制。 根据上述分析,我们编写如下工具方法。 注意:在Unity中,需要确保使用`System.Text.RegularExpressions`命名空间。 代码实现如下: </think> 在 Unity 中实现一个通过正则表达式从 JSON 字符串中提取指定字段值的工具方法,可以参考以下实现: ```csharp using System.Text.RegularExpressions; public static class JsonFieldExtractor { /// <summary> /// 从JSON字符串中提取指定字段的值 /// </summary> /// <param name="json">JSON字符串</param> /// <param name="fieldName">要提取的字段名</param> /// <returns>字段值字符串,未找到时返回null</returns> public static string GetFieldValue(string json, string fieldName) { // 构造正则表达式匹配 "fieldName": "value" 或 "fieldName": value 格式 string pattern = $@"""{fieldName}""\s*:\s*(""(?<value>[^""]*?)""|(?<value>[^,\}}]+))"; Match match = Regex.Match(json, pattern); if (match.Success && match.Groups["value"].Success) { return match.Groups["value"].Value; } return null; // 未找到字段 } } ``` ### 使用示例 ```csharp string json = @" { ""player"": { ""name"": ""John"", ""score"": 1500, ""inventory"": [""sword"", ""shield""], ""isActive"": true } }"; // 提取字段string name = JsonFieldExtractor.GetFieldValue(json, "name"); // 返回 "John" string score = JsonFieldExtractor.GetFieldValue(json, "score"); // 返回 "1500" string isActive = JsonFieldExtractor.GetFieldValue(json, "isActive"); // 返回 "true" ``` ### 功能说明 1. **正则表达式解析**: - 模式 `$@"""{fieldName}""\s*:\s*(""(?<value>[^""]*?)""|(?<value>[^,\}}]+))"` - 匹配两种格式: - 带双引号的字符串值:`"name": "John"` - 无引号的值:`"score": 1500` 或 `"isActive": true` 2. **返回值处理**: - 字符串值自动去除双引号(如 `"John"` → `John`) - 数字/布尔值直接返回原始字符串 - 未找到字段时返回 `null` 3. **使用注意事项**: - 适用于简单 JSON 结构,复杂嵌套结构建议用 `JsonUtility` - 字段名需完全匹配(区分大小写) - 不解析数组/对象等复杂结构值 - 如果值中包含逗号 `,` 或大括号 `}` 会提前截断 ### 性能优化建议 1. 频繁调用时可预编译正则表达式: ```csharp private static readonly Regex _regex = new Regex( pattern, RegexOptions.Compiled ); ``` 2. 对于大型 JSON 文件,建议使用 Unity 的 `JsonUtility` 或第三方库如 `Newtonsoft.Json` ### 应用场景 - 快速提取配置文件中的简单字段 - 解析简单 API 响应数据[^1] - 游戏开发中处理轻量级 JSON 数据[^2] - 编辑器工具中的简单数据解析[^3]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值