Como faço para corresponder qualquer caractere em várias linhas em uma expressão regular?

358

Por exemplo, este regex

(.*)<FooBar>

irá corresponder:

abcde<FooBar>

Mas como faço para corresponder em várias linhas?

abcde
fghij<FooBar>
andyuk
fonte
11
Esclarecer; Eu estava usando originalmente o Eclipse para encontrar e substituir em vários arquivos. O que descobri pelas respostas abaixo é que meu problema era a ferramenta e não o padrão regex.
andyuk
2
Sua sinalização "eclipse" deve ser removida, pois quem procura uma solução para eclipse encontrará essa pergunta (como eu fiz) e, em seguida, encontrará uma solução não-eclipse como a aceita.
Acme
2
Agora estou encontrando isso no mecanismo de pesquisa porque o eclipse foi mencionado. Oh, o horror.
21918 Brian Olsen

Respostas:

240

Depende do idioma, mas deve haver um modificador que você pode adicionar ao padrão regex. No PHP é:

/(.*)<FooBar>/s

O s no final faz com que o ponto corresponda a todos os caracteres, incluindo novas linhas.

Jeremy Ruten
fonte
e se eu quisesse apenas uma nova linha e nem todos os caracteres?
Graça
3
@Grace: use \ n para corresponder a uma nova linha
Jeremy Ruten 11/11
5
O sinalizador s é (agora?) Inválido, pelo menos no Chrome / V8. . Em vez disso use / ([\ s \ S] *) <FooBar> / classe de caracteres (espaço jogo e não-espaço] em vez do período de correspondência Veja outras respostas para mais informações.
Allen
8
@ Allen - JavaScript não suporta o smodificador. Em vez disso, faça [^]*o mesmo efeito.
Derek朕會功夫
11
Em Ruby, use o mmodificador
Ryan Buckley
358

Tente o seguinte:

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

Diz basicamente "qualquer caractere ou uma nova linha" repetida zero ou mais vezes.

levik
fonte
5
Isso depende do idioma e / ou ferramenta que você está usando. Por favor, deixe-nos saber o que você está usando, por exemplo, Perl, PHP, CF, C #, sed, awk, etc.
Ben Perdição
39
Dependendo de suas terminações de linha que você pode precisar((.|\n|\r)*)<FooBar>
Potherca
3
Ele disse que está usando o Eclipse. Esta é a solução correta na minha opinião. Eu tenho o mesmo problema e isso resolveu.
Danubian Sailor
4
Certo - a questão é sobre eclipse e as tags também. Mas a solução aceita é uma solução PHP. O seu deve ser a solução aceite ...
Acme
16
Este é o pior regex para correspondência de entrada de várias linhas. Nunca o use, a menos que você esteja usando o ElasticSearch. Use [\s\S]*ou (?s).*.
Wiktor Stribiżew
89

A questão é: o .padrão pode corresponder a qualquer caractere? A resposta varia de mecanismo para mecanismo. A principal diferença é se o padrão é usado por uma biblioteca de expressões regulares POSIX ou não POSIX.

Nota especial sobre : eles não são considerados expressões regulares, mas .correspondem a qualquer caractere igual aos mecanismos baseados no POSIX.

Outra nota sobre e : .corresponde a qualquer caractere por padrão ( demo ): str = "abcde\n fghij<Foobar>"; expression = '(.*)<Foobar>*'; [tokens,matches] = regexp(str,expression,'tokens','match');( tokenscontém um abcde\n fghijitem).

Além disso, em todos gramáticas regex, o ponto corresponde a quebras de linha por padrão. A gramática ECMAScript do Boost permite desativar isso com regex_constants::no_mod_m( fonte ).

Quanto a (é baseado em POSIX), use a nopção ( demo ):select regexp_substr('abcde' || chr(10) ||' fghij<Foobar>', '(.*)<Foobar>', 1, 1, 'n', 1) as results from dual

Mecanismos baseados em POSIX :

Um mero .já corresponde a quebras de linha, não há necessidade de usar nenhum modificador, consulte( demo ).

