如何在正则表达式的多行中匹配任何字符?


355

例如,此正则表达式

(.*)<FooBar>

将匹配:

abcde<FooBar>

但是我如何才能使其跨多行匹配?

abcde
fghij<FooBar>

1
澄清; 我最初使用Eclipse来查找并替换多个文件。我从下面的答案中发现的是我的问题是工具而不是正则表达式模式。
andyuk

2
然后应删除您的标记“ eclipse”,因为一个正在寻找日食解决方案的人会找到这个问题(就像我所做的那样),然后找到一个非日食解决方案作为接受的问题。
acme 2012年

2
现在,我在搜索引擎中找到了它,因为提到了eclipse。哦,恐怖。
布莱恩·奥尔森

Answers:


240

它取决于语言,但是应该有一个可以添加到正则表达式模式的修饰符。在PHP中是:

/(.*)<FooBar>/s

末尾的s使点匹配所有字符,包括换行符。


如果我想要的只是一个新的生产线,而不是所有的角色?
恩典

3
@Grace:使用\ n匹配换行符
Jeremy Ruten 2011年

5
至少在Chrome / V8中,s标志无效(现在?)。而是使用/([\ s \ S] *)<FooBar> /字符类(匹配空格和非空格)代替句点匹配器。有关更多信息,请参见其他答案。–
Allen

8
@Allen-JavaScript不支持s修饰符。相反,[^]*要达到相同的效果。
德里克·朕会功夫2015年

1
在Ruby中,使用m修饰符
Ryan Buckley

355

尝试这个:

((.|\n)*)<FooBar>

它基本上说“任何字符或换行符”重复零次或多次。


5
这取决于您使用的语言和/或工具。请让我们知道你用的是什么,如Perl中,PHP,CF,C#,用sed,awk的,等等
奔死命

39
根据您的行尾,您可能需要((.|\n|\r)*)<FooBar>
Potherca'3

3
他说他正在使用Eclipse。我认为这是正确的解决方案。我有同样的问题,这解决了它。
Danubian Sailor'Apr

4
是的-问题是关于日食的,标签也是。但是公认的解决方案是PHP解决方案。您应该成为公认的解决方案...
acme 2012年

16
这是匹配多行输入最差的正则表达式。除非您正在使用ElasticSearch,否则请不要使用它。使用[\s\S]*(?s).*
WiktorStribiżew16年

88

问题是,.模式可以匹配任何字符吗?答案因引擎而异。主要区别在于该模式是由POSIX还是非POSIX正则表达式库使用的。

关于特别说明 :它们不被认为是正则表达式,但.与基于POSIX的引擎相同,可以匹配其中的任何char。

另一个注意 :在.默认情况下,任何字符(匹配演示): str = "abcde\n fghij<Foobar>"; expression = '(.*)<Foobar>*'; [tokens,matches] = regexp(str,expression,'tokens','match');tokensabcde\n fghij项目)。

而且,在所有 的regex语法默认情况下点匹配换行符。Boost的ECMAScript语法使您可以使用regex_constants::no_mod_msource)将其关闭。

至于 (基于POSIX),使用n选项demo):select regexp_substr('abcde' || chr(10) ||' fghij<Foobar>', '(.*)<Foobar>', 1, 1, 'n', 1) as results from dual

基于POSIX的引擎

.已匹配换行符,无需使用任何修饰符,请参见演示)。

演示),演示),(TRE,不带base R的默认引擎perl=TRUE,对于具有perl=TRUE或用于stringr / stringi模式的base R ,请使用(?s)inline修饰符)(demo)也.以相同的方式处理。

但是,大多数基于POSIX的工具都是逐行处理输入的。因此,.由于行号不在范围内,因此与行号不匹配。以下是一些如何覆盖此示例:

  • -有多种解决方法,最精确但也不是很安全的方法sed 'H;1h;$!d;x; s/\(.*\)><Foobar>/\1/'H;1h;$!d;x;将文件拖入内存)。如果必须包括整行,则可以考虑sed '/start_pattern/,/end_pattern/d' file(从开始删除将以包括匹配的行结束)或sed '/start_pattern/,/end_pattern/{{//!d;};}' file(不包括匹配的行)。
  • - perl -0pe 's/(.*)<FooBar>/$1/gs' <<< "$str"-0将整个文件保存到内存中,-p应用给出的脚本后打印文件-e)。请注意,-000pe在Perl使用连续换行符(\n\n)作为记录分隔符的情况下,using 将对文件进行处理并激活“段落模式” 。
  • - grep -Poz '(?si)abc\K.*?(?=<Foobar>)' file。在这里,z启用文件特征分析,(?s)为模式启用DOTALL模式.(?i)启用不区分大小写的模式,\K省略到目前为止*?已匹配的文本,是一个懒惰的量词,(?=<Foobar>)之前匹配的位置<Foobar>
  • - pcregrep -Mi "(?si)abc\K.*?(?=<Foobar>)" fileM在此处启用文件拖曳功能)。pcregrep对于Mac OS grep用户而言,Note 是一个很好的解决方案。

