【问题标题】:How to ignore order in regex?如何忽略正则表达式中的顺序?
【发布时间】:2023-04-10 23:25:01
【问题描述】:

我正在使用正则表达式来验证我的 angular2 应用程序中的一些内容:

Validators.pattern('\d*F{1}N{1}K?J?')

我只想允许数字 (0-9) 和字母 F N K J。K 和 J 应该是可选的,F 和 N 必须恰好出现一次。

有谁知道,如何忽略字母和数字的顺序?如果我按以下顺序准确插入字符,它不仅匹配:012FNKJ。 KJFN012 根本不匹配。 :(

【问题讨论】:

  • 你试过用方括号[] 包裹所有东西吗?
  • 您能否提供更多说明什么应该/不应该匹配? KJ 可以出现不止一次吗? FN 呢?所有字符可以按任何顺序排列吗?其他字符可以吗,例如空格或其他字母,在字符串中?小写字母呢?尝试提供更全面的匹配/不匹配字符串列表。
  • 另外,您是否需要为此使用正则表达式?使用简单的函数调用来完成此操作可能更简单,性能更高。
  • 要清楚一点,999999999999999999999999999999F9N99999999999999999999999 是有效的,对吗?
  • 是的,我需要一个正则表达式,因为我的客户希望我使用正则表达式 :)

标签: regex validation


【解决方案1】:

这里是您的问题的纯正则表达式答案(已添加空格以提高可读性):

^
(?=[^F]*F[^F]*$)
(?=[^N]*N[^N]*$)
(?=[^K]*K?[^K]*$)
(?=[^J]*J?[^J]*$)
[FNKJ\d]+$

解释:

  • 模式的四个(?=...) 部分中的每一个都是lookaheads。例如,他们说“字符串必须包含任意数量的非Ks,然后可能包含一个K,然后再包含任意数量的非Ks”。
  • 正则表达式的最后一部分是“整个字符串必须包含only FNKJ 和数字”。

但是,如果可能的话,我会建议对这个问题实施 非正则表达式 解决方案。我上面的答案很难理解,而且比一个简单的函数要慢得多(O(n)!)。

O(n) 中的一些伪代码无需正则表达式即可解决此问题:

function check_valid(string) {
  found_f = false
  found_n = false
  found_k = false
  found_j = false

  for(letter in string) {
    switch(letter) {
      case 'F':
        if(found_f) { return false }
        found_f = true
        break;
      case 'N':
        if(found_n) { return false }
        found_n = true
        break;
      case 'K':
        if(found_k) { return false }
        found_k = true
        break;
      case 'J':
        if(found_j) { return false }
        found_j = true
        break;
      case 0: case 1: case 2: case 3: case 4:
      case 5: case 6: case 7: case 8: case 9:
        break;
      default:
        return false; 
    }
  }

  return(found_f & found_n)
}

【讨论】:

  • 希望我能为最后一部分提供多个+1,因为这是最重要的一点。仅仅因为一个问题可以用正则表达式解决,当然并不意味着它应该
  • 你从哪里得到O(n)!?所写的这个解决方案只有O(5n),因为它是对字符串的5次线性扫描(独占边界意味着没有回溯)
  • @Tezra 我说你可以得到O(n) 如果你使用非正则表达式解决方案。我刚刚用这样一个函数用伪代码更新了我的答案。
  • @TomLord 啊...不过我仍然不同意这个论点。这个正则表达式是线性的(只有两倍的工作最坏情况。同样的工作与使用条件的更复杂的正则表达式)。因此,在实践中,Regex 的唯一问题是它很长/难以阅读(而且,修改起来很痛苦)。在这种情况下,您唯一关心的应该是易读性(因为这是在这种情况下实际上意味着任何事情的唯一因素)
  • @Tezra 你的5n 对正则表达式回溯的工作方式做出了错误的假设。例如,参见this example,它采用46 步骤使6 字符串无效(与我的函数将采用的6 步骤相比)。如果模式变得更复杂,例如“3 或 4 个 Fs”,那么你会看到比 5x 慢得多的性能。
【解决方案2】:

您可以将负前瞻与反向引用结合使用:

^(?=.*F)(?=.*N)(?:\d|([FNKJ])(?!.*\1))*$

它首先使用前瞻来确保FN 在那里。然后它匹配数字,来自允许组的字母捕获 该字母后跟一个否定的前瞻,以确保它不会重复。然后重复前面的替代方案,直到字符串结束。

See it here at regex101.

【讨论】:

  • 在性能说明中,需要 81 个步骤 将此正则表达式应用于像 KJFN012 这样的简单字符串。非正则表达式解决方案最多可通过 7 个步骤解决此问题。
  • 无可否认,这个解决方案非常聪明:)
【解决方案3】:

您需要使用| 来表示“这种模式或这种模式”,并明确布置这两种模式。

[0-9KJ]*F[0-9KJ]*N[0-9KJ]*|[0-9KJ]*N[0-9KJ]*F[0-9KJ]*

这就是正则表达式不适合模糊匹配的原因。您最好使用[0-9FNKJ]*string.containsOne('F', 'N')(伪代码)进行验证

【讨论】:

  • 虽然这还远远不够模糊...字符串应该包含只有一个FN,而你的模式匹配例如123FFNN.
  • @TomLord Misread OP,但这是一个简单的改变。主要的一点是,这种松散的逻辑会扩大您需要的 Regex 的大小,而且还有更简单的替代方案。
【解决方案4】:

这是一个建议。它匹配“KJdigits 的任何字符串,其中恰好包含一个 F 和一个 N”。

^[KJ0-9]*F[KJ0-9]*N[KJ0-9]*$

说明:

  • [KJ0-9]* 介于零和无限次 K、J 和数字之间
  • F[KJ0-9]* F 后跟 K、J 和数字
  • N[KJ0-9]* N 后跟 K、J 和数字

请注意,如果 N 在您的模式中可以位于 F 之前,您可能需要使用

^[KJ0-9]*F[KJ0-9]*N[KJ0-9]*|[KJ0-9]*N[KJ0-9]*F[KJ0-9]*$

由于您基本上无法计算正则表达式,因此我认为这是使用正则表达式检查它的更简洁的方法。

Test it on regex101

【讨论】:

  • 不匹配123NF
  • @TomLord 确实如此,如果你在 rexex101 上测试它。但是他应该在开头和结尾添加一个^$来匹配整个字符串
  • @PierreDuc 你的意思是更新到^[KJ0-9]*F[KJ0-9]*N[KJ0-9]*$|^[KJ0-9]*N[KJ0-9]*F[KJ0-9]*$
  • @TomLord 并没有指定 J 应该只出现 0 或一次
  • @PierreDuc 这是我的解释,因为 OP 在最初的尝试中使用了 \d*J?
猜你喜欢
  • 2018-10-29
  • 2022-10-07
  • 2014-04-17
  • 2014-08-05
  • 2023-03-04
  • 2011-02-25
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多