o ( demo ),( demo ),(TRE, motor de base R padrão sem perl=TRUE, para a base R com perl=TRUEou para stringr / Stringi padrões, use o (?s)modificador de linha) ( demonstração ) também tratar .da mesma maneira.

No entanto , a maioria das ferramentas baseadas no POSIX processa a entrada linha por linha. Portanto, .não corresponde às quebras de linha apenas porque elas não estão no escopo. Aqui estão alguns exemplos de como substituir isso:

  • - Existem várias soluções alternativas, a mais precisa, mas não muito segura sed 'H;1h;$!d;x; s/\(.*\)><Foobar>/\1/'( H;1h;$!d;x;coloca o arquivo na memória). Se for necessário incluir linhas inteiras, sed '/start_pattern/,/end_pattern/d' file(a remoção do início terminará com as linhas correspondentes incluídas) ou sed '/start_pattern/,/end_pattern/{{//!d;};}' file(com as linhas correspondentes excluídas) poderá ser considerada.
  • - perl -0pe 's/(.*)<FooBar>/$1/gs' <<< "$str"( -0coloca o arquivo inteiro na memória, -pimprime o arquivo após aplicar o script fornecido por -e). Observe que usar -000peirá arrastar o arquivo e ativar o 'modo de parágrafo' onde o Perl usa novas linhas consecutivas ( \n\n) como separador de registros.
  • - grep -Poz '(?si)abc\K.*?(?=<Foobar>)' file. Aqui, zhabilita o slurping de arquivo, (?s)habilita o modo DOTALL para o .padrão , habilita o modo sem distinção entre (?i)maiúsculas e minúsculas, \Komite o texto correspondente até agora, *?é um quantificador lento, (?=<Foobar>)corresponde ao local antes <Foobar>.
  • - pcregrep -Mi "(?si)abc\K.*?(?=<Foobar>)" file( Mativa o slurping de arquivo aqui). Nota pcregrepé uma boa solução para grepusuários de Mac OS .

Veja demos .

Mecanismos não baseados em POSIX :

  • - Use o smodificador PCRE_DOTALL : preg_match('~(.*)<Foobar>~s', $s, $m)( demo )
  • - Usar RegexOptions.Singlelinesinalizador ( demo ):
    - var result = Regex.Match(s, @"(.*)<Foobar>", RegexOptions.Singleline).Groups[1].Value;
    -var result = Regex.Match(s, @"(?s)(.*)<Foobar>").Groups[1].Value;
  • - Use (?s)a opção embutida:$s = "abcde`nfghij<FooBar>"; $s -match "(?s)(.*)<Foobar>"; $matches[1]
  • - Use smodificador (ou (?s)versão embutida no início) ( demo ):/(.*)<FooBar>/s
  • - Uso re.DOTALL(ou re.S) bandeiras ou (?s)modificador inline ( demonstração ): m = re.search(r"(.*)<FooBar>", s, flags=re.S)(e, em seguida if m:, print(m.group(1)))
  • - Use Pattern.DOTALLmodificador (ou (?s)sinalizador embutido ) ( demo ):Pattern.compile("(.*)<FooBar>", Pattern.DOTALL)
  • - Use (?s)modificador dentro do padrão ( demo ):regex = /(?s)(.*)<FooBar>/
  • - Use (?s)modificador ( demo ):"(?s)(.*)<Foobar>".r.findAllIn("abcde\n fghij<Foobar>").matchData foreach { m => println(m.group(1)) }
  • - Uso [^]ou soluções alternativas [\d\D]/ [\w\W]/ [\s\S]( demo ):s.match(/([\s\S]*)<FooBar>/)[1]
  • ( std::regex) Use [\s\S]ou as soluções alternativas JS ( demo ):regex rex(R"(([\s\S]*)<FooBar>)");
  • - Use a mesma abordagem que em JavaScript ([\s\S]*)<Foobar>,. ( OBSERVAÇÃO : Às vezes, considera-se erroneamente que a MultiLinepropriedade do RegExpobjeto é a opção para permitir a .correspondência entre quebras de linha, enquanto, na verdade, apenas altera o comportamento ^e $para corresponder ao início / fim de linhas em vez de cadeias de caracteres , o mesmo que no regex JS ) comportamento.)

  • - Use o modificador /m MULTILINE ( demo ):s[/(.*)<Foobar>/m, 1]

  • - Regexps PCRE base R - use (?s): regmatches(x, regexec("(?s)(.*)<FooBar>",x, perl=TRUE))[[1]][2]( demo )
  • - funções in stringr/ stringiregex que são alimentadas com o mecanismo regex ICU, também use (?s): stringr::str_match(x, "(?s)(.*)<FooBar>")[,2]( demo )
  • - Use o modificador embutido (?s)no início ( demo ):re: = regexp.MustCompile(`(?s)(.*)<FooBar>`)
  • - Use dotMatchesLineSeparatorsou (mais fácil) passe o (?s)modificador em linha para o padrão:let rx = "(?s)(.*)<Foobar>"
  • - O mesmo que Swift, (?s)funciona da maneira mais fácil, mas eis como a opção pode ser usada :NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionDotMatchesLineSeparators error:&regexError];
  • , - Usar (?s)modificador ( demo ): "(?s)(.*)<Foobar>"(nas planilhas do Google =REGEXEXTRACT(A2,"(?s)(.*)<Foobar>"))