参见演示

非基于POSIX的引擎

  • -使用s修改PCRE_DOTALL修饰符preg_match('~(.*)<Foobar>~s', $s, $m)演示
  • -使用RegexOptions.Singleline标志(演示
    ): var result = Regex.Match(s, @"(.*)<Foobar>", RegexOptions.Singleline).Groups[1].Value;
    - -var result = Regex.Match(s, @"(?s)(.*)<Foobar>").Groups[1].Value;
  • -使用(?s)内联选项:$s = "abcde`nfghij<FooBar>"; $s -match "(?s)(.*)<Foobar>"; $matches[1]
  • -使用s修饰符(或(?s)开始时使用内联版本)(demo):/(.*)<FooBar>/s
  • -使用re.DOTALL(或re.S)标志或(?s)内联修饰符(demo):(m = re.search(r"(.*)<FooBar>", s, flags=re.S)然后if m:print(m.group(1))
  • -使用Pattern.DOTALL修饰符(或内联(?s)标志)(demo):Pattern.compile("(.*)<FooBar>", Pattern.DOTALL)
  • -使用(?s)模式内修饰符(demo):regex = /(?s)(.*)<FooBar>/
  • -使用(?s)修饰符(demo):"(?s)(.*)<Foobar>".r.findAllIn("abcde\n fghij<Foobar>").matchData foreach { m => println(m.group(1)) }
  • -使用[^]或解决方法[\d\D]/ [\w\W]/ [\s\S]演示):s.match(/([\s\S]*)<FooBar>/)[1]
  • std::regex)使用[\s\S]或JS解决方法(demo):regex rex(R"(([\s\S]*)<FooBar>)");
  • -使用与JavaScript中相同的方法([\s\S]*)<Foobar>。(:本MultiLine该财产 RegExp的对象有时错误地认为是允许选择.跨行匹配,而事实上,它只会改变^$行为,以匹配开始/结束线,而不是字符串,相同JS正则表达式)的行为。)

  • -使用/m MULTILINE修饰符demo):s[/(.*)<Foobar>/m, 1]

  • -基础R PCRE正则表达式-使用(?s)regmatches(x, regexec("(?s)(.*)<FooBar>",x, perl=TRUE))[[1]][2]演示
  • - 由ICU regex引擎提供支持的in stringr/ stringiregex功能,也可以使用(?s)stringr::str_match(x, "(?s)(.*)<FooBar>")[,2]demo
  • - (?s)在开始时使用inline修饰符(demo):re: = regexp.MustCompile(`(?s)(.*)<FooBar>`)
  • -使用dotMatchesLineSeparators或(更轻松)将(?s)内联修饰符传递给模式:let rx = "(?s)(.*)<Foobar>"
  • -与Swift一样,(?s)最简单,但是可以使用以下选项NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionDotMatchesLineSeparators error:&regexError];
  • -使用(?s)修饰符(demo):("(?s)(.*)<Foobar>"在Google Spreadsheets中,=REGEXEXTRACT(A2,"(?s)(.*)<Foobar>")

注意事项(?s)

在大多数非POSIX引擎中,(?s)可以使用内联修饰符(或嵌入式标志选项)来强制.匹配换行符。

如果放置在模式的开头,请(?s)更改模式中所有行为.。如果将(?s)放置在开始位置之后,则只会.影响位于其右侧的内容,除非这是传递给Python的模式re。在Python中re,无论(?s)位置在哪里,整个模式都会.受到影响。该(?s)效果被停止使用(?-s)。修改后的组只能用于影响正则表达式模式的指定范围(例如,Delim1(?s:.*?)\nDelim2.*使第一个.*?匹配跨换行,而第二个.*匹配仅匹配行的其余部分)。

POSIX注意事项

在非POSIX正则表达式引擎,以匹配任何字符,[\s\S]/ [\d\D]/ [\w\W]构建体都可以使用。

在POSIX中,[\s\S]不匹配任何字符(如JavaScript或任何非POSIX引擎中的字符),因为括号表达式内不支持正则表达式转义序列。[\s\S]被解析为与单个char \sor 匹配的方括号表达式S


5
您应该从个人资料页面或其他链接(+1)链接到此出色的概述。
1

1
您可能希望将其添加到增强项:在regex_constants命名空间中,flag_type_'s:perl = ECMAScript = JavaScript = JScript = :: boost :: regbase :: normal = 0,默认为Perl。程序员将为它们#define MOD regex_constants::perl | boost::regex::no_mod_s | boost::regex::no_mod_m的正则表达式标志设置一个基本标志定义,以反映这一点。并且Arrbitor 始终是内联修饰符。在哪里(?-sm)(?s).*重置。

1
您还可以添加bash吗?
Pasupathi Rajamanickam,

2
@PasupathiRajamanickam Bash使用POSIX正则表达式引擎,.匹配那里的任何字符(包括换行符)。请参阅此在线Bash演示
维克多·斯特里比维(WiktorStribiżew)

1
令人震惊-这是我见过的(相对)复杂正则表达式中最详尽的迷你教程。您值得您的回答成为公认的答案!包括Go答案在内的荣誉和额外投票!
Gwyneth Llewelyn

68

如果您使用的是Eclipse搜索,则可以启用“ DOTALL”选项将其设为“。”。匹配任何字符,包括行定界符:只需在搜索字符串的开头添加“(?s)”。例:

(?s).*<FooBar>

1
在任何地方都没有,仅在支持内联修饰符的正则表达式中,当然在Ruby中也没有,其中(?s)=>(?m)
WiktorStribiżew16年

有什么好玩的吗?
Pasupathi Rajamanickam,

38

在许多正则表达式中,方言/[\S\s]*<Foobar>/都能满足您的需求。资源


2
通过该链接:“ JavaScript和VBScript没有使点匹配换行符的选项。在这些语言中,可以使用字符类,例如[\ s \ S]来匹配任何字符。” 代替。使用[\ s \ S](匹配空格和非空格)代替。
艾伦

32

([\s\S]*)<FooBar>

点匹配除换行符(\ r \ n)以外的所有字符。因此,请使用\ s \ S,它将匹配所有字符。


如果您使用的是Objective-C,则可以解决此问题[text rangeOfString:regEx options:NSRegularExpressionSearch]。谢谢!
J. Costa

1
感谢intelliJ的find&replace正则表达式。
barclay

这可行。但这必须是第一次出现<FooBar>
Ozkan


13

我们也可以使用

(.*?\n)*?

匹配包括换行符在内的所有内容而无需贪婪

这将使新行成为可选行

(.*?|\n)*?

8

"."通常与换行符不匹配。大多数正则表达式引擎都允许您添加S-flag(也称为DOTALLSINGLELINE)以使其"."也匹配换行符。如果失败,则可以执行[\S\s]


8

对于Eclipse,它的表达式如下:

oo

jadajada Bar”

正则表达式:

Foo[\S\s]{1,10}.*Bar*

5
/(.*)<FooBar>/s

s使点(。)匹配回车符


似乎是无效的(Chrome):text.match(/ a / s)语法错误:向RegExp构造函数's'提供了无效的标志
艾伦(Allen)

因为JavaScript RegEx引擎不支持它。这些s标志存在于PCRE中,PCRE是最完整的引擎(在Perl和PHP中可用)。PCRE有10个标志(以及许多其他功能),而JavaScript只有3个标志(gmi)。
Morgan Touverey Quilling '16

4

在基于Java的正则表达式中,您可以使用 [\s\S]


1
这些不应该是反斜杠吗?
保罗·德雷珀

它们位于正则表达式的末尾,而不位于in的末尾。示例:/ blah / s
RandomInsano 2013年

我猜你是说JavaScript,而不是Java?由于您可以将s标记添加到Java中的模式中,而JavaScript没有该s标记。
3limin4t0r

3

请注意,这(.|\n)*可能比(例如)效率低(例如,[\s\S]*如果您的语言正则表达式支持此类转义),则效率可能不及寻找如何指定makes的修饰符。也匹配换行符。或者,您可以使用POSIXy替代方案[[:space:][:^space:]]*


3

使用RegexOptions.Singleline,它将更改的含义。包括换行符

Regex.Replace(content,searchText,replaceText,RegexOptions.Singleline);



1

在语言中使用时,正则表达式作用于字符串,而不是行。因此,假设输入字符串有多行,您应该能够正常使用正则表达式。

在这种情况下,给定的正则表达式将匹配整个字符串,因为存在“ <FooBar>”。取决于正则表达式实现的细节,$ 1值(从“(。*)”获得)将是“ fghij”或“ abcde \ nfghij”。正如其他人所说,某些实现允许您控制是否为“。”。将与换行符匹配,为您提供选择。

基于行的正则表达式通常用于命令行,例如egrep。


1

我遇到了同样的问题,并可能不是以最佳方式解决了它,但它确实有效。在进行真正的比赛之前,我替换了所有换行符:

mystring= Regex.Replace(mystring, "\r\n", "")

我正在处理HTML,因此在这种情况下换行符对我而言并不重要。

我没有运气就尝试了上面的所有建议,我正在使用.Net 3.5 FYI


我也在使用.NET,(\s|\S)似乎可以帮我这个忙!
Vamshi克里希纳

@VamshiKrishna在.NET中,使用(?s)做出.匹配任何字符。不要使用(\s|\S)那会降低性能。
WiktorStribiżew18年

1

在Javascript中,您可以使用[^] *搜索零到无限个字符,包括换行符。

$("#find_and_replace").click(function() {
  var text = $("#textarea").val();
  search_term = new RegExp("[^]*<Foobar>", "gi");;
  replace_term = "Replacement term";
  var new_text = text.replace(search_term, replace_term);
  $("#textarea").val(new_text);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<button id="find_and_replace">Find and replace</button>
<br>
<textarea ID="textarea">abcde
fghij&lt;Foobar&gt;</textarea>


0

通常。与换行符不匹配,请尝试((.|\n)*)<foobar>


3
不,不要那样做。如果需要匹配任何内容,包括行分隔符,请使用DOTALL(又名/ s或SingleLine)修饰符。(。| \ n)hack不仅使正则表达式的效率降低,甚至不正确。至少,它应与\ r(回车符)以及\ n(换行符)匹配。尽管很少使用,但还有其他的行分隔符。但是,如果使用DOTALL标志,则不必担心它们。
艾伦·摩尔

1
\ R是Eclipse中换行符的与平台无关的匹配项。
2009年

@opyate您应该将其发布为答案,因为这个小宝石非常有用。
jeckhart 2012年

您可以改用此方法。它不会与内括号匹配,并且还会考虑使用可选的\r。:((?:.|\r?\n)*)<foobar>
ssc-hrep3,2016年

0

我想匹配Java中的特定if块

   ...
   ...
   if(isTrue){
       doAction();

   }
...
...
}

如果我使用regExp

if \(isTrue(.|\n)*}

它包括方法块的右括号,所以我使用了

if \(!isTrue([^}.]|\n)*}

从通配符匹配中排除右括号。


0

通常,我们必须使用一些关键字来修改子字符串,这些关键字分布在子字符串之前的行中。考虑一个xml元素:

<TASK>
  <UID>21</UID>
  <Name>Architectural design</Name>
  <PercentComplete>81</PercentComplete>
</TASK>

假设我们想将81修改为其他值,例如40。首先确定.UID.21..UID.,然后跳过所有字符,包括\n直到.PercentCompleted.。正则表达式模式和替换规范为:

String hw = new String("<TASK>\n  <UID>21</UID>\n  <Name>Architectural design</Name>\n  <PercentComplete>81</PercentComplete>\n</TASK>");
String pattern = new String ("(<UID>21</UID>)((.|\n)*?)(<PercentComplete>)(\\d+)(</PercentComplete>)");
String replaceSpec = new String ("$1$2$440$6");
//note that the group (<PercentComplete>) is $4 and the group ((.|\n)*?) is $2.

String  iw = hw.replaceFirst(pattern, replaceSpec);
System.out.println(iw);

<TASK>
  <UID>21</UID>
  <Name>Architectural design</Name>
  <PercentComplete>40</PercentComplete>
</TASK>

该子组(.|\n)可能是缺少的组$3。如果我们不进行捕获,(?:.|\n)$3is (<PercentComplete>)。因此,模式replaceSpec也可以是:

pattern = new String("(<UID>21</UID>)((?:.|\n)*?)(<PercentComplete>)(\\d+)(</PercentComplete>)");
replaceSpec = new String("$1$2$340$5")

并且替换可以像以前一样正常工作。


0

通常在Powershell中搜索三个连续的行,看起来就像:

$file = get-content file.txt -raw

$pattern = 'lineone\r\nlinetwo\r\nlinethree\r\n'     # "windows" text
$pattern = 'lineone\nlinetwo\nlinethree\n'           # "unix" text
$pattern = 'lineone\r?\nlinetwo\r?\nlinethree\r?\n'  # both

$file -match $pattern

# output
True

奇怪的是,这将是提示符下的unix文本,但是文件中的Windows文本:

$pattern = 'lineone
linetwo
linethree
'

这是打印行尾的一种方法:

'lineone
linetwo
linethree
' -replace "`r",'\r' -replace "`n",'\n'

# output
lineone\nlinetwo\nlinethree\n

-2

选项1

一种方法是使用s标志(就像接受的答案一样):

/(.*)<FooBar>/s

演示1

选项2

第二种方法是使用m(多行)标志和以下任何一种模式:

/([\s\S]*)<FooBar>/m

要么

/([\d\D]*)<FooBar>/m

要么

/([\w\W]*)<FooBar>/m

演示2

RegEx电路

jex.im可视化正则表达式:

在此处输入图片说明

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.