我需要一个正则表达式来匹配字符串的第一个句子

时间:2011-06-25 16:24:52

标签: regex

我正在努力想出一个与字符串的第一句匹配的正则表达式。我不需要任何太复杂的东西,只需要以“。”或“!”或“?”结尾的句子注意后面的空格,以便句子“我需要这个域名!”将匹配。我在regexlib.com上也找不到任何东西。 到目前为止,我坚持这个:

([^.|?|!]*)[\.\s\?\s\!\s]

它并没有真正起作用。如果我尝试匹配

"I need this domain.com! Another sentence. And another sentence."

我只得到“我需要这个域名。”我需要它“我需要这个域名!”

4 个答案:

答案 0 :(得分:6)

试试这个:

^.*?[\.!\?](?:\s|$)

答案 1 :(得分:2)

这是一个通过原始测试的模式,同时也解决了Vitali Ponomar关于manji答案的括号的评论。

^.*?[.!?](?:\s|$)(?!.*\))

这使用negated lookahead有效地说:

  1. 从头开始并按任意次数匹配任何字符,但尽可能少的时间仍允许以下内容保持为真。
  2. 我们看到其中一个字符:. ? !
  3. 接下来是:空白字符行尾
  4. 哪个,后跟任何导致)右括号字符的内容。
  5. 这利用了这样一个事实,即只要它是平衡的,我们就知道括号组的结束位置。因此,如果由于用户输入或处理不当等而导致句子格式错误,则可能会失败。

    您可以通过声明“句子的开头”标记必须包含大写字符来添加一定程度的保护。

    ^.*?[.!?](?:\s[A-Z]|$)(?!.*\))
    

    这是可取的原因是因为,在大多数程序中,在连接字符串之前将字符串大写更容易,并确保在括号内正确平衡字符串。

    请注意,由于OP使用non-capturing组接受了答案,例如(?:foo),我也使用了一个。{这将导致“句子的开头”标记包含在匹配中。您可能会也可能不想这样做,具体取决于您是仅依靠空格字符还是我添加的大写检查。

    我的建议是不要包含它,您可以使用lookahead来代替(?=foo)

    ^.*?[.!?](?=\s[A-Z]|$)(?!.*\))
    

    现在我们没有在比赛中包括残骸,让我们来处理在我们的第一句话之后只有空格的情况:

    ^.*?[.!?](?=\s[A-Z]|\s?$)(?!.*\))
    

    现在用这个相当不错的模式进行一些测试:

    • 输入:“我需要这个domain.com!另一句话。另一句话。”

      匹配:“我需要这个domain.com!”

    • 输入:“这是第一个(例如第一个)句子。第二个。”

      匹配:“这是第一个(例如第一个)句子。”

    • 输入:“这是一个破碎的(例如第一句。第二句。”

      匹配:“这是一个破碎的(例如第一句。”

    • 输入:“这最让人兴奋......但不是我。”

      匹配:“这最让人兴奋......”

    大。但仍有一些地方会倒塌。例如:引号。句子很复杂!要做到这一点,你真的需要考虑给定语言的整个标点规则,然后提出一个算法,它不会假设每个人都会完全遵循它们,并且在不引入奇怪匹配的情况下使某些部分成为可选项。一旦你沿着这条路走下去,你就会得到一个很长的,不可读的表达式,有很多greed operators?问号的某些用法)。

    最后,它主要归结为程序的输入是什么样的,它来自何处,以及在对其应用复杂模式匹配之前如何预先处理它。通常,它更可靠,更易读,但性能更低,可以进行更小,更简单的模式的多次传递。一个用于删除或删除您不关心的内容(如换行符或其他空白字符),然后用于删除可能的恶意输入痕迹,等等。随着输入的简化,慢慢变得越来越复杂。< / p>

答案 2 :(得分:0)

尚未测试,但应该这样做

^([^.|?|!]+)

问题是*匹配零个或多个字符,而+匹配至少一个字符

答案 3 :(得分:0)

(我将使用Java正则表达式语法编写,因为这就是我所知道的;它应该与我们使用的任何其他正则表达式系统相同,但我不是百分之百确定。)

句子边界的正则表达式显然是[.!?]\s。所以,你希望将所有内容与第一个相匹配。 “。+”贪婪地匹配并匹配最后一句开头的所有内容。你想要一个不情愿的捕获:

(.+?)[.!?]\s