如何匹配不在段落末尾的句号?

3

如果我想找到所有位于段落末尾的句点,可以使用\.($|\n)。但是如何否定它并表示“后面跟着任何不是这些字符之一的字符”,因为元字符无法在字符类中使用,这阻止了我使用否定字符类?


@Braj 我也有同样的想法。在Java、{C#、PHP、Python}以及{JS、Ruby}中,"$"有三种不同的含义 :) - zx81
你需要使用双换行符作为段落终止符,因此在你原本的\n处使用类似于\n{2,}的表达式可能是最好的选择。然而,在某些情况下,单独的换行符本身并不足以表示段落分隔符(例如在Markdown或电子邮件中),甚至在HTML中也可能不够用。 - tchrist
@zx81 实际上不止3个。 - tchrist
4个回答

5

美元符号$有什么作用?这要看情况!

答案非常取决于您使用的语言和正则表达式引擎。你看,

  1. 在Java中,$断言我们定位在字符串末尾或者在字符串末尾的任何回车符或换行符之前。因此,您可以使用\.(?!$)来确保安全。
  2. 在PCRE、C#和Python中,$断言我们定位在字符串末尾或在字符串末尾的任何换行符之前。因此,您可以使用\.(?!$|\r)
  3. 在JavaScript和Ruby中,$断言我们定位在字符串的末尾。因此,您需要使用完整的正则表达式:\.(?!$|[\r\n])

因此,对于多引擎方案,最安全的办法是:

\.(?!$|[\r\n])

但是在正确的上下文中,另外两个选项也是完全可以接受的。

说明

  • \. 匹配字面上的句号
  • 负向先行断言 (?!$|[\r\n]) 断言后面不是“字符串的结尾”、回车符或换行符。

UTS #18 强烈建议 \R 成为正则表达式语言的一部分,这并非没有原因。请查看我的回答。 - tchrist
我一直使用标准的“字符串结尾”匹配方式。 - temporary_user_name
我一直使用标准的“字符串结尾”方式。是的,这正是我理解的。这个答案的重点是你在不同的语言中以不同的方式表达它,并且它为你提供了在这些不同语言中的选项。例如,在JS中,你选择的答案将无法工作!在Windows文件中,它会匹配字符串末尾的句号。 - zx81

4

使用负向先行断言来实现此目的。

\.(?!\n|$)

解释:

\.          '.'
(?!         look ahead to see if there is not:
  \n          '\n' (newline)
 |           OR
  $           before an optional \n, and the end of the string
)           end of look-ahead

Live Demo


1
只需使用 (?x) 并从一开始就用注释编写即可完成。 - tchrist

4
最有用的长手写版本的负向预测行尾检查在句号之后会使整个模式变成这样:
   (?x:           # enable comments
        \.        # a literal dot character
        (?!       # look ahead for not the following{
            \R ?  #    optional EOL grapheme cluster
            \z    #    at the true end of string
        )         # } end look ahead
   )

假设您不想将其与“插页式”(即,在任何换行符表意字符之前)匹配,这将更简单:
(?=\R)

有人认为将\R?改为\R*可能更合适,以防万一记录末尾有多个行终止符,比如连续几个换行符。这样可以允许0、1、2或者更多的EOL图形符号出现在字符串末尾。
另一方面,一个段落至少要有两个EOL图形符号,而不仅仅是一个。例如,在这里标记和其他“空行分隔”的段落文件中都是如此。因此,没有EOL也可以,而两个或更多也可以,但不能只有一个。
对于这样的文本,您需要使用\R{2,},但整个部分都是可选的,如果这种情���下需要,则返回结果如下:
   (?x:           # enable comments
        \.        # a literal dot character
        (?!       # look ahead for NOT the following {
            (?:
                \R {2,}   # two or more EOL grapheme clusters
            ) ?   #       # optionally
            \z    #    at the true end of string
        )         # } end negated look ahead
   )  

如果您的正则表达式中没有来自UTS 18:Unicode 正则表达式- 行边界\R,那么您将不得不用更为烦人的方法把它写出来:

 (?x:              # We are emulating \R per UTS#18 
      (?>          # Prohibit backtrack within subpattern
          \r \n    # Match a CRLF without backtracking
                   # or else any code point with the 
                   # vertical space character property
                   # \p{VertSpace}, here enumerated in full 
        | [\x0A-\x0D\x85\x{2028}\x{2029}] 
       )         
 )

你需要使用“不回溯位”来避免像\R{2}这样的匹配只能匹配一个CRLF,而不能这样做。
最后需要考虑的一件事是你是否希望允许可选的水平空格介入到句点和EOL之间。我认为你可能会想要这样做,但没有更紧密的正式规范,就无法确定。

1
感谢您引导我来看您的答案。 :) 我不想提及\R,因为我担心我的答案已经有点复杂了(事实上,OP选择了一个在Windows上可以匹配字符串末尾句号的解决方案...)。但是您的讨论绝对是精湛的,就像我偶然发现的其他文章一样。从现在开始,我可能不得不寻找它们。 :) 实际上,仅仅今天早上,我就将您的一个答案添加到了“我喜欢阅读的答案”部分中!+1,很抱歉我不能+5 :) - zx81

2

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接