NOTAS SOBRE(?s) :

Na maioria dos mecanismos que não sejam POSIX, o (?s)modificador em linha (ou opção de sinalizador incorporado) pode ser usado para aplicar .para corresponder a quebras de linha.

Se colocado no início do padrão, (?s)altera o comportamento de todos .no padrão. Se o (?s)item for colocado em algum lugar após o início, apenas os .afetados serão localizados à direita, a menos que esse seja um padrão passado para o Python re. No Python re, independentemente da (?s)localização, todo o padrão .é afetado. O (?s)efeito é parado de usar (?-s). Um grupo modificado pode ser usado para afetar apenas um intervalo especificado de um padrão de regex (por exemplo Delim1(?s:.*?)\nDelim2.*, fará a primeira .*?correspondência entre as novas linhas e a segunda .*corresponderá apenas ao restante da linha).

Nota POSIX :

Em mecanismos regex não POSIX, para corresponder a qualquer caractere, [\s\S]/ [\d\D]/ [\w\W]construções podem ser usadas.

No POSIX, [\s\S]não corresponde a nenhum caractere (como no JavaScript ou em qualquer mecanismo que não seja POSIX) porque as sequências de escape regex não são suportadas dentro das expressões de colchete. [\s\S]é analisado como expressões de colchete que correspondem a um único caractere \ou sou S.

Wiktor Stribiżew
fonte
5
Você deve vincular a esta excelente visão geral na sua página de perfil ou algo assim (+1).
Jan
11
Convém adicionar isso ao item de impulso : No espaço de nome regex_constants, os tipos de flag_tipo: perl = ECMAScript = JavaScript = JScript = :: boost :: regbase :: normal = 0, cujo padrão é Perl. Os programadores definirão uma definição de sinalizador base #define MOD regex_constants::perl | boost::regex::no_mod_s | boost::regex::no_mod_mpara seus sinalizadores de expressão regular para refletir isso. E o árbitro é sempre os modificadores em linha. Onde (?-sm)(?s).*redefine.
11
Você também pode adicionar para o bash, por favor?
Pasupathi Rajamanickam
2
O @PasupathiRajamanickam Bash usa um mecanismo de regex POSIX, que .corresponde a qualquer caractere lá (incluindo quebras de linha). Veja esta demonstração online do Bash .
Wiktor Stribiżew
11
Você é demais - este é o mini-tutorial mais exaustivo sobre expressões regulares (relativamente) complexas que eu já vi. Você merece que sua resposta seja a aceita! Parabéns e votos extras por incluir Gona resposta!
Gwyneth Llewelyn
68

