查找仅在末尾可选地包含特定字符的字符串

Find strings only optionally containing a specific char at the end

我想查找其中没有 个字符的字符串,并且可以选择在字符串末尾出现此字符。

我搜索了一些提示,诸如此类,但没有解决我的问题。

^(?!\.)(?!.*\.$)(?!.*\.\.)[a-zA-Z0-9_.]+$
(?!\.) - don't allow . at start
(?!.*\.\.) - don't allow 2 consecutive dots
(?!.*\.$) - don't allow . at end

我尝试使用

str_l  = ["aaa。bbb。","aaa。","aaa"]
for str1 in str_l:
  res1 = re.search(r'(.*?!。*$)', str1) #if 。not in string, return True
  res2 = re.search(r'(?<!(。)。$)',str1) # if 。 only appear at the end of string, return True, but not solved
  print(res1,res2)

我想将 res1res2 组合成一个正则表达式,字符串结果类似于 False, True, True.

这可以通过以下代码完成。

import re

p = re.compile("^(?:(?!。).)*(。$)?(?!.*。).*$")

l = [
    "aaa。bbb。",
    "aaa bbb。",  # matches because only at end
    "aaa。bbb",
    "。aaa bbb",
    "aaa bbb",  # matches because none found
]

print([s for s in l if p.match(s)])

这导致:

['aaa bbb。', 'aaa bbb']

可以找到完整的解释 here at regex101.com

与更简洁的 ^[^。]*。?$ 相比,此匹配表达式的唯一优势是除了给定字符外,它还可以与字符串一起使用。因此,假设您需要匹配可能以“foo”结尾的字符串,但它不应出现在字符串的前面。然后你可以使用 ^(?:(?!foo).)*(foo$)?(?!.*foo).*$.

但是,它慢了大约 60%。您可以在此处查看测试和结果:

import re
import timeit

a = re.compile("^(?:(?!。).)*(。$)?(?!.*。).*$")
b = re.compile("^[^。]*。?$")

l = [
    "aaa。bbb。",
    "aaa bbb。",  # matches because only at end
    "aaa。bbb",
    "。aaa bbb",
    "aaa bbb",  # matches because none found
]

print(
    timeit.timeit(
        "matches = [s for s in l if a.match(s)]",
        setup="from __main__ import (l, a)",
    )
)

print(
    timeit.timeit(
        "matches = [s for s in l if b.match(s)]",
        setup="from __main__ import (l, b)",
    )
)

给出:

2.6208932230000004
1.6510743480000003

你可以使用

import re
str_l  = ["aaa。bbb。","aaa。","aaa"]
for str1 in str_l:
  print(str1, '=>', bool(re.search(r'^[^。]*。?$', str1)))

输出:

# => aaa。bbb。 => False
aaa。 => True
aaa => True

Python demo详情:

  • ^ - 字符串开头
  • [^。]* - 除点
  • 以外的零个或多个字符
  • 。? - 一个可选的点
  • $ - 在字符串末尾。

要使用此正则表达式从列表中获取有效字符串,您可以使用

rx = re.compile(r'^[^。]*。?$')
print( list(filter(rx.search, str_l)) )
# => ['aaa。', 'aaa']

另一种方法可以拆分

如果您使用 split 并且字符串末尾是 ,则列表中的最后一项将为空。

如果没有出现,则列表大小为 1。

str_l = ["aaa。bbb。", "aaa。", "aaa", "。", "。  ", "。。"]

for str1 in str_l:
    lst = str1.split(r"。")
    nr = len(lst)
    print(f"'{str1}' -> {nr == 1 or nr == 2 and lst[1] == ''}")

输出

'aaa。bbb。' -> False
'aaa。' -> True
'aaa' -> True
'。' -> True
'。  ' -> False
'。。' -> False

看到一个Python demo