PHP正则表达式:如何匹配\r和\n而不使用[\r\n]?

19

我已经测试了\v(垂直空白)是否与\r\n及其组合匹配,但我发现\v不匹配\r\n。以下是我正在使用的代码:

$string = "
Test
";

if (preg_match("#\v+#", $string )) {
  echo "Matched";
} else {
  echo "Not Matched";
}

更明确地说,我的问题是,是否存在匹配\r\n的其他替代方法?


1
你可以使用\s+,我猜。你不想使用\r\n的原因是什么? - Jerry
@Jerry:\s 匹配任何空白字符。 - Jason OOO
2
是的,我知道,但你要求一个替代\r\n的方法,而\s可以匹配。 - Jerry
1
@Jerry:好的,但它不仅仅是匹配\r\n,我猜\v可能有用,但它并没有起作用。 - Jason OOO
2
@Jerry,请看下面的答案,你可以使用PCRE中使用的\R代替\s - hwnd
显示剩余2条评论
7个回答

45

PCRE和换行符

PCRE有超多与换行符相关的转义序列和替代方案。

一个很棒的转义序列是\R。默认情况下,\R将匹配Unicode的换行符,但是可以使用不同的替代方案来进行配置。

要匹配任何在ASCII范围内的Unicode换行符序列。

preg_match('~\R~', $string);

这相当于以下组:

(?>\r\n|\n|\r|\f|\x0b|\x85)

为了匹配任何Unicode换行序列,包括ASCII范围之外的换行符和行分隔符(U+2028)和段落分隔符(U+2029),您需要打开u(unicode)标志。
preg_match('~\R~u', $string);
u (unicode) 修饰符打开 PCRE 的附加功能,模式字符串被视为 (UTF-8)。
这等同于以下组:
(?>\r\n|\n|\r|\f|\x0b|\x85|\x{2028}|\x{2029})

我们可以限制\R只匹配 CRLFCRLF

preg_match('~(*BSR_ANYCRLF)\R~', $string);

这等同于以下组合:

(?>\r\n|\n|\r)

补充

支持五种不同的约定方式来指示字符串中的换行:

(*CR)        carriage return
(*LF)        linefeed
(*CRLF)      carriage return, followed by linefeed
(*ANYCRLF)   any of the three above
(*ANY)       all Unicode newline sequences
注意: \R 在字符类中没有特殊意义。与其他未识别的转义序列一样,默认情况下将其视为文字字符“R”。

哇!我从未使用过,这正是我一直在寻找的 :) 请看此示例:http://phpfiddle.org/main/code/phd-ebj - Jason OOO
3
此答案已添加到Stack Overflow正则表达式FAQ的“转义序列”部分。 - aliteralmind
1
+1 对于 \R。仅限学术目的,如果您不在 u 模式下,则可以发明另一种匹配 \r\n 的方法,而不使用它们:(?![ \t\cK\f])\s 为什么?因为 \s 匹配 [ \t\cK\f\r\n],所以这是一种类减法。 :) - zx81
小心。我在使用捕获组“〜\ R〜”与俄语单词时遇到了问题。当将此正则表达式应用于单词“необходимости”时,它变成了“необ� одимости”。 - Pedro Sousa
@PedroSousa,为什么你省略了 u 模式修饰符?在输入字符串中读取多字节字符时,您需要告诉正则表达式引擎。 - mickmackusa
对其他人来说这可能很显然,但我曾认为 ~\R~ 中的波浪线是转义序列的一部分。其实这些波浪线只是分隔符,通常情况下使用的是 /。所以有趣的部分只有 \R - Tyler Collier

8

这并没有回答关于替代方案的问题,因为\v可以完美地工作。

\v匹配任何被认为是垂直空白的字符;这包括平台的回车和换行符(newline)以及其他几个字符,全部列在下表中。

您只需要将"#\v+#"更改为以下之一:

  • "#\\v+#" 转义反斜杠

或者

  • '#\v+#' 使用单引号

在两种情况下,您都将得到\r\n的任意组合的匹配。

更新:

只是为了明确\v的范围与\R相比较,来自perlrebackslash

  • \R
    \R匹配通用换行符;也就是说,Unicode认为是换行序列的所有内容。这包括所有\v匹配的字符(垂直空白),...

6

如果有一些奇怪的需求阻止你在模式中使用字面上的[\r\n],你总是可以使用十六进制转义序列代替:

preg_match('#[\xD\xA]+#', $string)

这个模式等同于 [\r\n]+

1
为了匹配给定字符串的每一行,只需使用^$锚点,并建议您的正则表达式引擎在多行模式下运行。然后^$将匹配每行的开头和结尾,而不是整个字符串的开头和结尾。

http://php.net/manual/en/reference.pcre.pattern.modifiers.php

在PHP中,这将是模式后面的“m”修饰符。使用/^(.*?)$/m将简单匹配给定字符串内由任何垂直空格分隔的每一行。
顺便说一下:对于行拆分,您还可以使用split()PHP_EOL常量:
$lines = explode(PHP_EOL, $string);

0

在 PHP 中匹配换行符,请使用 php 常量 PHP_EOL。这是跨平台的。

if (preg_match('/\v+' . PHP_EOL ."/", $text, $matches ))
   print_R($matches );

小心贪婪匹配!你可能会得到太多。 - Byron Whitlock

0

这个正则表达式也匹配换行符\n和回车符\r

(?![ \t\f])\s

演示

为了匹配一个或多个换行符或回车符,您可以使用以下正则表达式。

(?:(?![ \t\f])\s)+

演示


0

这并不是使 \v 匹配 \r\n。 - Jason OOO
1
多行模式无关紧要。许多正则表达式用户会认为,只要目标字符串包含行分隔符,就必须指定多行模式。实际上,它只是调整了锚点(^$)的行为,使它们在行边界(即行分隔符之前和之后)匹配。OP 的正则表达式并不包含任何锚点。 - Alan Moore

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