将字符串与正则表达式部分匹配

时间:2017-02-25 21:12:22

标签: javascript regex

假设我有这个正则表达式:/ abcd / 假设我想检查针对该正则表达式的用户输入并禁止在输入中输入无效字符。当用户输入" ab"时,它失败作为正则表达式的匹配,但我不能禁止输入" a"然后" b"因为用户不能一次输入所有4个字符(复制/粘贴除外)。所以我需要的是一个部分匹配,它检查一个不完整的字符串是否可能与正则表达式匹配。

Java有一些用于此目的:.hitEnd()(此处描述http://glaforge.appspot.com/article/incomplete-string-regex-matching)python本身没有这样做但是有这个包来完成这项工作:https://pypi.python.org/pypi/regex

我在js中找不到任何解决方案。几年前就被问过:Javascript RegEx partial match 甚至在此之前:Check if string is a prefix of a Javascript RegExp

P.S。正则表达式是自定义的,假设用户自己输入正则表达式然后尝试输入与该正则表达式匹配的文本。该解决方案应该是适用于在运行时输入的正则表达式的通用解决方案。

6 个答案:

答案 0 :(得分:8)

看起来你很幸运,我已经在JS中实现了这些东西(适用于大多数模式 - 也许这对你来说已经足够了)。见my answer here。你还会在那里找到一个有效的演示。

这里没有必要复制完整的代码,我只是说明整个过程:

  • 解析输入正则表达式,并执行一些替换。由于您无法在JS中的RegExp对象中使用无效模式,因此无需进行错误处理。
  • abc替换为(?:a|$)(?:b|$)(?:c|$)
  • 为任何"原子"做同样的事情。例如,字符组[a-c]将变为(?:[a-c]|$)
  • 按原样保持锚点
  • 保持负面预测

如果JavaScript具有更高级的正则表达式功能,则可能无法进行此转换。但凭借其有限的功能集,它可以处理大多数输入正则表达式。如果您的输入字符串在反向引用匹配的中间结束(例如将^(\w+)\s+\1$hello hel匹配),那么 在正则表达式中使用反向引用产生不正确的结果。

答案 1 :(得分:1)

我认为您必须有2个正则表达式用于键入/a?b?c?d?/,而另一个用于在结束时进行测试,同时粘贴或留下输入/abcd/

这将测试有效的电话号码:



const input = document.getElementById('input')

let oldVal = ''
input.addEventListener('keyup', e => {
  if (/^\d{0,3}-?\d{0,3}-?\d{0,3}$/.test(e.target.value)){
    oldVal = e.target.value
  } else {
    e.target.value = oldVal
  }
})
input.addEventListener('blur', e => {
  console.log(/^\d{3}-?\d{3}-?\d{3}-?$/.test(e.target.value) ? 'valid' : 'not valid')
})

<input id="input">
&#13;
&#13;
&#13;

这就是名字姓氏

的情况

&#13;
&#13;
const input = document.getElementById('input')

let oldVal = ''
input.addEventListener('keyup', e => {
  if (/^[A-Z]?[a-z]*\s*[A-Z]?[a-z]*$/.test(e.target.value)){
    oldVal = e.target.value
  } else {
    e.target.value = oldVal
  }
})
input.addEventListener('blur', e => {
  console.log(/^[A-Z][a-z]+\s+[A-Z][a-z]+$/.test(e.target.value) ? 'valid' : 'not valid')
})
&#13;
<input id="input">
&#13;
&#13;
&#13;

答案 2 :(得分:0)

我强烈怀疑(虽然我不是100%肯定)这个问题的一般情况与着名的图灵的“Haltin问题”没有解决方法(见Undecidable problem)。即使有一个解决方案,它很可能不会是用户真正想要的,因此根据您的严格程度将导致糟糕的可怕用户体验。

示例:

假设“目标RegEx”为[a,b]*c[a,b]*,也假设您乍看之下产生了合理的“测试RegEx”[a,b]*c?[a,b]*(显然字符串中的两个c无效,是吗?)并假设当前用户输入为aabcbb,但存在拼写错误,因为用户实际需要的是aacbbb。有很多方法可以解决这个错误:

  • 删除c并在第一个b之前添加它 - 将正常工作
  • 首先删除b并在c之后添加 - 将正常运行
  • 在第一个c之前添加b然后删除旧的{O},我们禁止此输入无效,用户会发疯,因为没有正常人能理解这样的逻辑。

另请注意,您的hitEnd在此处会遇到同样的问题,除非您禁止用户在输入框的中间输入字符,这将是制作可怕用户界面的另一种方式。

在现实生活中,会有许多更为复杂的例子,任何你的智能启发法都无法正确解释,从而会让用户感到不安。

那该怎么办?我认为你能做的唯一事情仍然是合理的用户体验,这是你能做的最简单的事情,即只需分析你的“目标RegEx”以获得允许的字符集,并使你的“测试RegEx”[set of allowed chars]*。是的,如果“目标RegEx”包含.野外车,您根本无法进行任何合理的过滤。

答案 3 :(得分:0)

对于那些认为根本没有解决方案的人来说,这是一个很难的解决方案:在js中实现python版本(https://bitbucket.org/mrabarnett/mrab-regex/src/4600a157989dc1671e4415ebe57aac53cfda2d8a/regex_3/regex/_regex.c?at=default&fileviewer=file-view-default)。所以有可能。如果某人有更简单的回答,他将赢得赏金。

使用python模块的示例(带后向引用的正则表达式):

$ pip install regex
$ python
>>> import regex
>>> regex.Regex(r'^(\w+)\s+\1$').fullmatch('abcd ab',partial=True)
<regex.Match object; span=(0, 7), match='abcd ab', partial=True>

答案 4 :(得分:0)

你们可能会发现这个感兴趣的页面:

https://github.com/desertnet/pcre

这是一个英勇的工作:制作一个支持PCRE的WebAssembly实现。我仍在玩它,但我怀疑它不切实际。 WebAssembly二进制文件的重量约为300K;并且如果您的JS意外终止,您最终可能不会破坏该模块,从而导致大量内存泄漏。

最重要的是:这显然是ECMAscript人们应该正规化的东西,浏览器制造商应该提供(对WebAssembly开发人员以耻辱让他们沾沾自喜...的荣誉)

我最近尝试使用input [type ='text']元素的“ pattern”属性。我和其他许多人一样,发现它令人失望,直到提交表单后才能验证。因此,一个人会浪费时间输入(或粘贴...)许多字符并跳到其他字段,而在提交表单后才发现自己输入了错误的字段。理想情况下,我希望它能够在用户键入每个键时(或在粘贴时...)立即验证字段输入。

进行部分正则表达式匹配的诀窍(直到ECMAscript的人们和浏览器制造商将其与PCRE结合在一起...)不仅要指定模式正则表达式,而且还要指定关联的模板值作为数据属性。如果您的字段输入短于模式(或input.maxLength ...),则可以将它们用作后缀以进行验证。是的-这对于具有复杂案例结果的正则表达式不可行;但是对于固定位置模板模式匹配-通常是需要的-很好(如果您碰巧需要更复杂的东西,则可以基于我的代码中显示的方法...)

该示例针对的是比特币地址[现在引起您注意吗? -好,不是那些不相信数字货币技术的人。]完成此任务的关键JS函数是validatePattern。 HTML标记中的input元素将像这样指定:

<input id="forward_address"
       name="forward_address"
       type="text"
       maxlength="90"
       pattern="^(bc(0([ac-hj-np-z02-9]{39}|[ac-hj-np-z02-9]{59})|1[ac-hj-np-z02-9]{8,87})|[13][a-km-zA-HJ-NP-Z1-9]{25,34})$"
       data-entry-templates="['bc099999999999999999999999999999999999999999999999999999999999','bc1999999999999999999999999999999999999999999999999999999999999999999999999999999999999999','19999999999999999999999999999999999']"
       onkeydown="return validatePattern(event)"
       onpaste="return validatePattern(event)"
       required
/>

[信用信息转至该帖子:“ RegEx to match Bitcoin addresses? ”注意老式的比特币狂热者,他们将在这里拒绝在正则表达式中使用零-这仅仅是完成PRELIMINARY验证的一个示例;接受浏览器传递的地址的服务器可以在表单发布后进行RPC调用,以更加严格地对其进行验证。调整您的正则表达式以适合。]

在数据输入模板中 exact 个字符的选择有些武断;但是它们必须是这样的,如果用户键入或粘贴的输入的长度仍然不完整,它将用作乐观的替代,并且到目前为止,该输入仍将视为有效。在该示例中,对于最后一个数据输入模板('19999999999999999999999999999999999'),该值是一个“ 1”,后跟39个9(参见正则表达式规范“ {25,39}”如何指示一个在第二个字符跨度/组中最多39个数字...)因为要用两种形式-“ bc”前缀和较旧的“ 1” /“ 3”前缀-我提供了一些支架-in模板供验证器尝试(如果它仅通过其中一个,它将进行验证...)在每种模板情况下,我都提供了 longest 可能的模式,以确保最大的可能性就长度而言。

如果您是在动态Web内容服务器上生成此标记的,则带有模板变量(la django ...)的示例将是:

 <input id="forward_address"
        name="forward_address"
        type="text"
        maxlength="{{MAX_BTC_ADDRESS_LENGTH}}"
        pattern="{{BTC_ADDRESS_REGEX}}" {# base58... #}
        data-entry-templates="{{BTC_ADDRESS_TEMPLATES}}" {# base58... #}
        onkeydown="return validatePattern(event)"
        onpaste="return validatePattern(event)"
        required
/>

[请紧记:我去了游泳池的最深处。您也可以将其用于更简单的验证模式。]

如果您不想使用事件属性,而是在文档加载时将函数透明地挂接到元素的事件上,请自行退出。

您会注意到,我们需要在以下三个事件上指定validatePattern:

  • 按下此键可拦截删除键和退格键。

  • 粘贴(剪贴板粘贴到该字段的值中,如果有效,它将接受为有效;否则,粘贴不会蒸腾...)

当然,当字段中的文本部分被选中时,我也考虑到了这一点,它指出按键或粘贴的文本将替换所选的文本。

这是执行魔术的[无依赖关系]代码的链接:

https://gitlab.com/osfda/validatepattern.js

(如果碰巧引起了我的兴趣,我将结合建设性和实用建议,并为其提供更好的自述...)

PS:Lucas Trzesniewski在上面发布的增量正则软件包:

  • 似乎尚未更新? (我看到它正在进行修改的迹象?)

  • 未进行浏览器化(尝试对其进行操作,以至于不胜其烦-这是一个模块混乱;欢迎其他任何人在此发布浏览器化的版本以进行测试。如果可以,我将其集成使用我的输入验证钩子,并提供它作为替代解决方案...)如果您成功地将其浏览器优化,也许分享所需的确切步骤也将使本文中的每个人都受益。我尝试使用esm程序包来修复browserify所面临的版本不兼容问题,但这绝非易事...

答案 5 :(得分:-1)

正如许多人所说的那样,没有标准库,所幸我编写了一个Javascript实现,可以完全满足您的要求。它有一些小的限制,可用于Javascript支持的正则表达式。 参见:incr-regex-package

另外还有一个反应组件,使用此功能提供一些有用的功能:

  1. 键入时检查输入
  2. 尽可能自动完成
  3. 为可能的输入值提出建议

功能Demo of use的演示