Skip to content

Latest commit

 

History

History
49 lines (29 loc) · 5.82 KB

007-点操作符.md

File metadata and controls

49 lines (29 loc) · 5.82 KB

7.1 .可以(几乎)匹配任何字符

句点是正则表达式中最常用的字符之一,但是同时它也是最常被用错的元字符之一。

句号可以匹配除了行终结符\r\n以外的任何字符。在本教程所涉及的引擎都会默认不匹配行终结符。

句号不能匹配行终结符主要是历史原因造成的。因为第一个使用正则表达式的工具是基于行来匹配的,它会把文件按行读取,并用正则匹配每一行。所以每一次匹配中都不包含行终结符,句号也不会去匹配行终结符。

现代的正则工具或者编程语言可以匹配非常大的字符串甚至是一个文件,所以大部分引擎可以通过配置使句号可以匹配行终结符。但是javascript和VBScript没有这项配置。

在javascript和VBScript中,你可以使用[\s\S]来匹配所有的字符。因为它可以匹配一个空白字符(包含了行终结符)或者非空白字符,所以它可以匹配到所有的字符。

7.2 行终结符

虽然所有的引擎都支持点操作符,但是他们对行终结符的定义各有不同。所有的引擎都把\n作为行终结符,UNIX中的文本文件把\n作为换行符,Windows使用\r\n,所有的脚本语言都把\n作为换行符。这些脚本语言在windows上读取文件时,如果遇到\r\n会自动转为\n,在写文件是如果遇到\n则会自动转为\r\n

待翻译内容 std::regex, XML Schema and XPath also treat the carriage return \r as a line break character. JavaScript adds the Unicode line separator \u2028 and page separator \u2029 on top of that. Java includes these plus the Latin-1 next line control character \u0085. Boost adds the form feed \f to the list. Only Delphi and the JGsoft flavor supports all Unicode line breaks, completing the mix with the vertical tab.

.NET is notably absent from the list of flavors that treat characters other than \n as line breaks. Unlike scripting languages that have their roots in the UNIX world, .NET is a Windows development framework that does not automatically strip carriage return characters from text files that it reads. If you read a Windows text file as a whole into a string, it will contain carriage returns. If you use the regex abc.* on that string, without setting RegexOptions.SingleLine, then it will match abc plus all characters that follow on the same line, plus the carriage return at the end of the line, but without the newline after that.

Some flavors allow you to control which characters should be treated as line breaks. Java has the UNIX_LINES option which makes it treat only \n as a line break. PCRE has options that allow you to choose between \n only, \r only, \r\n, or all Unicode line breaks.

On POSIX systems, the POSIX locale determines which characters are line breaks. The C locale treats only the newline \n as a line break. Unicode locales support all Unicode line breaks.

7.3 请不要过度依赖句号

句号的功能非常强大,但是它使人们变得懒惰。在编写正则表达式时,只要加入句号就可以轻易的匹配所有的合法测试数据,但是它的问题在于句号也会匹配上原本不应该匹配的数据。有些时候这些错误非常隐蔽。

我们使用一个日期的例子来说明这个问题。假设我们要匹配以下格式的日期mm/dd/yy,但是我们允许用户来选择分隔符。最简单的方法是\d\d.\d\d.\d\d。一开始看起来没有什么问题,他可以匹配02/12/03。但是它同样可以匹配02512703,因为句号可以匹配数字。

\d\d[- /.]\d\d[- /.]\d\d 是一个更好方案。它可以匹配以- .为分隔符的日期。你还记得吗,.在字符集中是不需要转义的。

但这还远没有达到完美的程度,因为它可以匹配99/99/99。我们可以继续优化为[01]\d[- /.][0-3]\d[- /.]\d\d,但是它任然可以匹配19/39/99。其实我们应该根据实际需要来决定正则表达式的精确程度。如果我们用来匹配用户的输入,那么必须非常完美。如果它的数据是来自于数据库或者某个稳定的数据源,那么之前我们做的最后一个尝试也许是一种效率更高的选择,同时避免了错误。如何你想知道如何精确的匹配日期,你可以看日期的匹配

7.4 使用字符集取反来取代句号

使用字符集取反来匹配字符串通常要比使用句号更好。有关这一技术的细节我们会在重复操作符中详细讨论。但是出于这一结论的重要性,我们有必要在这里提前叙述其中的一部分。我们还是通过例子来说明。

假设这次我们的目标是匹配被引号引起来的字符串。这乍一听很容易,我们只要找到引号中间的任意多个字符就可以了,所以最简单的答案是".*"。其中.可以匹配任意字符,而*可以匹配任意多次。如果你用它来匹配Put a "string" between double quotes,那么它会匹配到"string"。但是你用它来匹配Houston, we have a problem with "string one" and "string two". Please respond.时,它会匹配到"string one" and "string two"。这是因为*是贪婪的。

在日期的例子中我们使用一个字符集来替代句号,在引号的例子中我们可以使用字符集取反。我们之前的尝试是有缺陷的,其实我们要匹配的不是引号中间的任何字符串,而是引号之间包含任意数量的引号和换行符之外的字符。所以更好的正则表达式是"[^"\r\n]*".


如果文章出现错误,请给我提Issues - - Github地址

需要进一步翻译的内容:

  • 重复操作符
  • 日期的匹配

原文