【问题标题】:Regex pattern match only the first two times正则表达式模式仅匹配前两次
【发布时间】:2017-06-26 18:20:11
【问题描述】:

一般问题

如果该模式之前已匹配 N 次,您如何防止正则表达式匹配?

具体例子

我有一个 13 位的记录标识符,用户将在文本字段中键入,例如“123-456-7891011”。

我试图封装的行为是在键入时在第 4 位和第 7 位数字之前添加一个破折号。我拥有处理事件的所有 javascript,没有任何问题。

它将始终是数字 - 我在此特定步骤之前处理它。

当我每次输入第 n+4 位数字时,我能够很好地降低正则表达式以插入破折号。我需要的是它在第二个“-”之后的任何时候都不要这样做

到目前为止,我的事件处理程序 (oninput) 中最接近的是:

var addDash = /([^\-\d{3}]*\d{3})(\d+)/;
this.value = this.value.replace(addDash, "$1-$2");

例如,输入“1234567891011”会得到“123-456-789-101-1”,而我想要的是“123-456-7891011”。

可能有其他方法来处理这种情况(例如splitjoin),但出于这个问题的目的,让我们坚持使用正确的正则表达式模式来回答,如果这样的模式是可能的。还发生了其他一些事情,使这种模式非常有用 - 我只是没有运气弄清楚它。


为清楚起见,此模式将在文本字段上运行 oninput - 匹配应适用于 1 到 13 之间的任何字符长度:

"123456" -> "123-456"
"123456789" -> "123-456-789"
"1234567891011" -> 123-456-7891011"

【问题讨论】:

  • @anubhava 仅适用于全长模式,而不适用于所有情况。为了清楚起见,我更新了 OP
  • 您的意思是任何长度的文本 1 - 15。您是在处理 onkeypres,还是在每个键盘输入上,甚至是退格键或箭头键?你在处理 paste 吗?
  • 你的正则表达式没有做你认为它已经做的事情。 [^-\d{3}]* 表示 0 个或多个不是连字符、数字、左大括号、数字 3 或右大括号的字符。所以排除所有有效字符,所以它总是匹配零个字符。
  • For example, typing "1234567891011"1234567 之前得到123 ...?
  • 如果您对每个键输入的文本框进行重写,除非它是复合的(即双缓冲),否则它将看起来很生涩。最好仅在发生更改时处理/重写。换句话说,让盒子处理添加,然后在删除时重写。或附加-。保持最后一张当前图像进行比较。

标签: javascript regex


【解决方案1】:

使用callbacks

您可以指定一个函数作为第二个参数。在这种情况下,将在执行匹配后调用该函数。函数的结果(返回值)将用作替换字符串。

针对您当前需求的解决方案是更改模式并始终进行完全匹配:

this.value = this.value.replace(/^(\d{3})-?(\d{3})?-?(\d*)$/, function (m, a, b, c) {
    return b ? a + "-" + b + "-" + c : a + "-" + c;
});

工作完成。

如果您想要更通用的解决方案,可以使用计数器变量:

var count = 0;
this.value = this.value.replace(/(\d{3})-?/g, function (m, a) {
    return ++count <= 2 ? a + "-" : m;
});

这个使用的模式不太准确地满足您的需求,但它展示了如何推广到您真正需要限制替换数量的不同情况。


顺便说一句,您有一个语法误解:[^\-\d{3}] 不是 的意思是您的想法。它将匹配 单个 字符,该字符不是数字或其中任何一个:-{}[...] 括号内的所有内容都定义了一个字符类,因此{n} 等量词在那里毫无意义。

【讨论】:

  • 我认为您的第二个示例将继续在前 3 位数字中添加连字符。
  • @NetMage 不会,因为该模式匹配可选的连字符,如果它遵循 3 位数字组。如果它在那里,它将被“替换”。
  • 我明白你的意思 - a 只包括括号内的匹配项。
  • +1 为解决方案,这是很好的信息,帮助我找到了我正在寻找的答案。尽管我特别要求正则表达式模式解决方案,但我不能接受作为最终答案。谢谢!
  • @RandyHall 好吧,如果你问我,这完全是一个正则表达式解决方案,但按照你的意愿去做,很高兴它有帮助;)
【解决方案2】:

您可以在替换中使用回调,其中包含一些关于如何处理分隔符的逻辑。这将使您更容易获得您所期望的行为。

var values = ['12', '123', '123-4', '123-45', '123-456', '123-456-7', '123-456-7891011'];
var addDash = /^(\d{3})-?(\d?\d?\d?)-?(\d*)/;
values.forEach(function(value) {
  value = value.replace(addDash, function(match, capture1, capture2, capture3) {
    var secondDelimiter = capture2.length == 3 ? '-' : '';
    return capture1 + '-' + capture2 + secondDelimiter + capture3;
  });

})
console.log(values)

【讨论】:

    【解决方案3】:

    使用正则表达式而不需要额外的 JS:

    var addDash = /^((\d{3}\-?)(\d{3}\-?)?)(\d)$|^(\d{3})(\d)$/;
    this.value = this.value.replace(addDash, "$1$5-$4$6");
    

    从字符串的开头开始,匹配 3 位数字和可选的“-”,以及可选的 3 位数字,紧跟在字符串末尾的最后一个数字。如果不匹配,请查看字符串是否以 3 位数字开头,紧跟在字符串末尾的最后一位数字。

    使用替换字符串中的捕获组来实现所需的格式。

    【讨论】:

      【解决方案4】:
      $(function() {
          var txt='1232';
          var addDash = /^(\d{3})-?(\d?\d?\d?)-?(\d*)/;
          $("#test").html(txt.replace(addDash, function(match, capture1, capture2, capture3) {
              var firstDelimiter = (capture1.length == 3 && capture2.length > 0) ? '-' : '';
              var secondDelimiter = (capture2.length == 3 && capture3.length > 0) ? '-' : '';
              return capture1 + firstDelimiter + capture2 + secondDelimiter + capture3;
          }));
      })
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2020-10-10
        • 2015-09-27
        • 1970-01-01
        • 2022-10-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多