Como usar a expressão regular JavaScript em várias linhas?

275
var ss= "<pre>aaaa\nbbb\nccc</pre>ddd";
var arr= ss.match( /<pre.*?<\/pre>/gm );
alert(arr);     // null

Eu gostaria que o bloco PRE fosse escolhido, mesmo que se estendesse por caracteres de nova linha. Eu pensei que a bandeira 'm' faz isso. Não.

Encontre a resposta aqui antes de postar. Desde que achei que sabia JavaScript (li três livros, trabalhei horas) e não havia uma solução existente na SO, ousarei postar de qualquer maneira. jogue pedras aqui

Então a solução é:

var ss= "<pre>aaaa\nbbb\nccc</pre>ddd";
var arr= ss.match( /<pre[\s\S]*?<\/pre>/gm );
alert(arr);     // <pre>...</pre> :)

Alguém tem uma maneira menos enigmática?

Edit: esta é uma duplicata, mas como é mais difícil de encontrar que a minha, não a removo.

Ele propõe [^]como um "ponto multilinha". O que ainda não entendo é por [.\n]que não funciona. Acho que essa é uma das partes tristes do JavaScript.

akauppi
fonte
29
Um regex menos enigmático? Impossível, por natureza.
Rubens Farias
btw, você deve ler: "Analisando Html: The Cthulhu Way" codinghorror.com/blog/archives/001311.html
Rubens Farias
1
O link foi alterado em relação ao comentário anterior: blog.codinghorror.com/parsing-html-the-cthulhu-way (5yrs-ish later)
dab

Respostas:

248

[.\n]não funciona porque .não tem um significado especial dentro [], significa apenas um literal .. (.|\n)seria uma maneira de especificar "qualquer caractere, incluindo uma nova linha". Se você quiser combinar todas as novas linhas, você precisa adicionar \r, assim como para incluir o Windows e clássicos finais de linha estilo Mac OS: (.|[\r\n]).

Isso acaba sendo um tanto complicado e lento (consulte a resposta de KrisWebDev para obter detalhes ); portanto, uma abordagem melhor seria combinar todos os caracteres de espaço em branco e todos os caracteres que não sejam de espaço em branco com [\s\S], o que corresponderá a tudo e é mais rápido e rápido. mais simples.

Em geral, você não deve tentar usar uma regexp para corresponder às tags HTML reais. Veja, por exemplo, estas perguntas para obter mais informações sobre o porquê.

Em vez disso, tente realmente pesquisar no DOM a tag que você precisa (o uso do jQuery facilita isso, mas você sempre pode fazer document.getElementsByTagName("pre")com o DOM padrão) e, em seguida, pesquise o conteúdo de texto desses resultados com uma expressão regular se precisar corresponder ao conteúdo .

Brian Campbell
fonte
O que estou fazendo é fazer a conversão de .wiki -> HTML rapidamente, usando JavaScript. Portanto, ainda não tenho o DOM disponível. O arquivo Wiki é principalmente sua própria sintaxe, mas eu permito que tags HTML sejam usadas, se necessário. Seu conselho é muito válido, se eu estivesse lidando com DOM com isso. Obrigado. :)
akauppi
Justo. Suponho que seja uma razão válida para querer usar expressões regulares no HTML, embora as sintaxes do wiki misturadas ao HTML possam ter todos os tipos de casos de canto divertidos.
Brian Campbell
2
[\r\n]aplicado a uma sequência \ r \ n, corresponderia primeiro a \ re depois \ n. Se você deseja corresponder a seqüência inteira de uma vez, independentemente de essa sequência está \ r \ n ou apenas \ n, use o padrão.|\r?\n
Eirik Birkeland
1
Para corresponder a uma sequência multilinha inteira , tente o ganancioso [\s\S]+.
Boaz
Eu só quero acrescentar para a posteridade que a sintaxe JS regex, ignorando o significado de .dentro, []é diferente de outras estruturas regex, principalmente a avançada no .NET. Gente, por favor, não assuma que as expressões regulares são multiplataforma, elas frequentemente não são !!
Sr. TA
330

NÃO use em (.|[\r\n])vez de .para correspondência de várias linhas.

Use em [\s\S]vez de .para correspondência multilinha

Além disso, evite a ganância onde não for necessário usando *?ou +?quantificador em vez de *ou +. Isso pode ter um enorme impacto no desempenho.

Veja a referência que eu fiz: http://jsperf.com/javascript-multiline-regexp-workarounds

