我想编写一个正则表达式来检查一个单词是否以除 s、x、y、z、ch、sh 或元音之外的任何内容结尾,然后是一个 s。这是我失败的尝试:
re.match(r".*[^ s|x|y|z|ch|sh|a|e|i|o|u]s",s)
补充一组字符的正确方法是什么?
使用非正则表达式解决方案str.endswith
:
>>> from itertools import product
>>> tup = tuple(''.join(x) for x in product(('s','x','y','z','ch','sh'), 's'))
>>> 'foochf'.endswith(tup)
False
>>> 'foochs'.endswith(tup)
True
[^ s|x|y|z|ch|sh|a|e|i|o|u]
这是一个倒置字符类。字符类匹配单个字符,因此在您的情况下,它将匹配任何字符,除了以下之一:acehiosuxyz |
. 请注意,它不会尊重诸如 and 之类的复合组ch
,sh
而 the|
实际上被解释为在字符类中多次出现的管道字符(其中重复项被忽略)。
所以这实际上等价于下面的字符类:
[^acehiosuxyz |]
相反,您必须使用否定的外观来确保尾随s
没有任何字符序列:
.*(?<!.[ sxyzaeiou]|ch|sh)s
这个有一个问题是它不能匹配两个字符词,因为要能够使用look behinds,look behinds需要有一个固定的大小。为了在后面的外观中同时包含单个字符和两个字符组,我必须在单个字符匹配中添加另一个字符。但是,您可以改用两个单独的外观:
.*(?<![ sxyzaeiou])(?<!ch|sh)s
正如 LarsH 在评论中提到的,如果你真的想匹配以此结尾的单词,你应该在表达式的末尾添加某种边界。如果你想匹配字符串/行的结尾,你应该添加一个$
,否则你至少应该添加一个单词边界\b
以确保单词实际上在那里结束。
看起来您需要在这里进行负面回顾:
import re
rx = r'(?<![sxyzaeiou])(?<!ch|sh)s$'
print re.search(rx, 'bots') # ok
print re.search(rx, 'boxs') # None
请注意,re
它不支持可变宽度的 LB,因此您需要其中的两个。
怎么样
re.search("([^sxyzaeiouh]|[^cs]h)s$", s)
使用search()
而不是match()
意味着匹配不必从字符串的开头开始,因此我们可以消除.*
.
这是假设单词的结尾是字符串的结尾;即我们不必检查单词边界。
它还假设您不需要匹配 "word" hs
,即使它在字面上符合您的规则。如果您也想匹配它,您可以添加另一种选择:
re.search("([^sxyzaeiouh]|[^cs]|^h)s$", s)
但同样,我们假设单词的开头是字符串的开头。
请注意,原始字符串符号 ,r"..."
在这里是不必要的(但无害)。仅当您在正则表达式中有反斜杠时才有帮助,这样您就不必在字符串表示法中转义它们。