正则表达式:如何在复合表达式中获得AND THEN运算符的作用?

时间:2019-04-12 21:20:53

标签: javascript regex

我正努力使用正则表达式。我想我了解个人的表达方式,但是将某些东西组合在一起完全使我感到困惑。我不了解如何使用与AND运算符等效的东西来将我想要的片段连接成一个“完整”的匹配表达式。

例如,我想将一个字符串拆分成一个数组,将<1><57></1></57>的任何值上断开。

所以,我认为我需要类似的东西:

( '<' or '<\/' ) and ( [1-9] or [1-4][0-9] or [5][0-7] ) and '>'

我可以单独使用<[1-4] [0-9]>或,但是当与'|'一起使用时它返回部分匹配项,或者在完全匹配项之间返回未定义的值。

能不能告诉我我不明白的地方?附件是我的例子。

如果对第一个表达式单击“尝试”,它将在每个<21></21>之后产生空值。当我测试它时,它在console.log中打印为未定义。第二个表达式在每个标签之后产生<</。我不明白这一点,更不用说如何在此问题的早期将更完整的表达式转换为regExp。

所需的输出是:

'This is a', '<21>', 'test', '<\/21>', '.'

谢谢。

添加 收到格奥尔格(Georg)对这个问题的回答后,我开始对转义这些标签的方法感兴趣,特别是因为当前仅在Chrome中不支持负向回溯。我的意思是,\<21>将被视为常规文本,并且此时不会生成字符串拆分。如果您对类似内容感兴趣,则可能会发现Revo Defining Types, Streamlining Functions提供的我的后续问题的答案很有帮助。

let b, B = document.querySelectorAll('button');

for ( b of B ) b.addEventListener( 'click', split_str, false );

function split_str( evt )
 {
   let e = evt.currentTarget,
       r = new RegExp( e.previousElementSibling.value ),
       s = e.parentNode.previousElementSibling.value;
   e.parentNode.lastElementChild.textContent = s.split(r);   
 }
div > div  { border: 1px solid rgb(150,150,150); width: 500px; height: 200px;padding: 5px; }

input { border: 1px solid rgb(150,150,150); width: 500px; margin-bottom: 20px; padding:5px; }
<input type='text' value="This is a<21>test</21>.">

<div>

<input type='text' value="(<[1-4][0-9]>)|(<\/[1-4][0-9]>)"> <button>try</button>

<input type='text' value="((<|<\/)[1-4][0-9]>)"> <button>try</button>

<div></div>

</div> 

3 个答案:

答案 0 :(得分:2)

您几乎可以理解。其实就像用|替换'or'并用串联替换and一样简单。然后通过在每个组的开头添加?:来确保组不匹配:

(?:<|<\/)(?:[1-9]|[1-4][0-9]|[5][0-7])>

MDN has an explanation on the interaction of split and regex。但是简短的示例解释是:

'hi_joe'.split('_'); // ['hi', 'joe']
'hi_joe'.split(/_/); // ['hi', 'joe']
'hi_joe'.split(/(_)/); // ['hi', '_', 'joe']
'hi_joe'.split(/(?:_)/); // ['hi', 'joe']

每个评论的更新,如果您也希望在结果数组中使用<##>,则将正则表达式包装在另外一组括号中。

((?:<|<\/)(?:[1-9]|[1-4][0-9]|[5][0-7])>)

答案 1 :(得分:1)

好的,让我们从thingy开始。很好,但是从技术上讲,不需要在单个符号中加上[5]

 [1-9] | [1-4][0-9] | 5[0-7]

(为清楚起见,在此处和下方使用空格)。

在第一部分中,类似a | ab的更改在写为ab?时读起来更好,即“ a,然后可选地是b`。这给了我们

 < \/ ?

现在,您要查找的“ and”(或“ and then”)运算符在正则表达式语言中非常简单-没什么。也就是说,a and then b就是ab

但是,如果我们像这样简单地组合两个部分

a  x | y | z

这将是一个错误,因为|的优先级较低,因此将其解释为

ax | y | z

这不是我们想要的。因此,我们需要将数字内容放入括号中,出于下面将要解释的原因,这些括号也必须不可捕获:

<\/?  (?: [1-9] | [1-4][0-9] | 5[0-7] )

这与我们的定界符匹配,但是我们也需要介于两者之间的所有内容,因此我们将split输入。 split通常会返回与定界符不匹配的字符串数组:

"a,b,c".split(/,/) => a b c

如果我们也想包含定界符,则必须将其放置在捕获组中:

"a,b,c".split(/(,)/) => a , b , c

所以我们必须再次将所有内容包装在括号中:

(  <\/?  (?: [1-9] | [1-4][0-9] | 5[0-7] )  )

这就是?:的原因-我们希望捕获全部内容,而不是数字部分。

将所有内容放在一起似乎可以解决问题:

s = "This is a<21>test</21>."


console.log(s.split(/(<\/?(?:[1-9]|[1-4][0-9]|5[0-7])>)/))

希望这可以说明一些问题

答案 2 :(得分:0)

我了解正则表达式的方式是,除非另有说明,例如一个OR子句,您定义为正则表达式的所有内容都为AND形式。 [a-z]将匹配一个字符,而[a-z][a-z]将匹配一个字符和另一个字符。

根据您的用例,可能需要以下正则表达式。如您所见,它捕获了<number></number>之间的所有内容。

<[1-5][0-9]>([\s\S]*?)<\/[1-5][0-9]>

<[1-5][0-9]> matches <number> where number is between 00 and 59.
[\s\S]*? matches every single character there is, including new lines, between zero and unlimited times.
</[1-5][0-9]> matches </number> where number is between 00 and 59.

这是一个代码段,返回<number></number>之间的所有内容。它将匹配项转换为数组,并获取第一个匹配项的第一个捕获组。第一个捕获组是<number></number>之间的所有内容,如正则表达式本身的括号所示。

let str = '<10>Hello, world!</10>';

let reg = /<[1-5][0-9]>([\s\S]*?)<\/[1-5][0-9]>/g;

let matches = Array.from( str.matchAll(reg) );

console.log(matches[0][1]);