Regex字符计数,但有些计数为3
Regex character count, but some count for three
我试图构建一个限制输入长度的正则表达式,但并非所有字符在此长度中都相等。我将把基本原理放在问题的后面。作为一个简单的例子,让我们将最大长度限制为12,并且只允许a
和b
,但b
计数为3个字符。
可以是:
-
aa
(小于12是可以的) -
aaaaaaaaaaaa
(正好12个就可以了)。 -
aaabaaab
(6 + 2 * 3 = 12,这是好的)。 -
abaaaaab
(still 6 + 2 * 3 = 12).
不允许:
-
aaaaaaaaaaaaa
(13a
). -
bbbba
(1 + 4 * 3 = 13,这太大了)。 -
baaaaaaab
(7 + 2 * 3 = 13).
我已经做了一个相当接近的尝试:
^(a{0,3}|b){0,4}$
最多匹配4个集群,可以由0-3个a
或一个b
组成。
然而,它不能匹配我的最后一个正示例:abaaaaab
,因为这迫使第一个集群在开始时是单个a
,为b
消耗第二个集群,然后只剩下2个集群用于其余的aaaaab
,这太长了。
- 必须在JavaScript中运行。这个正则表达式提供给Qt,它显然使用JavaScript的语法。
- 真的不需要这么快。最后,它只适用于最多40个字符的字符串。我希望在50毫秒左右的时间内验证,但稍微慢一点也是可以接受的。
为什么我需要用正则表达式做这个?
这是一个用户界面在Qt通过PyQt和QML。用户可以在这里的文本字段中为概要文件输入名称。该概要名称是url编码的(特殊字符由%XX替换),然后保存在用户的文件系统中。当用户输入大量特殊字符(如中文)时,我们会遇到问题,这些字符随后会编码为非常长的文件名。结果是,在诸如17个字符的地方,这个文件名对于某些文件系统来说太长了。url编码编码为UTF-8,每个字符最多有4个字节,因此文件名中最多有12个字符(因为每个字符都得到百分比编码)。
16个字符对于配置文件名称来说太短了。甚至我们的一些默认名称也超过了这个值。我们需要一个基于这些特殊字符的变量限制。
Qt通常允许您指定一个Validator来确定文本框中哪些值是可接受的。我们尝试实现这样的验证器,但由于PyQt中的一个错误,导致了上游的段错误。目前,它似乎无法处理自定义Validator实现。然而,PyQt也公开了三个内置验证器。2只适用于数字。第三种是一个regex验证器,它允许您输入匹配所有有效字符串的正则表达式。因此需要这个正则表达式。
标题>标题>由于regexp的限制,没有真正直接的方法可以做到这一点。您必须测试所有组合,例如13个b
和最多1个a
, 12个b
和最多4个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]*$)/
我们将编写一个函数来创建这样的组合查找头,它精确匹配c1
的m
次出现和c2
的n
次出现:
function matchTwoLetters(c1, m, c2, n) {
return matchLetter(c1, m, m) + matchLetter(c2, 0, n);
}
我们可以用它来匹配12个b
和最多4个a
,总共40个或更少:
> 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");
对于length=40的情况,regxp长度为679个字符。一个非常粗略的基准测试显示,它的执行时间不到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
试试这样写:
^((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。
- Regex匹配除“”之外的所有字符;.js”;
- regex/ng模式接受某些字符
- Regex测试特殊字符
- jQuery-Regex以防止任何特殊字符
- RegEx只获取特殊字符,前后不获取任何字符
- Regex-从字符第N次出现到字符串末尾的匹配
- Javascript Regex选择每个非字母数字字符和空白
- 为什么regex只验证字段中的一个字符,而不是所有输入的字符
- Regex查找前面没有特定字符的字符
- Regex:允许除某些选定字符之外的所有字符
- regex中连字符的这种用法有效吗
- Javascript和RegEx用于删除特殊字符
- JavaScript Unicode Regex - 字符类中的顺序范围
- REGEX (javascript) - 允许字母数字字符中的特殊字符不在第一个位置
- Javascript-使用Regex获取两个字符之间的字符
- regex,用于修改单词末尾特定大小写的字符
- Javascript RegEx在字符后匹配单词
- JavaScript-Regex删除代码/特殊字符/数字等
- JS Regex只允许数字、分号和连字符
- 检查值是否具有给定字符RegEx