Se você estiver usando a pesquisa Eclipse, poderá ativar a opção "DOTALL" para criar '.' corresponda a qualquer caractere, incluindo delimitadores de linha: basta adicionar "(? s)" no início da string de pesquisa. Exemplo:

(?s).*<FooBar>
Paulo Merson
fonte
11
Não em qualquer lugar, só na sabores regex apoio modificadores em linha, e certamente não em Ruby, onde (?s)=>(?m)
Wiktor Stribiżew
Algo para o bash?
Pasupathi Rajamanickam
38

Em muitos dialetos regex, /[\S\s]*<Foobar>/fará exatamente o que você deseja. Fonte

Abbas Shahzadeh
fonte
2
Nesse link: "JavaScript e VBScript não têm uma opção para fazer com que os pontos correspondam aos caracteres de quebra de linha. Nesses idiomas, você pode usar uma classe de caracteres como [\ s \ S] para corresponder a qualquer caractere." Ao invés de . use [\ s \ S] (combine espaços e não espaços).
Allen
32

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

O ponto corresponde a todos, exceto às novas linhas (\ r \ n). Portanto, use \ s \ S, que corresponderá a TODOS os caracteres.

samwize
fonte
Isso resolve o problema se você estiver usando o Objective-C [text rangeOfString:regEx options:NSRegularExpressionSearch]. Obrigado!
243 J. Costa
11
Isso funciona na localização e substituição de expressões regulares do inteliJ, obrigado.
Barclay
Isso funciona. Mas precisa ser a primeira ocorrência de #<FooBar>
Ozkan
13

nós também podemos usar

(.*?\n)*?

para combinar com tudo, incluindo nova linha sem ganancioso

Isso tornará a nova linha opcional

(.*?|\n)*?
Nambi_0915
fonte
8

"."normalmente não corresponde a quebras de linha. A maioria dos mecanismos de expressão regular permite adicionar o S-flag (também chamado DOTALLe SINGLELINE) para fazer com que "."também correspondam novas linhas. Se isso falhar, você pode fazer algo parecido [\S\s].

Markus Jarderot
fonte
8

Para o Eclipse funcionou a seguinte expressão:

Foo

jadajada Bar "

Expressão regular:

Foo[\S\s]{1,10}.*Bar*
Gordon
fonte
5
/(.*)<FooBar>/s

s faz com que Dot (.) corresponda aos retornos de carro

Conta
fonte
Parece que este é inválido (Chrome): text.match (/ a / s) SyntaxError: Sinalizadores inválidos fornecidos ao construtor RegExp 's'
Allen
Porque não é suportado nos mecanismos JavaScript RegEx. Os ssinalizadores existem no PCRE, o mecanismo mais completo (disponível em Perl e PHP). O PCRE possui 10 sinalizadores (e muitos outros recursos) enquanto o JavaScript possui apenas 3 sinalizadores ( gmi).
Morgan Touverey Quilling
4

Na expressão regular baseada em java, você pode usar [\s\S]

Kamahire
fonte
11
Não deveriam ser barras invertidas?
Paul Draper
Eles vão no final da Expressão Regular, e não dentro. Exemplo: / blah / s
RandomInsano
Eu acho que você quer dizer JavaScript, não Java? Como você pode adicionar a ssinalização ao padrão em Java, o JavaScript não possui a ssinalização.
3limin4t0r
3

Observe que (.|\n)*pode ser menos eficiente do que (por exemplo) [\s\S]*(se as expressões regulares do seu idioma suportam essas fugas) e descobrir como especificar o modificador que faz. também correspondem a novas linhas. Ou você pode optar por alternativas como POSIXy [[:space:][:^space:]]*.

amarrar
fonte
3

Use RegexOptions.Singleline, ele altera o significado de. para incluir novas linhas

Regex.Replace (conteúdo, searchText, replaceText, RegexOptions.Singleline);

shmall
fonte
1

No contexto do uso em idiomas, expressões regulares atuam em strings, não em linhas. Portanto, você deve poder usar a regex normalmente, assumindo que a sequência de entrada tenha várias linhas.

