正则表达式匹配前面没有字符串的字符
Regex match characters when not preceded by a string
我正在尝试匹配标点符号后的空格,以便我可以拆分大量文本,但我看到一些常见的边缘情况,包括地点、标题和常见缩写:
I am from New York, N.Y. and I would like to say hello! How are you today? I am well. I owe you . 00 because you bought me a No. 3 burger. -Sgt. Smith
我将它与 Python 中的 re.split
函数一起使用 3 我想得到这个:
["I am from New York, N.Y. and I would like to say hello!",
"How are you today?",
"I am well.",
"I owe you . 00 because you bought me a No. 3 burger."
"-Sgt. Smith"]
这是我目前的正则表达式:
(?<=[\.\?\!])(?<=[^A-Z].)(?<=[^0-9].)(?<=[^N]..)(?<=[^o].)
我决定首先尝试修复 No.
,最后两个条件。但它依赖于独立匹配 N
和 o
,我认为这会在其他地方出现误报。我无法弄清楚如何让它只制作句点后面的字符串 No
。然后,我将对 Sgt.
和我遇到的任何其他 "problem" 字符串使用类似的方法。
我正在尝试使用类似的东西:
(?<=[\.\?\!])(?<=[^A-Z].)(?<=[^0-9].)^(?<=^No$)
但在那之后它没有捕捉到任何东西。我怎样才能让它排除某些我希望其中有句点的字符串,而不是捕获它们?
这是我的情况的正则表达式:https://regexr.com/4sgcb
只使用一个正则表达式会很棘手 - 正如评论中所述,有很多边缘情况。
我自己会分三步完成:
- 将应该保留的 space 替换为一些特殊字符 (
re.sub
)
- 拆分文本 (
re.split
)
- 将特殊字符替换为space
例如:
import re
zero_width_space = '\u200B'
s = 'I am from New York, N.Y. and I would like to say hello! How are you today? I am well. I owe you . 00 because you bought me a No. 3 burger. -Sgt. Smith'
s = re.sub(r'(?<=\.)\s+(?=[\da-z])|(?<=,)\s+|(?<=Sgt\.)\s+', zero_width_space, s)
s = re.split(r'(?<=[.?!])\s+', s)
from pprint import pprint
pprint([line.replace(zero_width_space, ' ') for line in s])
打印:
['I am from New York, N.Y. and I would like to say hello!',
'How are you today?',
'I am well.',
'I owe you . 00 because you bought me a No. 3 burger.',
'-Sgt. Smith']
这是我能得到的最接近的正则表达式(尾随 space 是我们匹配的那个):
(?<=(?<!(No|\.\w))[\.\?\!])(?! *\d+ *)
它也会在 Sgt.
之后拆分,原因很简单,即后向断言必须在 Python 中固定宽度(限制!)。
这就是我在 vim
中的做法,它没有这样的限制(尾随 space 是我们匹配的那个):
\(\(No\|Sgt\|\.\w\)\@<![?.!]\)\( *\d\+ *\)\@!\zs
对于 OP 以及休闲 reader, 都是环顾四周,非常有趣。
您可以考虑一种匹配方法,它可以让您更好地控制要计为单个词而不是断句信号的实体。
使用类似
的模式
\s*((?:\d+\.\s*\d+|(?:No|M[rs]|[JD]r|S(?:r|gt))\.|\.(?!\s+-?[A-Z0-9])|[^.!?])+(?:[.?!]|$))
它与我发布的内容非常相似 ,但它包含一个匹配格式不正确的浮点数的模式,添加了 No.
和 Sgt.
缩写支持以及更好地处理未以最后一句标点符号结尾的字符串。
import re
p = re.compile(r'\s*((?:\d+\.\s*\d+|(?:No|M[rs]|[JD]r|S(?:r|gt))\.|\.(?!\s+-?[A-Z0-9])|[^.!?])+(?:[.?!]|$))')
s = "I am from New York, N.Y. and I would like to say hello! How are you today? I am well. I owe you . 00 because you bought me a No. 3 burger. -Sgt. Smith"
for m in p.findall(s):
print(m)
输出:
I am from New York, N.Y. and I would like to say hello!
How are you today?
I am well.
I owe you . 00 because you bought me a No. 3 burger.
-Sgt. Smith
图案详情
\s*
- 匹配 0 个或多个空格(用于 trim 结果)
(?:\d+\.\s*\d+|(?:No|M[rs]|[JD]r|S(?:r|gt))\.|\.(?!\s+-?[A-Z0-9])|[^.!?])+
- 多个替代词出现一次或多次:
\d+\.\s*\d+
- 1+ 位,.
,0+ 空格,1+ 位
(?:No|M[rs]|[JD]r|S(?:r|gt))\.
- 缩写字符串,如 No.
、Mr.
、Ms.
、Jr.
、Dr.
、Sr.
、 Sgt.
\.(?!\s+-?[A-Z0-9])
- 匹配后跟一个或多个空格的点,然后是可选的 -
和大写字母或数字
|
- 或
[^.!?]
- 除了 .
、!
和 ?
之外的任何字符
(?:[.?!]|$)
- .
、!
和 ?
或字符串结尾。
正如我在上面的评论中提到的,如果您无法定义一组固定的边缘情况,那么如果没有误报或漏报,这可能是不可能的。同样,如果没有上下文,您将无法区分“-Sgt. Smith”等缩写和 "Sergeant is often times abbreviated as Sgt. This makes it shorter.".
等句子结尾
但是,如果您可以 定义一组固定的边缘情况,则分多个步骤执行此操作可能更容易且可读性更高。
1.确定您的边缘案例
例如,您可以通过检查后续数字来区分 "Ill have a No. 3" 和 "No. I am your father"。所以你会用这样的正则表达式来识别边缘情况:No. \d
。 (同样,上下文很重要。像 "Is 200 enough? No. 200 is not enough." 这样的句子仍然会给你一个误报)
2。掩盖你的边缘情况
对于每个边缘情况,用 100% 不属于原始文本的相应字符串屏蔽字符串。例如。 "No." => "======NUMBER======"
3。 运行你的算法
现在您已经删除了不需要的标点符号,您可以 运行 一个更简单的正则表达式来识别真正的积极因素:[\.\!\?]\s
4.揭开你的边缘案例
将“======NUMBER======”转回"No."
我正在尝试匹配标点符号后的空格,以便我可以拆分大量文本,但我看到一些常见的边缘情况,包括地点、标题和常见缩写:
I am from New York, N.Y. and I would like to say hello! How are you today? I am well. I owe you . 00 because you bought me a No. 3 burger. -Sgt. Smith
我将它与 Python 中的 re.split
函数一起使用 3 我想得到这个:
["I am from New York, N.Y. and I would like to say hello!",
"How are you today?",
"I am well.",
"I owe you . 00 because you bought me a No. 3 burger."
"-Sgt. Smith"]
这是我目前的正则表达式:
(?<=[\.\?\!])(?<=[^A-Z].)(?<=[^0-9].)(?<=[^N]..)(?<=[^o].)
我决定首先尝试修复 No.
,最后两个条件。但它依赖于独立匹配 N
和 o
,我认为这会在其他地方出现误报。我无法弄清楚如何让它只制作句点后面的字符串 No
。然后,我将对 Sgt.
和我遇到的任何其他 "problem" 字符串使用类似的方法。
我正在尝试使用类似的东西:
(?<=[\.\?\!])(?<=[^A-Z].)(?<=[^0-9].)^(?<=^No$)
但在那之后它没有捕捉到任何东西。我怎样才能让它排除某些我希望其中有句点的字符串,而不是捕获它们?
这是我的情况的正则表达式:https://regexr.com/4sgcb
只使用一个正则表达式会很棘手 - 正如评论中所述,有很多边缘情况。
我自己会分三步完成:
- 将应该保留的 space 替换为一些特殊字符 (
re.sub
) - 拆分文本 (
re.split
) - 将特殊字符替换为space
例如:
import re
zero_width_space = '\u200B'
s = 'I am from New York, N.Y. and I would like to say hello! How are you today? I am well. I owe you . 00 because you bought me a No. 3 burger. -Sgt. Smith'
s = re.sub(r'(?<=\.)\s+(?=[\da-z])|(?<=,)\s+|(?<=Sgt\.)\s+', zero_width_space, s)
s = re.split(r'(?<=[.?!])\s+', s)
from pprint import pprint
pprint([line.replace(zero_width_space, ' ') for line in s])
打印:
['I am from New York, N.Y. and I would like to say hello!',
'How are you today?',
'I am well.',
'I owe you . 00 because you bought me a No. 3 burger.',
'-Sgt. Smith']
这是我能得到的最接近的正则表达式(尾随 space 是我们匹配的那个):
(?<=(?<!(No|\.\w))[\.\?\!])(?! *\d+ *)
它也会在 Sgt.
之后拆分,原因很简单,即后向断言必须在 Python 中固定宽度(限制!)。
这就是我在 vim
中的做法,它没有这样的限制(尾随 space 是我们匹配的那个):
\(\(No\|Sgt\|\.\w\)\@<![?.!]\)\( *\d\+ *\)\@!\zs
对于 OP 以及休闲 reader,
您可以考虑一种匹配方法,它可以让您更好地控制要计为单个词而不是断句信号的实体。
使用类似
的模式\s*((?:\d+\.\s*\d+|(?:No|M[rs]|[JD]r|S(?:r|gt))\.|\.(?!\s+-?[A-Z0-9])|[^.!?])+(?:[.?!]|$))
它与我发布的内容非常相似 No.
和 Sgt.
缩写支持以及更好地处理未以最后一句标点符号结尾的字符串。
import re
p = re.compile(r'\s*((?:\d+\.\s*\d+|(?:No|M[rs]|[JD]r|S(?:r|gt))\.|\.(?!\s+-?[A-Z0-9])|[^.!?])+(?:[.?!]|$))')
s = "I am from New York, N.Y. and I would like to say hello! How are you today? I am well. I owe you . 00 because you bought me a No. 3 burger. -Sgt. Smith"
for m in p.findall(s):
print(m)
输出:
I am from New York, N.Y. and I would like to say hello!
How are you today?
I am well.
I owe you . 00 because you bought me a No. 3 burger.
-Sgt. Smith
图案详情
\s*
- 匹配 0 个或多个空格(用于 trim 结果)(?:\d+\.\s*\d+|(?:No|M[rs]|[JD]r|S(?:r|gt))\.|\.(?!\s+-?[A-Z0-9])|[^.!?])+
- 多个替代词出现一次或多次:\d+\.\s*\d+
- 1+ 位,.
,0+ 空格,1+ 位(?:No|M[rs]|[JD]r|S(?:r|gt))\.
- 缩写字符串,如No.
、Mr.
、Ms.
、Jr.
、Dr.
、Sr.
、Sgt.
\.(?!\s+-?[A-Z0-9])
- 匹配后跟一个或多个空格的点,然后是可选的-
和大写字母或数字|
- 或[^.!?]
- 除了.
、!
和?
之外的任何字符
(?:[.?!]|$)
-.
、!
和?
或字符串结尾。
正如我在上面的评论中提到的,如果您无法定义一组固定的边缘情况,那么如果没有误报或漏报,这可能是不可能的。同样,如果没有上下文,您将无法区分“-Sgt. Smith”等缩写和 "Sergeant is often times abbreviated as Sgt. This makes it shorter.".
等句子结尾但是,如果您可以 定义一组固定的边缘情况,则分多个步骤执行此操作可能更容易且可读性更高。
1.确定您的边缘案例
例如,您可以通过检查后续数字来区分 "Ill have a No. 3" 和 "No. I am your father"。所以你会用这样的正则表达式来识别边缘情况:No. \d
。 (同样,上下文很重要。像 "Is 200 enough? No. 200 is not enough." 这样的句子仍然会给你一个误报)
2。掩盖你的边缘情况
对于每个边缘情况,用 100% 不属于原始文本的相应字符串屏蔽字符串。例如。 "No." => "======NUMBER======"
3。 运行你的算法
现在您已经删除了不需要的标点符号,您可以 运行 一个更简单的正则表达式来识别真正的积极因素:[\.\!\?]\s
4.揭开你的边缘案例
将“======NUMBER======”转回"No."