如何用Perl正则表达式验证保龄球得分字符串?
保龄球得分字符串的Perl正则验证方案
纯正则表达式方案(依赖Perl高级特性)
可以用一个正则表达式整合所有校验规则,包括帧数量、前9帧规则和第10帧规则。注意该方案需要启用Perl的re 'eval'特性,因为用到了嵌入式代码断言。
use re 'eval'; # 必须启用该特性才能使用嵌入式代码断言 my $gs = 'X-91-55-72-X-X-X-90-82-91X'; my $valid = 0; if ($gs =~ /\A(?:(?:X|\d{2}(?<=(\d)\d)(?{ $1 + $^N <= 10 }))-){9}(?:XXX|XX\d|X\d{2}(?<=(\d)\d)(?{ $2 + $^N <= 10 })|\d{2}(?<=(\d)\d)(?{ $3 + $^N < 10 })|\d{2}(?<=(\d)\d)(?{ $4 + $^N == 10 })(?:X|\d))\z/) { $valid = 1; } print $valid ? "有效" : "无效";
正则拆解
前9帧匹配:
(?:(?:X|\d{2}(?<=(\d)\d)(?{ $1 + $^N <= 10 }))-)X:匹配单字符的全中帧\d{2}(?<=(\d)\d)(?{ $1 + $^N <= 10 }):匹配两个数字,通过后向捕获组和嵌入式代码校验两数之和≤10($1是第一个数字,$^N是第二个数字)- 整个前9帧部分重复9次,每次后接分隔符
-
第10帧匹配:
(?:XXX|XX\d|X\d{2}(?<=(\d)\d)(?{ $2 + $^N <= 10 })|\d{2}(?<=(\d)\d)(?{ $3 + $^N < 10 })|\d{2}(?<=(\d)\d)(?{ $4 + $^N == 10 })(?:X|\d))XXX:三次全中场景XX\d:两次全中加0-9数字场景X\d{2}(?<=(\d)\d)(?{ $2 + $^N <= 10 }):一次全中加两数之和≤10的数字场景\d{2}(?<=(\d)\d)(?{ $3 + $^N < 10 }):两数之和小于10的无补投场景\d{2}(?<=(\d)\d)(?{ $4 + $^N == 10 })(?:X|\d):两数之和等于10的补中场景,后面接全中或数字
拆分校验方案(可读性更强)
如果不想依赖Perl的高级正则特性,可以先拆分帧,再逐个校验规则,可读性和可调试性更好:
my $gs = 'X-91-55-72-X-X-X-90-82-91X'; my $valid = 0; # 拆分帧 my @frames = split /-/, $gs; # 校验帧数量是否为10 if (@frames == 10) { my $all_valid = 1; # 校验前9帧 for my $frame (@frames[0..8]) { if ($frame ne 'X') { # 必须是两个数字 unless ($frame =~ /^(\d)(\d)$/) { $all_valid = 0; last; } # 校验两数之和≤10 unless ($1 + $2 <= 10) { $all_valid = 0; last; } } } # 校验第10帧 if ($all_valid) { my $tenth = $frames[9]; if ( $tenth =~ /^XXX$/ || $tenth =~ /^XX\d$/ || ($tenth =~ /^X(\d)(\d)$/ && $1 + $2 <= 10) || ($tenth =~ /^(\d)(\d)$/ && $1 + $2 < 10) || ($tenth =~ /^(\d)(\d)(X|\d)$/ && $1 + $2 == 10) ) { $valid = 1; } } } print $valid ? "有效" : "无效";
方案说明
- 先通过
split /-/拆分所有帧,校验数量是否为10 - 遍历前9帧,每帧要么是
X,要么是两个数字且和≤10 - 单独校验第10帧,匹配所有合法场景
内容的提问来源于stack exchange,提问作者rollstreet
相关产品推荐
相关产品推荐

