如何用Perl正则表达式可选匹配配对引号?
匹配带可选配对引号的配置项格式
我需要用单行正则处理文件内容替换,sed满足不了需求,改用Perl。目前能匹配setting = value # comment格式,拆分出setting = 、value和 # comment三个部分,示例命令:
perl -lne 'print "-1-$1-2-$2-3-$3-E-" if /^\s*(setting\s*=\s*)([^\s#]+)(\s*#\s*.*)?/'
执行后输出类似-1-setting = -2-value-3- # comment-E-。
现在遇到的问题是:value可能被单引号(')或双引号(")包裹,且前后引号必须一致。我尝试用反向引用匹配,但命令:
perl -lne 'print "-1-$1-2-$2-3-$3-4-$4-5-$5-E-" if /^\s*(setting\s*=\s*)(\N{APOSTROPHE}|\N{QUOTATION MARK})?([^\s#]+)(\2)?(\s*#\s*.*)?/'
存在问题:组3会捕获闭合引号,组4为空;而且Perl不支持在组3的否定断言里用反向引用。
我的思路方向:
- 有没有匹配非指定字符串的断言形式?
- 能否创建单个字符的捕获组,让它能在否定字符集中通过反向引用调用?
- 能否引用反向引用的第一个字符?
- 能否把反向引用当作字符集纳入否定集合?
- 用环视断言,但只有正/反向环视,没有“当前环视”。
现在需要解决:Perl正则怎么实现这种可选配对引号的匹配?
解决方案:分支匹配+反向引用
可以通过分支结构分别处理「不带引号」和「带配对引号」的情况,同时用反向引用确保引号一致:
perl -lne 'print "-1-$1-2-$2-3-$3-E-" if /^\s*(setting\s*=\s*)(?:([^\s#"\']+)|(["\'])(.*?)\3)(\s*#\s*.*)?/'
正则解释:
^\s*(setting\s*=\s*):匹配配置项的前缀部分,捕获到组1(setting =)。(?:...):非捕获组,用来包裹两种情况的分支:([^\s#"\']+):第一种情况——不带引号的value,捕获到组2,匹配内容是不含空格、#、单双引号的字符序列。(["\'])(.*?)\3:第二种情况——带配对引号的value:(["\']):捕获开头的单/双引号到组3;(.*?):非贪婪匹配引号内的内容(避免匹配到后面的引号),捕获到组2;\3:反向引用组3的引号,确保结尾引号和开头一致。
(\s*#\s*.*)?:可选的注释部分,捕获到组4(对应原来的组3)。
测试效果:
- 对于
setting = value # comment,输出-1-setting = -2-value-3- # comment-E- - 对于
setting = 'quoted value' # comment,输出-1-setting = -2-quoted value-3- # comment-E- - 对于
setting = "another value" # comment,输出-1-setting = -2-another value-3- # comment-E-
优化点说明:
- 用非捕获组
(?:...)分支处理两种情况,避免多余的捕获组干扰; - 非贪婪匹配
.*?确保引号内的内容不会跨引号匹配; - 反向引用
\3严格保证前后引号一致,不匹配单双引号混用的情况(比如'value"会被视为不匹配)。
如果需要支持引号内转义(比如"value with \"quote\""),可以把引号内的匹配部分改成(.*?(?<!\\)),不过如果你的场景不需要转义,上面的正则足够用。
内容的提问来源于stack exchange,提问作者Bernd Wechner
相关产品推荐
相关产品推荐

