使用正则表达式解决配置文件字符串替换的思路和例子
正则表达式是非常强大的字符串处理工具,但由于晦涩难懂,唯有不断的学习和使用,才能积累经验。我使用正则表达式总是断断续续,所以水平也很初级。下面就记录这次的使用经验,备查。
下面的xml代码是一个游戏技能配置文件的简化版,其中的items是一个技能,item是该技能的一个级别的值。desc属性是该技能的介绍文本。由于介绍中包含对技能的效果的引用,而技能的效果在不同的技能级别中的值是不同的,因此这里使用定界符来标识可能会变动的值。
针对定界符,我制定的规则很简单,用花括号 {}
包含要替换的属性值即可,如 {key}
,其中key就是属性名称。对于使用数组方式提供的属性值,则使用 {key[n]}
的方式来提供,其中n是数组的索引。
程序中要做的,就是在游戏中需要技能的介绍的时候,会根据技能的级别获取到相关的的值,然后查询desc中有哪些定界符,再根据定界符获取到item中的值,最后进行替换。
这个工作虽然并不复杂,但也不是indexof能够解决的,后面就是我使用正则表达式的处理思路。对于XML的解析,我使用E4X。
1<skill>
2 <items id="2121" name="魔能烧烬" targetSide="2" type="special1" element="2" desc="主动技能。使魔导师能够掌握魔法精密之处,从而对目标造成{effect[0]}点的火元素伤害,同时减少目标{effect[1]}%MP上限。">
3 <item lv="1" effect="[20,4]" targetNum="1" expended="600" cd="20000" />
4 <item lv="2" effect="[40,6]" targetNum="1" expended="640" cd="20000" />
5 </items>
6 <items id="1203" name="无情杀戮" targetSide="2" type="phy" element="-1" desc="主动技能。没有一丝怜悯的连续两次猛击目标要害,对目标造成{effect}点的物理伤害。">
7 <item lv="1" effect="1114" targetNum="1" expended="50" cd="13000" />
8 <item lv="2" effect="1180" targetNum="1" expended="60" cd="13000" />
9 </items>
10</skill>
一、检测值是否是数组形式
在这里,由于值都是整数,出现的也值可能是整数数组,所以正则表达式写起来就简单许多:
1public static function isArray($str:String):Boolean
2{
3 $str = $str.replace(/\s/g, '');
4 var _reg:RegExp = /^\[(\d+,?)+\d+\]$/;
5 return _reg.test($str);
6}
先使用replace将字符串中的空格剔除,然后检测如 [int,int...]
的格式。这个正则原理如下:
(\d+,?)+
匹配以逗号分隔的整型字符串,例如“123,456”或者“1”这样的格式- 第二个
\d+
则确保123,
这样的字符串不被匹配 - 其他的就很简单,不说了
二、将字符串转成数组
先剔除空格,再剔除方括号,然后通过定界符将字符串转成数组,并转换成int存储。其中重点是字符类 []
的运用。由于要查找的正好是方括号,而正则表达式中也是用方括号来定义字符类的,因此方括号在字符类中,必须使用转义符 \
进行转义才能使用。
1public static function toArray($str:String, $delimiter:String=','):Array
2{
3 $str = $str.replace(/\s/g, '');
4 $str = $str.replace(/[\[\]]/g, '');
5 var __arr:Array = $str.split($delimiter);
6 for(var i:int=0; i<__arr.length; i++)
7 __arr[i] = int(__arr[i]);
8 return __arr;
9}
三、将XML中的其他值存入一个对象
为了方便后面提取值,将XML中的所有属性存入一个对象中,这里为了演示方便,只存储了effect属性:
1public function getSkillVO($id:String, $lv:String):Object
2{
3 var __itemxml:XML = _skill.items.(@id==$id).item.(@lv==$lv)[0];
4 var __vo:Object = {};
5 var __effect:String = __itemxml.@effect.toString();
6 if(isArray(__effect))
7 __vo.effect = toArray(__effect);
8 else
9 __vo.effect = int(__effect);
10 return __vo;
11}
四、替换
重要的是String的match方法的作用。这个方法对没有使用global标志 /g
的正则表达式,会将其中的每个组的匹配结果作为一个元素加入到匹配结果数组中。这样,就可以方便的提取到形如 {effect[0]}
的字符串中的数组索引值。
所有的分析都写在注释中了:
1//检测字符串中是否有如{delimter}和{delimter[n]}形式的定界符,并使用对应的值进行替换
2private function replaceDesc($str:String, $skillvo:Object):String
3{
4 //支持全局匹配的正则
5 var __globalReg:RegExp = /{(\w+)(\[(\d)\])?}/g;
6 //用于匹配每个全局匹配结果的正则
7 var __itemReg:RegExp = /{(\w+)(\[(\d)\])?}/;
8 //全局匹配的结果
9 var __globalMatch:Array = $str.match(__globalReg);
10 //待替换的键名数组
11 var __keys:Array = [];
12 //待替换的值数组
13 var __values:Array = [];
14
15 var __itemMatch:Array = null;
16 var __itemIsArray:Boolean = false;
17 var __itemKey:String = '';
18 for each(var __str:String in __globalMatch)
19 {
20 __itemMatch = __str.match(__itemReg);
21 /*按照正则表达式规则,__itemMatch应该是有4个元素的数组:
22 1 整个字符串,2 第一个括号(\w+)的内容,3 第二个括号(\[(\d)\])的内容,4 第三个括号(\d)的内容
23 因此,如果第4个元素为undefined,就说明在字符串中,没有数组的定界符*/
24 __itemIsArray = __itemMatch[3] != undefined;
25 //第2个元素就是去掉了花括号和数组定界符(如果有)的字符串,也就是要$skillvo中的变量名
26 __itemKey = __itemMatch[1];
27 //第1个元素是整个大字符串中的要被替换的部分,包含花括号
28 __keys.push(__itemMatch[0]);
29 //如果值是数组中的元素,就是用数组中对应索引的值
30 if(__itemIsArray)
31 __values.push($skillvo[__itemKey][__itemMatch[3]]);
32 //否则,就直接使用变量的值
33 else
34 __values.push($skillvo[__itemKey]);
35 }
36 //开始替换
37 for(var j:int=0; j<__keys.length; j++)
38 {
39 $str = $str.replace(__keys[j], __values[j]);
40 }
41 return $str;
42}
全部代码
1package
2{
3import flash.display.Sprite;
4
5public class SkillRegExpTest extends Sprite
6{
7 public function SkillRegExpTest()
8 {
9 trace(replaceDesc(_skill.items.(@id=="2121").@desc.toString(), getSkillVO('2121', '2')));
10 }
11
12 private var _skill:XML=
13 <skill>
14 <items id="2121" name="魔能烧烬" targetSide="2" type="special1" element="2" desc="主动技能。使魔导师能够掌握魔法精密之处,从而对目标造成{effect[0]}点的火元素伤害,同时减少目标{effect[1]}%MP上限。">
15 <item lv="1" effect="[20,4]" targetNum="1" expended="600" cd="20000" />
16 <item lv="2" effect="[40,6]" targetNum="1" expended="640" cd="20000" />
17 </items>
18 <items id="1203" name="无情杀戮" targetSide="2" type="phy" element="-1" desc="主动技能。没有一丝怜悯的连续两次猛击目标要害,对目标造成{effect}点的物理伤害。">
19 <item lv="1" effect="1114" targetNum="1" expended="50" cd="13000" />
20 <item lv="2" effect="1180" targetNum="1" expended="60" cd="13000" />
21 </items>
22 </skill>
23
24 /**
25 * 检测一个字符串是否是[int,int...]的形式
26 */
27 public function isArray($str:String):Boolean
28 {
29 //将字符串中的空格剔除
30 $str = $str.replace(/\s/g, '');
31 var _reg:RegExp = /^\[(\d+,?)+\d+\]$/;
32 return _reg.test($str);
33 }
34
35 /**
36 * 将一个符合数组格式的字符串转换成整型数组
37 */
38 public function toArray($str:String, $delimiter:String=','):Array
39 {
40 $str = $str.replace(/\s/g, '');
41 $str = $str.replace(/[\[\]]/g, '');
42 var __arr:Array = $str.split($delimiter);
43 for(var i:int=0; i<__arr.length; i++)
44 {
45 __arr[i] = int(__arr[i]);
46 }
47 return __arr;
48 }
49
50 public function getSkillVO($id:String, $lv:String):Object
51 {
52 var __itemxml:XML = _skill.items.(@id==$id).item.(@lv==$lv)[0];
53 var __vo:Object = {};
54 var __effect:String = __itemxml.@effect.toString();
55 if(isArray(__effect))
56 __vo.effect = toArray(__effect);
57 else
58 __vo.effect = int(__effect);
59 return __vo;
60 }
61
62 /**
63 * 检测字符串中是否有如{delimter}和{delimter[n]}形式的定界符,并使用对应的值进行替换
64 * @param $str 被替换的完整字符串
65 * @param $skillvo 包含要替换的内容的
66 * @return
67 *
68 */
69 private function replaceDesc($str:String, $skillvo:Object):String
70 {
71 //支持全局匹配的正则
72 var __globalReg:RegExp = /{(\w+)(\[(\d)\])?}/g;
73 //用于匹配每个全局匹配结果的正则
74 var __itemReg:RegExp = /{(\w+)(\[(\d)\])?}/;
75 //全局匹配的结果
76 var __globalMatch:Array = $str.match(__globalReg);
77 //待替换的键名数组
78 var __keys:Array = [];
79 //待替换的值数组
80 var __values:Array = [];
81
82 var __itemMatch:Array = null;
83 var __itemIsArray:Boolean = false;
84 var __itemKey:String = '';
85 for each(var __str:String in __globalMatch)
86 {
87 __itemMatch = __str.match(__itemReg);
88 /*按照正则表达式规则,__itemMatch应该是有4个元素的数组:
89 1 整个字符串,2 第一个括号(\w+)的内容,3 第二个括号(\[(\d)\])的内容,4 第三个括号(\d)的内容
90 因此,如果第4个元素为undefined,就说明在字符串中,没有数组的定界符*/
91 __itemIsArray = __itemMatch[3] != undefined;
92 //第2个元素就是去掉了花括号和数组定界符(如果有)的字符串,也就是要$skillvo中的变量名
93 __itemKey = __itemMatch[1];
94 //第1个元素是整个大字符串中的要被替换的部分,包含花括号
95 __keys.push(__itemMatch[0]);
96 //如果值是数组中的元素,就是用数组中对应索引的值
97 if(__itemIsArray)
98 __values.push($skillvo[__itemKey][__itemMatch[3]]);
99 //否则,就直接使用变量的值
100 else
101 __values.push($skillvo[__itemKey]);
102 }
103 //开始替换
104 for(var j:int=0; j<__keys.length; j++)
105 {
106 $str = $str.replace(__keys[j], __values[j]);
107 }
108 return $str;
109 }
110}
111}
- 文章ID:1233
- 原文作者:zrong
- 原文链接:https://blog.zengrong.net/post/regexp-config-files-replace/
- 版权声明:本作品采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可,非商业转载请注明出处(原文作者,原文链接),商业转载请联系作者获得授权。