如果字符不属于数组,如何指定匹配应该发生?

How to I specify that a match should occur if a character does not belong to an array?

我在正则表达式中指定 "the next character should not be from this group of characters" 时遇到问题。我有

TOKENS = [":", ".", "'"]
"01:39\t" =~  /\b0\d[#{Regexp.union(TOKENS)}]\d\d^#{Regexp.union(TOKENS)}/
 #=> nil

因为 "\t" 不是我的 TOKENS 数组的一部分,我认为上面应该匹配,但它不匹配。如何调整我的正则表达式,特别是这部分

^#{Regexp.union(TOKENS)}

说这个字符不应该是这个数组的一部分?

正则表达式的 "not" 部分需要括号。

>> TOKENS = [":", ".", "'"]
>> regex = /\b0\d[#{Regexp.union(TOKENS)}]\d\d^#{Regexp.union(TOKENS)}/
>> "01:39\t" =~ regex
#=> nil

但是:

>> regex = /\b0\d[#{Regexp.union(TOKENS)}]\d\d[^#{Regexp.union(TOKENS)}]/
# Add brackets                            here^                and here^
>> "01:39\t" =~ regex
#=> 0

您的 /\b0\d[#{Regexp.union(TOKENS)}]\d\d^#{Regexp.union(TOKENS)}/ 模式最终会像

/(?-mix:\b0\d[(?-mix::|\.|')]\d\d^(?-mix::|\.|'))/
             ^^^^^^^^^^^^^^^^    ^^^^^^^^^^^^^^^

这里,正则表达式对象是一个修饰符组,禁用了多行、不区分大小写和自由间距模式。最后一个 ^ 是行锚点的开始,它单独破坏了整个正则表达式,将其变成一个永远不匹配任何字符串的模式。

[...] 字符 class 括号包裹 #{Regexp.union(TOKENS)} 是不够的,您需要使用 .source 属性 摆脱 (?-mix:...) 因为你不想否定 mix但是,你不能使用 Regexp.union,因为它会添加 | 字符,并在字符 class 内,它被视为文字字符(因此,你也会否定管道)。

您应该使用 TOKENS.join().gsub(/[\]\[\^\-]/, '\\\&') 定义分隔符序列以转义所有应该在正则表达式字符 class 内转义的字符,然后放在字符 class 方括号之间。

Ruby demo:

TOKENS = [":", ".", "'", "]"]
sep_rx = TOKENS.join().gsub(/[\]\[\^\-]/, '\\\&')
puts sep_rx
# => :.'\]
rx = /\b0\d[#{sep_rx}]\d\d[^#{sep_rx}]/
puts rx.source
# => \b0\d[:.'\]]\d\d[^:.'\]]
puts "01:39\t" =~  rx
# => 0

Rubular demo

注意 .gsub(/[\]\[\^\-]/, '\\\&') 匹配 ][^\- 并在它们前面添加一个反斜杠. '\\\&' 中的前 4 个反斜杠定义替换模式中的文字反斜杠,\& 代表整个匹配项