我正在尝试构建一个对输入长度设置限制的正则表达式,但并非所有字符在此长度内都相等。我会把理由放在问题的最底层。举个简单的示例,我们将最大长度限制为12,仅允许a
和b
,但b
计为3个字符。
允许:
aa
(任何小于12的东西都可以。)aaaaaaaaaaaa
(确切地说是12可以)。aaabaaab
(6 + 2 * 3 = 12,很好)。abaaaaab
(仍为6 + 2 * 3 = 12)。不允许是:
aaaaaaaaaaaaa
(13 a
)。bbbba
(1 + 4 * 3 = 13,这太多了。)baaaaaaab
(7 + 2 * 3 = 13,这太多了。)我做了一个相当接近的尝试:
^(a{0,3}|b){0,4}$
这最多可匹配4个群集,可能包含0-3 a
个或b
个。
但是,它无法与我上一个正面示例匹配:abaaaaab
,因为这会强制第一个群集在开头是单个a
,为{{1}消耗第二个群集1}},剩下的只剩下2个集群了,这个集群太长了。
为什么我需要使用正则表达式执行此操作?
它是通过PyQt和QML在Qt中的用户界面。用户可以在此处的文本字段中键入配置文件的名称。此配置文件名称是url编码的(特殊字符由%XX替换),然后保存在用户的文件系统中。当用户键入许多特殊字符(例如中文)时会遇到问题,然后编码为非常长的文件名。事实证明,在17个字符的某个地方,这个文件名对于某些文件系统来说太长了。 URL编码编码为UTF-8,每个字符最多4个字节,文件名中最多12个字符(因为每个字符都有百分比编码)。
对于个人资料名称,16个字符太短。甚至我们的一些默认名称也超过了。我们需要基于这些特殊字符的变量限制。
Qt通常允许您指定Validator以确定文本框中可接受的值。我们尝试实现这样的验证器,但由于PyQt中的错误,导致了上游的段错误。目前似乎无法处理自定义Validator实现。但是,PyQt还公开了三个内置验证器。两个仅适用于数字。第三个是正则表达式验证器,允许您放置一个匹配所有有效字符串的正则表达式。因此需要这种正则表达式。
答案 0 :(得分:6)
鉴于regexp的局限性,没有真正直接的方法可以做到这一点。您将需要测试所有组合,例如十三个b
,最多一个a
,十二个b
最多四个a
,等等上。我们将构建一个小程序来为我们生成这些程序。测试最多四个a
的基本格式为
/^(?=([^a]*a){0,4}[^a]*$)/
我们会写一个小例程为我们创建这些前瞻,给出一些字母以及最小和最大出现次数:
function matchLetter(c, m, n) {
return `(?=([^${c}]*${c}){${m},${n}}[^${c}]*$)`;
}
> matchLetter('a', 0, 4)
< "(?=([^a]*a){0,4}[^a]*$)"
我们可以将这些结合起来测试三个b
,最多三个a
:
/^(?=([^b]*b){3}[^b]*$)(?=([^a]*a){0,3}[^a]*$)/
我们将编写一个函数来创建这样的组合前瞻,它恰好匹配m
c1
次出现n
次c2
次出现{/ 1}}:
function matchTwoLetters(c1, m, c2, n) {
return matchLetter(c1, m, m) + matchLetter(c2, 0, n);
}
我们可以使用它来匹配十二个b
和最多四个a
,总共四十个或更少:
> matchTwoLetters('b', 12, 'a', 1, 4)
< "(?=([^b]*b){12,12}[^b]*$)(?=([^a]*a){0,4}[^a]*$)"
只需为b
的每个计数创建此版本,并将它们放在一起(对于最大计数为12的情况):
function makeRegExp() {
const res = [];
for (let bs = 0; bs <= 4; bs++)
res.push(matchTwoLetters('b', bs, 'a', 12 - bs*3));
return new RegExp(`^(${res.join('|')})`);
}
> makeRegExp()
< "^((?=([^b]*b){0,0}[^b]*$)(?=([^a]*a){0,12}[^a]*$)|(?=([^b]*b){1,1}[^b]*$)(?=([^a]*a){0,9}[^a]*$)|(?=([^b]*b){2,2}[^b]*$)(?=([^a]*a){0,6}[^a]*$)|(?=([^b]*b){3,3}[^b]*$)(?=([^a]*a){0,3}[^a]*$)|(?=([^b]*b){4,4}[^b]*$)(?=([^a]*a){0,0}[^a]*$))"
现在你可以用
进行测试了makeRegExp().test("baabaaa");
对于长度= 40的情况,regxp长度为679个字符。一个非常粗略的基准测试显示它在不到一微秒的时间内执行。
答案 1 :(得分:1)
尝试使用以下内容:
^((a{1,3}|b){1,4}|(a{1,4}|a?b|ba){1,3}|((a{2,3}|b){2}|aaba|abaa){2})$
示例:https://regex101.com/r/yTTiEX/6
这将其分解为逻辑可能性:
4个部分,每个部分的值最多为3 3个部分,每个部分的值最多为4 2个部分,每个部分的值最多为6个。
答案 2 :(得分:1)
如果要在存在多字节编码时计算字节数,可以使用此功能:
function bytesLength(str) {
var s = str.length;
for (var i = s-1; i > -1; i--) {
var code = str.charCodeAt(i);
if (code > 0x7f && code <= 0x7ff) {s++;}
else if (code > 0x7ff && code <= 0xffff) {s+=2;}
if (code >= 0xDC00 && code <= 0xDFFF) {i--;}
}
return s;
}
console.log(bytesLength('敗')); // length 3