提问人:Adam Pietrasiak 提问时间:3/18/2014 最后编辑:Adam Pietrasiak 更新时间:7/19/2020 访问量:7063
正则表达式 - 检查输入是否仍有机会匹配
Regex - check if input still has chances to become matching
问:
我们有这样的正则表达式:
var regexp = /^one (two)+ three/;
因此,只有字符串 like or or etc. 才能匹配它。"one two three"
"one two three four"
"one twotwo three"
但是,如果我们有像这样的字符串
"one "
- 仍然“有希望”,也许很快就会匹配
但是这个字符串:无论我们做什么,都永远不会匹配。"one three"
有没有办法检查给定的字符串是否有机会匹配?
当我想推荐所有以给定输入开头的选项时,我在写作过程中需要它来提供一些提示(我使用的正则表达式很长,我不想真的弄乱它们)。
换句话说 - 我想检查字符串是否在检查期间结束,并且没有遇到任何“不匹配”的情况。
换句话说 - 答案将是不匹配的内部原因。如果 reason 是字符串的末尾 - 那么它将是 promissing。但是,我不知道有任何方法可以检查为什么某些字符串不匹配
答:
这实际上是一个非常有趣的问题。
就个人而言,我会使用 RegExp 构造函数来做到这一点,因此可以分解查询:
var input = "one ";
var query = "^one two three";
var q_len = query.length;
var match;
for( var i=1; i<=q_len; i++) {
match = input.match(new RegExp(query.substr(0,i));
if( !match) {alert("Pattern does NOT match"); break;}
if( match[0].length == input.length) {alert("Input is promising!"); break;}
// else continue to the next iteration
}
显然,您可以预先处理“完全匹配”的情况,以避免整个循环问题。如果整个模式与输入匹配,那么你就很好。
编辑:我刚刚意识到这对团体和其他东西不起作用。由于格式不正确的正则表达式,它会失败,但我希望它可以作为您查询的基础。
评论
try..catch
编辑:在您编辑帖子澄清后,此答案可能与您的具体问题无关。留在这里供参考。
在正则表达式本身中,关于一旦你知道你什么也找不到,就会迅速失败的问题:
正则表达式中的锚点意味着一旦正则表达式引擎到达 of ,它将停止寻找匹配项,并且不会尝试在第二个字符上开始匹配。在这种情况下,我认为没有比这更好的了(但它已经是线性复杂性,所以还不错)。h
three
在其他情况下,我相信你有一些常用的最佳实践需要学习,以便在你知道再也找不到匹配项时尽快失败。
如果你还不知道所有格量词,你可以看看它们,但还有许多其他技巧......
你最初的问题只是测试一个字符串在另一个字符串中的位置,特别是开始。最快的方法是在匹配字符串上使用,然后使用 .我已经更新了我的原始答案以反映这一点:substr
indexOf
function check(str){
return 'one two three'.substr(0, str.length) === str;
};
console.log(check('one')); // true
console.log(check('one two')); // true
console.log(check('one two three')); // true
console.log(check('one three')); // false
如果你需要不区分大小写,那么简单地匹配和输入字符串仍然是最快的。(如果有兴趣,这里有一个 substr
、indexOf
和 RegExp
测试的 jsperf 用于字符串的开头,不区分大小写:http://jsperf.com/substr-vs-indexof-vs-regex )toLowerCase
评论
^something literal here
return rgxStr.substr(0,str.length) == str
不确定是否有办法在不制作极其复杂的模式的情况下使用正则表达式执行此操作。但是,如果您只是检查字符串,则可以执行以下操作:
function matchPartialFromBeginning(pattern, value) {
if(pattern.length == value.length)
return pattern == value;
if(pattern.length > value.length)
return pattern.substr(0, value.length) == value;
return false;
}
下面是 jsFiddle 中的简单概念证明。基本上,您只需反向循环访问建议的正则表达式,并查找最长的子匹配项。
注意:这有一个已知问题,因为它并不总是很好地处理组。例如,它会说根本不匹配,而它应该说还有希望。这可以通过使用以下行变得更有创意来解决:foo bar b
foo( bar)+
temp_re = new RegExp(regex_part+'$'); // make sure this partial match ends in a piece that could still match the entire regex
基本上,您需要解析部分正则表达式以查看它是否以组结尾,然后递归检查与该结束组的部分匹配项。这相当复杂,所以我不是为了我的演示而讨论它。
JavaScript(仅将 jQuery 用于演示目的):
var strings = {
matches: "Matches!",
stillhope: "There's still hope...",
mismatch: "Does not match",
empty: "No text yet"
};
// Object to handle watching for partial matches
function PartialRegexMonitor(regex, input_id) {
var self = this;
this.relen = regex.length;
$('body').on('keyup', '#'+input_id, function() {
self.update_test_results(regex, input_id);
});
}
PartialRegexMonitor.prototype.update_test_results = function(regex, input_id) {
var input = $('#'+input_id).val(),
matches = find_partial_matches(regex, input),
span = $('#'+input_id).siblings('.results');
span.removeClass('match');
span.removeClass('stillhope');
span.removeClass('mismatch');
span.removeClass('empty');
span.addClass(matches.type)
.html(strings[matches.type]);
}
// Test a partial regex against a string
function partial_match_tester(regex_part, str) {
var matched = false;
try {
var re = new RegExp(regex_part, 'g'),
matches = str.match(re),
match_count = matches.length,
temp_re;
for(var i = 0; i < match_count; i++) {
temp_re = new RegExp(regex_part+'$'); // make sure this partial match ends in a piece that could still match the entire regex
matched = temp_re.test(str);
if(matched) break;
}
}
catch(e) {
}
return matched;
}
// Find the longest matching partial regex
function find_partial_matches(regex, str) {
var relen = regex.length,
matched = false,
matches = {type: 'mismatch',
len: 0},
regex_part = '';
if(str.length == 0) {
matches.type = 'empty';
return matches;
}
for(var i=relen; i>=1; i--) {
if(i==1 && str[0] == '^') { // don't allow matching against only '^'
continue;
}
regex_part = regex.substr(0,i);
// replace {\d}$ with {0,\d} for testing
regex_part = regex_part.replace(/\{(\d)\}$/g, '{0,$1}');
matched = partial_match_tester(regex_part, str);
if(matched) {
matches.type = (i==relen ? 'matches' : 'stillhope');
console.log(matches.type + ": "+regex.substr(0,i)+" "+str);
matches.len = i;
break;
}
}
return matches;
}
// Demo
$(function() {
new PartialRegexMonitor('foo bar', 'input_0');
new PartialRegexMonitor('^foo bar$', 'input_1');
new PartialRegexMonitor('^fo+(\\s*b\\S[rz])+$', 'input_2');
new PartialRegexMonitor('^\\d{3}-\\d{3}-\\d{4}$', 'input_3');
});
演示的 HTML:
<p>
Test against <span class="regex">foo bar</span>:<br/>
<input type="text" id="input_0" />
<span class="results empty">No text yet</span>
</p>
<p>
Test against <span class="regex">^foo bar$</span>:<br/>
<input type="text" id="input_1" />
<span class="results empty">No text yet</span>
</p>
<p>
Test against <span class="regex">^fo+(\s*b\S[rz])+$</span> (e.g., "foo bar", "foo baz", "foo bar baz"):<br/>
<input type="text" id="input_2" />
<span class="results empty">No text yet</span>
</p>
<p>
Test against <span class="regex">^\d{3}-\d{3}-\d{4}$</span>:<br/>
<input type="text" id="input_3" />
<span class="results empty">No text yet</span>
</p>
演示的 CSS
.empty {
background-color: #eeeeee;
}
.matches {
background-color: #ccffcc;
font-weight: bold;
}
.stillhope {
background-color: #ccffff;
}
.mismatch {
background-color: #ffcccc;
font-weight: bold;
}
.regex {
border-top:1px solid #999;
border-bottom:1px solid #999;
font-family: Courier New, monospace;
background-color: #eee;
color: #666;
}
演示中的示例屏幕截图
评论
foo bad
我以前使用过的另一个有趣的选项是 OR 符号预期的每个字符。这可能不适用于每种情况,但对于您正在查看特定字符并且需要对每个字符进行部分匹配的情况,这可行。$
例如(在 Javascript 中):
var reg = /^(o|$)(n|$)(e|$)(\s|$)$/;
reg.test('') -> true;
reg.test('o') -> true;
reg.test('on') -> true;
reg.test('one') -> true;
reg.test('one ') -> true;
reg.test('one t') -> false;
reg.test('x') -> false;
reg.test('n') -> false;
reg.test('e') -> false;
reg.test(' ') -> false;
虽然这不是最漂亮的正则表达式,但它是可重复的,所以如果你出于某种原因需要动态生成它,你知道一般的模式。
同样的模式也可以应用于整个单词,这可能没有那么有用,因为他们无法一个接一个地输入来达到这些点。
var reg = /^(one|$)(\stwo|$)$/;
reg.test('') -> true;
reg.test('one') -> true;
reg.test('one ') -> false;
reg.test('one two') -> true;
评论
根据@Andacious答案,我制作了自己的,更高级一点,它似乎有效。
我制作了修改正则表达式的方法,使其接受有希望的答案。
它用 so 替换任何文字字符,从而成为寻找匹配的字母或输入结尾。"(?:OLD_LETTER|$)"
k
(?:k|$)
它还会查找不要像 {1,2} 那样替换的部件,并将它们保持原样。
我敢肯定它不完整,但是添加新的检查规则和主要技巧非常容易,似乎在任何情况下都可以作为字符串匹配的结尾并且不要继续,所以基本上我们需要对主正则表达式进行这样的修改,任何文字字符或字符组都将有替代,并且每个语法(有时似乎也有字面字符)都不能被破坏。any_sign or end of input
|$
RegExp.prototype.promising = function(){
var source = this.source;
var regexps = {
replaceCandidates : /(\{[\d,]\})|(\[[\w-]+\])|((?:\\\w))|([\w\s-])/g, //parts of regexp that may be replaced
dontReplace : /\{[\d,]\}/g, //dont replace those
}
source = source.replace(regexps.replaceCandidates, function(n){
if ( regexps.dontReplace.test(n) ) return n;
return "(?:" + n + "|$)";
});
source = source.replace(/\s/g, function(s){
return "(?:" + s + "|$)";
});
return new RegExp(source);
}
这是一个称为部分匹配的正则表达式功能,它在多个正则表达式引擎(如 PCRE、Boost、Java)中可用,但在 JavaScript 中不可用。
Andacious的回答显示了克服这个限制的一个非常好的方法,我们只需要自动化它。
井。。。接受挑战:)
幸运的是,JavaScript 的正则表达式特性集非常有限,语法简单,因此我根据 MDN 上列出的特性为这项任务编写了一个简单的解析器和动态转换。此代码已更新为处理 ES2018 功能。
几个兴趣点:
- 这将生成一个正则表达式,该正则表达式几乎总是与空字符串匹配。因此,当 is 的结果或第一个元素为空字符串的数组时,将发生部分匹配失败
exec
null
- 负面展望保持原样。我相信这是正确的做法。匹配失败的唯一方法是通过它们(即在正则表达式中放置 a)和锚点 ( 和 )。回溯(正面和负面)也保持原样。
(?!)
^
$
- 解析器假定输入模式有效:您首先不能从无效模式创建对象。如果引入新的正则表达式功能,这可能会在将来中断。
RegExp
- 此代码将无法正确处理反向引用:例如,不会产生部分匹配。
^(\w+)\s+\1$
hello hel
RegExp.prototype.toPartialMatchRegex = function() {
"use strict";
var re = this,
source = this.source,
i = 0;
function process () {
var result = "",
tmp;
function appendRaw(nbChars) {
result += source.substr(i, nbChars);
i += nbChars;
};
function appendOptional(nbChars) {
result += "(?:" + source.substr(i, nbChars) + "|$)";
i += nbChars;
};
while (i < source.length) {
switch (source[i])
{
case "\\":
switch (source[i + 1])
{
case "c":
appendOptional(3);
break;
case "x":
appendOptional(4);
break;
case "u":
if (re.unicode) {
if (source[i + 2] === "{") {
appendOptional(source.indexOf("}", i) - i + 1);
} else {
appendOptional(6);
}
} else {
appendOptional(2);
}
break;
case "p":
case "P":
if (re.unicode) {
appendOptional(source.indexOf("}", i) - i + 1);
} else {
appendOptional(2);
}
break;
case "k":
appendOptional(source.indexOf(">", i) - i + 1);
break;
default:
appendOptional(2);
break;
}
break;
case "[":
tmp = /\[(?:\\.|.)*?\]/g;
tmp.lastIndex = i;
tmp = tmp.exec(source);
appendOptional(tmp[0].length);
break;
case "|":
case "^":
case "$":
case "*":
case "+":
case "?":
appendRaw(1);
break;
case "{":
tmp = /\{\d+,?\d*\}/g;
tmp.lastIndex = i;
tmp = tmp.exec(source);
if (tmp) {
appendRaw(tmp[0].length);
} else {
appendOptional(1);
}
break;
case "(":
if (source[i + 1] == "?") {
switch (source[i + 2])
{
case ":":
result += "(?:";
i += 3;
result += process() + "|$)";
break;
case "=":
result += "(?=";
i += 3;
result += process() + ")";
break;
case "!":
tmp = i;
i += 3;
process();
result += source.substr(tmp, i - tmp);
break;
case "<":
switch (source[i + 3])
{
case "=":
case "!":
tmp = i;
i += 4;
process();
result += source.substr(tmp, i - tmp);
break;
default:
appendRaw(source.indexOf(">", i) - i + 1);
result += process() + "|$)";
break;
}
break;
}
} else {
appendRaw(1);
result += process() + "|$)";
}
break;
case ")":
++i;
return result;
default:
appendOptional(1);
break;
}
}
return result;
}
return new RegExp(process(), this.flags);
};
// Test code
(function() {
document.write('<span style="display: inline-block; width: 60px;">Regex: </span><input id="re" value="^one (two)+ three"/><br><span style="display: inline-block; width: 60px;">Input: </span><input id="txt" value="one twotw"/><br><pre id="result"></pre>');
document.close();
var run = function() {
var output = document.getElementById("result");
try
{
var regex = new RegExp(document.getElementById("re").value);
var input = document.getElementById("txt").value;
var partialMatchRegex = regex.toPartialMatchRegex();
var result = partialMatchRegex.exec(input);
var matchType = regex.exec(input) ? "Full match" : result && result[0] ? "Partial match" : "No match";
output.innerText = partialMatchRegex + "\n\n" + matchType + "\n" + JSON.stringify(result);
}
catch (e)
{
output.innerText = e;
}
};
document.getElementById("re").addEventListener("input", run);
document.getElementById("txt").addEventListener("input", run);
run();
}());
我测试了一下,它似乎工作正常,如果您发现任何错误,请告诉我。
评论
^
$
仍然不是 100% 确定您要求什么,但您也可以嵌套它们,如下所示:
var regexp = /^(one)+((\s+two)+((\s+three)+((\s+four)+)?)?)?$/;
比赛:
- 一
- 一二二
- 一二二三
- 一二二三三四
不匹配:
- 一三
我发现了一个 npm 包,其中包含具有增量正则表达式匹配的 RegEx 的 JavaScript 实现:https://www.npmjs.com/package/incr-regex-package。看起来值得一试。它可以报告给定输入的 、 和结果。DONE
MORE
MAYBE
FAILED
这里还有一个 React 输入组件的实现示例:https://www.npmjs.com/package/react-maskedinput。它用于提供与正则表达式库交互的更以用户输入为中心的视图。{RXInputMask} from incr-regex-package
评论
"one "
.3076923
"one three"
-1
^o(?:n(?:e(?: (?:(?:two)*(?:t(?:w(?:o(?: (?:t(?:h(?:r(?:e(?:e.*?)?)?)?)?)?)?)?)?)?)?)?)?)?$