Using [^]: fastest
Using [\s\S]: 0.83% slower
Using (.|\r|\n): 96% slower
Using (.|[\r\n]): 96% slower

NB: Você também pode usar, [^]mas está obsoleto no comentário abaixo.

KrisWebDev
fonte
22
Bons pontos, mas eu recomendo não usar de [^]qualquer maneira. Por um lado, o JavaScript é o único sabor que conheço que suporta esse idioma e, mesmo lá, ele é usado nem de longe com a mesma frequência [\s\S]. Por outro lado, a maioria dos outros sabores permite escapar do ]listando-o primeiro. Em outras palavras, em JavaScript [^][^]combina quaisquer dois caracteres, mas em .NET que corresponde a qualquer um outro personagem que ], [ou ^.
Alan Moore
1
Como você sabe que isso \Sirá corresponder \rou \ncontra algum outro personagem?
Gili
3
Veja esta pergunta para detalhes de \ s \ S. Este é um truque para corresponder a todos os caracteres de espaço em branco + todos os caracteres que não sejam espaços em branco = todos os caracteres. Consulte também MDN para documentação de caracteres especiais regexp.
KrisWebDev
4
Algum motivo para preferir [\s\S]a outros, como [\d\D]ou [\w\W]?
Phrogz
1
Deixe-me salientar rapidamente que seu teste para o operador ganancioso é manipulado. /<p>Can[^]*?<\/p>/não corresponde ao mesmo conteúdo que /<p>Can[^]*<\/p>/. A variante gananciosa deve ser alterada /<p>(?:[^<]|<(?!\/p>))*<\/p>/para corresponder ao mesmo conteúdo.
3limin4t0r
19

Você não especifica o seu ambiente e a versão do Javascript (ECMAscript), e eu sei que este post foi de 2009, mas, para completar, com o lançamento do ECMA2018, agora podemos usar o ssinalizador para fazer .corresponder '\ n', consulte https : //stackoverflow.com/a/36006948/141801

Portanto:

let s = 'I am a string\nover several\nlines.';
console.log('String: "' + s + '".');

let r = /string.*several.*lines/s; // Note 's' modifier
console.log('Match? ' + r.test(s); // 'test' returns true

Esta é uma adição recente e não funcionará em muitos ambientes atuais, por exemplo, o Nó v8.7.0 parece não reconhecê-lo, mas funciona no Chromium e estou usando-o em um teste de Typecript que estou escrevendo e, presumivelmente, se tornará mais popular com o passar do tempo.

Neek
fonte
1
Isso funciona muito bem no Chrome (V67), mas rompe completamente o regex (também pára linha-a-linha de trabalho) no IE11 e Iedge (v42)
freedomn-m
Obrigado @ freedomn-m .. O IE não suporta um recurso muito novo é quase totalmente surpreendente :) Mas sim, vale a pena mencionar onde não funciona para salvar alguém tentando 'depurar' porque sua tentativa de usá-lo não está funcionando como esperado.
Neek
11

[.\n]não funciona, porque ponto [](por definição de regex; não apenas javascript) significa o caractere de ponto. Você pode usar (.|\n)(ou (.|[\n\r])) em vez disso.

Y. Shoham
fonte
24
[\s\S]é o idioma JavaScript mais comum para corresponder a tudo, incluindo novas linhas. É mais fácil para os olhos e muito mais eficiente do que uma abordagem baseada em alternância (.|\n). (É, literalmente, significa "qualquer caractere que é espaço em branco ou qualquer caractere que não seja espaço em branco.)
Alan Moore
2
Você está certo, mas a pergunta era sobre .e \n, e por [.\n]que não funciona. Como mencionado na pergunta, [^]também é uma boa abordagem.
Y. Shoham
6

Eu testei (Chrome) e funcionou para mim (ambos [^]e [^\0]), alterando o ponto ( .) por um [^\0]ou outro [^], porque o ponto não corresponde à quebra de linha (veja aqui:http://www.regular-expressions.info/dot.html ).

var ss= "<pre>aaaa\nbbb\nccc</pre>ddd";
var arr= ss.match( /<pre[^\0]*?<\/pre>/gm );
alert(arr);     //Working

Hzzkygcs
fonte
1
O problema [^\0]é que ele não corresponderá a caracteres nulos, mesmo que caracteres nulos sejam permitidos em strings Javascript (consulte esta resposta ).
Donald Duck
0

Além dos exemplos acima mencionados, é uma alternativa.

^[\\w\\s]*$

Onde \wé para palavras e \sé para espaços em branco

azhar22k
fonte