Nesse caso, o regex especificado corresponderá a toda a cadeia, pois "<FooBar>" está presente. Dependendo das especificidades da implementação do regex, o valor $ 1 (obtido de "(. *)") Será "fghij" ou "abcde \ nfghij". Como já foi dito, algumas implementações permitem controlar se o "." corresponderá à nova linha, dando a você a escolha.

O uso de expressões regulares com base em linhas é geralmente para coisas de linha de comando como egrep.

nsayer
fonte
1

Eu tive o mesmo problema e resolvi-o provavelmente não da melhor maneira, mas funciona. Substituí todas as quebras de linha antes de fazer minha correspondência real:

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

Estou manipulando o HTML para que as quebras de linha não sejam realmente importantes para mim neste caso.

Eu tentei todas as sugestões acima sem sorte, estou usando .Net 3.5 FYI

Slee
fonte
Também estou usando o .NET e (\s|\S)parece fazer o truque para mim!
Vamshi Krishna
@VamshiKrishna No .NET, use (?s)para fazer .corresponder quaisquer caracteres. Não use (\s|\S)isso para diminuir o desempenho.
Wiktor Stribiżew
1

Em Javascript, você pode usar [^] * para procurar por zero a infinitos caracteres, incluindo quebras de linha.

$("#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>

Paul Chris Jones
fonte
0

geralmente. não corresponde a novas linhas, então tente((.|\n)*)<foobar>

abordagem
fonte
3
Não faça isso. Se você precisar corresponder a qualquer coisa, incluindo separadores de linha, use o modificador DOTALL (aka / s ou SingleLine). O hack (. | \ N) não apenas torna o regex menos eficiente, como também não é correto. No mínimo, ele deve corresponder a \ r (retorno de carro) e \ n (avanço de linha). Também existem outros caracteres separadores de linha, embora raramente usados. Mas se você usar o sinalizador DOTALL, não precisará se preocupar com eles.
277 Alan Moore
11
\ R é a correspondência independente de plataforma para novas linhas no Eclipse.
opyate
@opyate Você deve postar isso como resposta, pois esta pequena jóia é incrivelmente útil.
jeckhart
Você pode tentar fazer isso. Não vai coincidir com os suportes internos e também considerar o opcional \r:.((?:.|\r?\n)*)<foobar>
SSC-hrep3
0

Eu queria combinar um bloco particular se em java

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

   }
...
...
}

Se eu usar o regExp

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

incluía a chave de fechamento do bloco de método, então eu usei

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

para excluir a chave de fechamento da correspondência de curinga.

Spangen
fonte
0

Freqüentemente, precisamos modificar uma substring com algumas palavras-chave espalhadas pelas linhas que precedem a substring. Considere um elemento xml:

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

Suponha que desejemos modificar o 81, para algum outro valor, digamos 40. Primeiro identifique .UID.21..UID.e depois pule todos os caracteres, incluindo \naté .PercentCompleted.. O padrão de expressão regular e a especificação de substituição são:

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>

O subgrupo (.|\n)é provavelmente o grupo ausente $3. Se fizermos a não captura até (?:.|\n)então, o $3é (<PercentComplete>). Portanto, o padrão e replaceSpectambém pode ser:

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

e a substituição funciona corretamente como antes.

user1348737
fonte
0

Normalmente, procurando três linhas consecutivas no Powershell, seria semelhante a:

$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

Estranhamente, isso seria texto unix no prompt, mas o texto do Windows em um arquivo:

$pattern = 'lineone
linetwo
linethree
'

Aqui está uma maneira de imprimir as terminações de linha:

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

# output
lineone\nlinetwo\nlinethree\n
js2010
fonte
-2

Opção 1

Uma maneira seria usar a sbandeira (assim como a resposta aceita):

/(.*)<FooBar>/s

Demo 1

opção 2

Uma segunda maneira seria usar o msinalizador (multilinha) e qualquer um dos seguintes padrões:

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

ou

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

ou

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

Demo 2

Circuito RegEx

O jex.im visualiza expressões regulares:

insira a descrição da imagem aqui

Emma
fonte