¿Cómo hago coincidir cualquier carácter en varias líneas en una expresión regular?

358

Por ejemplo, esta expresión regular

(.*)<FooBar>

coincidirá:

abcde<FooBar>

Pero, ¿cómo hago para que coincida en varias líneas?

abcde
fghij<FooBar>
andyuk
fuente
1
Para aclarar; Originalmente estaba usando Eclipse para hacer una búsqueda y reemplazar en múltiples archivos. Lo que descubrí con las respuestas a continuación es que mi problema era la herramienta y no el patrón de expresiones regulares.
Andy
2
Su bandera "eclipse" debe eliminarse porque quien busque una solución de eclipse encontrará esta pregunta (como yo lo hice) y luego encontrará una solución que no sea de eclipse como la aceptada.
acme
2
Ahora estoy encontrando esto en el motor de búsqueda porque se mencionó eclipse. Oh el horror
Brian Olsen

Respuestas:

240

Depende del idioma, pero debe haber un modificador que pueda agregar al patrón regex. En PHP es:

/(.*)<FooBar>/s

La s al final hace que el punto coincida con todos los caracteres, incluidas las nuevas líneas.

Jeremy Ruten
fuente
¿Y si solo quisiera una nueva línea y no todos los personajes?
Grace
3
@Grace: utilice \ n para que coincida con un salto de línea
Jeremy Ruten
55
La bandera s es (¿ahora?) Inválida, al menos en Chrome / V8. En su lugar, use / ([\ s \ S] *) <FooBar> / clase de caracteres (espacio de coincidencia y no espacio) en lugar del marcador de punto. Consulte otras respuestas para obtener más información.
Allen
8
@Allen: JavaScript no admite el smodificador. En cambio, hazlo [^]*por el mismo efecto.
Derek 朕 會 功夫
1
En Ruby, use el mmodificador
Ryan Buckley
358

Prueba esto:

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

Básicamente dice "cualquier carácter o una nueva línea" repetido cero o más veces.

levik
fuente
55
Esto depende del idioma y / o herramienta que esté utilizando. Háganos saber lo que está utilizando, por ejemplo, Perl, PHP, CF, C #, sed, awk, etc.
Ben Doom
39
Dependiendo de los finales de línea que pueda necesitar((.|\n|\r)*)<FooBar>
Potherca
3
Dijo que está usando Eclipse. Esta es la solución correcta en mi opinión. Tengo el mismo problema y esto lo resolvió.
Danubian Sailor
44
Correcto: la pregunta es sobre el eclipse y también lo son las etiquetas. Pero la solución aceptada es una solución PHP. La suya debería ser la solución aceptada ...
acme
16
Esta es la peor expresión regular para hacer coincidir la entrada de varias líneas. Nunca lo use a menos que esté usando ElasticSearch. Use [\s\S]*o (?s).*.
Wiktor Stribiżew
89

La pregunta es, ¿puede el .patrón coincidir con algún personaje? La respuesta varía de un motor a otro. La principal diferencia es si el patrón lo utiliza una biblioteca de expresiones regulares POSIX o no POSIX.

Nota especial sobre : no se consideran expresiones regulares, pero .coinciden con cualquier carácter allí, igual que los motores basados ​​en POSIX.

Otra nota sobre y : .coincide con cualquier carácter por defecto ( demo ): str = "abcde\n fghij<Foobar>"; expression = '(.*)<Foobar>*'; [tokens,matches] = regexp(str,expression,'tokens','match');( tokenscontiene un abcde\n fghijelemento).

Además, en todos La expresión regular de las expresiones regulares es que el punto coincide con los saltos de línea de forma predeterminada. La gramática ECMAScript de Boost le permite desactivar esto con regex_constants::no_mod_m( fuente ).

Como para (está basado en POSIX), use la nopción ( demo ):select regexp_substr('abcde' || chr(10) ||' fghij<Foobar>', '(.*)<Foobar>', 1, 1, 'n', 1) as results from dual

Motores basados ​​en POSIX :

Un simple .salto de línea ya coincide, no es necesario utilizar ningún modificador, vea( demo )

los ( demo ),( demo ),(TRE, motor predeterminado base R sin perl=TRUE, para la base R con perl=TRUEo para los patrones stringr / stringi , use el (?s)modificador en línea) ( demo ) también se trata de .la misma manera.

Sin embargo , la mayoría de las herramientas basadas en POSIX procesan la entrada línea por línea. Por lo tanto, .no coincide con los saltos de línea solo porque no están dentro del alcance. Aquí hay algunos ejemplos de cómo anular esto:

  • - Existen múltiples soluciones, la más precisa pero no muy segura es sed 'H;1h;$!d;x; s/\(.*\)><Foobar>/\1/'( H;1h;$!d;x;extrae el archivo en la memoria). Si se deben incluir líneas completas, sed '/start_pattern/,/end_pattern/d' file(la eliminación del inicio finalizará con las líneas coincidentes incluidas) o sed '/start_pattern/,/end_pattern/{{//!d;};}' file(con las líneas coincidentes excluidas) puede considerarse.
  • - perl -0pe 's/(.*)<FooBar>/$1/gs' <<< "$str"( -0sorbe todo el archivo en la memoria, -pimprime el archivo después de aplicar la secuencia de comandos dada por -e). Tenga en cuenta que el uso -000pearrastrará el archivo y activará el 'modo de párrafo' donde Perl usa líneas nuevas consecutivas ( \n\n) como separador de registros.
  • - grep -Poz '(?si)abc\K.*?(?=<Foobar>)' file. Aquí, zhabilita (?s)la extracción de archivos, habilita el modo DOTALL para el .patrón, (?i)habilita el modo insensible a mayúsculas y minúsculas, \Komite el texto coincidente hasta ahora, *?es un cuantificador diferido, (?=<Foobar>)coincide con la ubicación anterior <Foobar>.
  • - pcregrep -Mi "(?si)abc\K.*?(?=<Foobar>)" file( Mhabilita la extracción de archivos aquí). Note pcregrepes una buena solución para grepusuarios de Mac OS .

Ver demos .

Motores no basados ​​en POSIX :

  • - Utilice el smodificador PCRE_DOTALL modificador : preg_match('~(.*)<Foobar>~s', $s, $m)( demo )
  • - Use la RegexOptions.Singlelinebandera ( demo ):
    - var result = Regex.Match(s, @"(.*)<Foobar>", RegexOptions.Singleline).Groups[1].Value;
    -var result = Regex.Match(s, @"(?s)(.*)<Foobar>").Groups[1].Value;
  • - Use la (?s)opción en línea:$s = "abcde`nfghij<FooBar>"; $s -match "(?s)(.*)<Foobar>"; $matches[1]
  • - Usar smodificador (o (?s)versión en línea al inicio) ( demo ):/(.*)<FooBar>/s
  • - Utilice re.DOTALL(o re.S) banderas o (?s)modificador en línea ( demostración ): m = re.search(r"(.*)<FooBar>", s, flags=re.S)(y luego if m:, print(m.group(1)))
  • - Usar Pattern.DOTALLmodificador (o (?s)bandera en línea ) ( demo ):Pattern.compile("(.*)<FooBar>", Pattern.DOTALL)
  • - Utilice el (?s)modificador en patrón ( demo ):regex = /(?s)(.*)<FooBar>/
  • - Usar (?s)modificador ( demo ):"(?s)(.*)<Foobar>".r.findAllIn("abcde\n fghij<Foobar>").matchData foreach { m => println(m.group(1)) }
  • - Uso [^]o soluciones alternativas [\d\D]/ [\w\W]/ [\s\S]( demo ):s.match(/([\s\S]*)<FooBar>/)[1]
  • ( std::regex) Use [\s\S]o las soluciones alternativas de JS ( demo ):regex rex(R"(([\s\S]*)<FooBar>)");
  • - Utilizar el mismo enfoque que en JavaScript, ([\s\S]*)<Foobar>. ( NOTA : La MultiLinepropiedad del RegExpobjeto a veces se piensa erróneamente que la opción de permitir .partido a través de los saltos de línea, mientras que, de hecho, sólo cambia el ^y $el comportamiento para que coincida con inicio / final de las líneas en lugar de cadenas , igual que en JS expresiones regulares ) comportamiento)

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

  • - Base R PCRE regexps - uso (?s): regmatches(x, regexec("(?s)(.*)<FooBar>",x, perl=TRUE))[[1]][2]( demo )
  • - las funciones in stringr/ stringiregex que funcionan con el motor regex de ICU, también usan (?s): stringr::str_match(x, "(?s)(.*)<FooBar>")[,2]( demo )
  • - Utilice el modificador (?s)en línea al inicio ( demo ):re: = regexp.MustCompile(`(?s)(.*)<FooBar>`)
  • - Use dotMatchesLineSeparatorso (más fácil) pase el (?s)modificador en línea al patrón:let rx = "(?s)(.*)<Foobar>"
  • - Igual que Swift, (?s)funciona de la manera más fácil, pero así es como se puede usar la opción :NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionDotMatchesLineSeparators error:&regexError];
  • , - Usar (?s)modificador ( demo ): "(?s)(.*)<Foobar>"(en hojas de cálculo de Google, =REGEXEXTRACT(A2,"(?s)(.*)<Foobar>"))

NOTAS SOBRE(?s) :

En la mayoría de los motores que no son POSIX, (?s)el modificador en línea (o la opción de marca incrustada) se puede utilizar para hacer cumplir los .saltos de línea.

Si se coloca al comienzo del patrón, (?s)cambia el comportamiento de todos .en el patrón. Si el (?s)se coloca en algún lugar después del comienzo, solo .se verán afectados aquellos que se encuentran a la derecha del mismo, a menos que este sea un patrón pasado a Python re. En Python re, independientemente de la (?s)ubicación, .se ve afectado todo el patrón . El (?s)efecto se deja de usar (?-s). Un grupo modificado se puede usar para afectar solo un rango especificado de un patrón de expresiones regulares (por ejemplo, Delim1(?s:.*?)\nDelim2.*hará que la primera .*?coincidencia entre las nuevas líneas y la segunda .*solo coincida con el resto de la línea).

Nota POSIX :

En motores regex que no son POSIX, para que coincida con cualquier carácter, se pueden usar [\s\S]/ [\d\D]/ [\w\W]construcciones.

En POSIX, [\s\S]no coincide con ningún carácter (como en JavaScript o cualquier motor que no sea POSIX) porque las secuencias de escape de expresiones regulares no son compatibles dentro de las expresiones de paréntesis. [\s\S]se analiza como expresiones de paréntesis que coinciden con un único carácter, \o so S.

Wiktor Stribiżew
fuente
55
Debe vincular a esta excelente descripción general desde su página de perfil o algo (+1).
Jan
1
Es posible que desee agregar esto al elemento boost : en el espacio de nombres regex_constants, flag_type_'s: perl = ECMAScript = JavaScript = JScript = :: boost :: regbase :: normal = 0, que por defecto es Perl. Los programadores establecerán una definición de bandera base #define MOD regex_constants::perl | boost::regex::no_mod_s | boost::regex::no_mod_mpara sus banderas de expresiones regulares para reflejar eso. Y el árbitro siempre es el modificador en línea. Donde se (?-sm)(?s).*restablece.
1
¿Puedes agregar también para bash por favor?
Pasupathi Rajamanickam
2
@PasupathiRajamanickam Bash utiliza un motor de expresiones regulares POSIX, que .coincide con cualquier carácter allí (incluidos los saltos de línea). Vea esta demostración en línea de Bash .
Wiktor Stribiżew
1
Eres genial: este es el mini-tutorial más exhaustivo sobre expresiones regulares (relativamente) complejas que he visto. ¡Te mereces que tu respuesta se convierta en la aceptada! ¡Felicitaciones y votos adicionales por incluir Goen la respuesta!
Gwyneth Llewelyn
68

Si está utilizando la búsqueda de Eclipse, puede habilitar la opción "DOTALL" para hacer '.' coincide con cualquier carácter, incluidos los delimitadores de línea: simplemente agregue "(? s)" al comienzo de su cadena de búsqueda. Ejemplo:

(?s).*<FooBar>
Paulo Merson
fuente
1
No en ninguna parte, solo en sabores de expresiones regulares que admiten modificadores en línea, y ciertamente no en Ruby donde (?s)=>(?m)
Wiktor Stribiżew
¿Algo para bash?
Pasupathi Rajamanickam
38

En muchos dialectos de expresiones regulares, /[\S\s]*<Foobar>/hará exactamente lo que quieras. Fuente

Abbas Shahzadeh
fuente
2
Desde ese enlace: "JavaScript y VBScript no tienen una opción para hacer que los caracteres de salto de línea coincidan con los puntos. En esos idiomas, puede usar una clase de caracteres como [\ s \ S] para que coincida con cualquier carácter". En vez de . use [\ s \ S] (espacios coincidentes y no espacios) en su lugar.
Allen
32

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

El punto coincide con todos excepto las nuevas líneas (\ r \ n). Entonces use \ s \ S, que coincidirá con TODOS los caracteres.

samwize
fuente
Esto resuelve el problema si está utilizando Objective-C [text rangeOfString:regEx options:NSRegularExpressionSearch]. ¡Gracias!
J. Costa
1
Esto funciona en la búsqueda y reemplazo de expresiones regulares de intelliJ, gracias.
Barclay
Esto funciona. Pero tiene que ser la primera aparición de<FooBar>
Ozkan
13

también podemos usar

(.*?\n)*?

para que coincida con todo, incluida la nueva línea sin codicia

Esto hará que la nueva línea sea opcional.

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

"."normalmente no coincide con saltos de línea. La mayoría de los motores regex le permiten agregar S-flag (también llamado DOTALLy SINGLELINE) para hacer "."coincidir también las nuevas líneas. Si eso falla, podrías hacer algo así [\S\s].

Markus Jarderot
fuente
8

Para Eclipse funcionó la siguiente expresión:

Foo

Bar jadajada "

Expresión regular:

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

la s hace que el Punto (.) coincida con los retornos de carro

Cuenta
fuente
Parece que esto no es válido (Chrome): text.match (/ a / s) SyntaxError: indicadores no válidos suministrados a los constructores de RegExp
Allen
Porque no es compatible con los motores JavaScript RegEx. Las sbanderas existen en PCRE, el motor más completo (disponible en Perl y PHP). PCRE tiene 10 banderas (y muchas otras características) mientras que JavaScript solo tiene 3 banderas ( gmi).
Morgan Touverey Quilling
4

En la expresión regular basada en Java puedes usar [\s\S]

Kamahire
fuente
1
¿No deberían ser esas barras invertidas?
Paul Draper
Van al final de la Expresión regular, no dentro. Ejemplo: / blah / s
RandomInsano
¿Supongo que te refieres a JavaScript, no a Java? Como puede agregar la sbandera al patrón en Java y JavaScript no tiene la sbandera.
3limin4t0r
3

Tenga en cuenta que (.|\n)*puede ser menos eficiente que (por ejemplo) [\s\S]*(si las expresiones regulares de su idioma admiten tales escapes) y que encontrar la forma de especificar el modificador que hace. También coinciden con las nuevas líneas. O puedes ir con POSIXy alternativas como [[:space:][:^space:]]*.

tye
fuente
3

Use RegexOptions.Singleline, cambia el significado de. para incluir nuevas líneas

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

shmall
fuente
1

En el contexto de uso dentro de los idiomas, las expresiones regulares actúan sobre cadenas, no líneas. Por lo tanto, debería poder usar la expresión regular normalmente, suponiendo que la cadena de entrada tenga varias líneas.

En este caso, la expresión regular dada coincidirá con la cadena completa, ya que "<FooBar>" está presente. Dependiendo de los detalles de la implementación de expresiones regulares, el valor de $ 1 (obtenido de "(. *)") Será "fghij" o "abcde \ nfghij". Como otros han dicho, algunas implementaciones le permiten controlar si el "." coincidirá con la nueva línea, dándole la opción.

El uso de expresiones regulares basadas en líneas generalmente es para líneas de comando como egrep.

nsayer
fuente
1

Tuve el mismo problema y lo resolví probablemente de la mejor manera, pero funciona. Reemplacé todos los saltos de línea antes de hacer mi partido real:

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

Estoy manipulando HTML para que los saltos de línea realmente no me importen en este caso.

Intenté todas las sugerencias anteriores sin suerte, estoy usando .Net 3.5 FYI

Slee
fuente
¡Estoy usando .NET también y (\s|\S)parece hacer el truco para mí!
Vamshi Krishna
@VamshiKrishna En .NET, use (?s)para hacer .coincidir cualquier carácter. No lo use, (\s|\S)esto disminuirá el rendimiento.
Wiktor Stribiżew
1

En Javascript puede usar [^] * para buscar caracteres de cero a infinito, incluidos los saltos de línea.

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

generalmente . no coincide con las nuevas líneas, así que intente((.|\n)*)<foobar>

tloach
fuente
3
No, no hagas eso. Si necesita hacer coincidir algo, incluidos los separadores de línea, use el modificador DOTALL (también conocido como / so SingleLine). El hack (. | \ N) no solo hace que la expresión regular sea menos eficiente, sino que ni siquiera es correcta. Como mínimo, debe coincidir con \ r (retorno de carro) y \ n (salto de línea). También hay otros caracteres separadores de línea, aunque rara vez se usan. Pero si usa la bandera DOTALL, no tiene que preocuparse por ellos.
Alan Moore
1
\ R es la combinación independiente de la plataforma para las nuevas líneas en Eclipse.
Opiato
@opyate Deberías publicar esto como respuesta ya que esta pequeña joya es increíblemente útil.
jeckhart
Podrías probar esto en su lugar. No coincidirá con los corchetes internos y también considerará el opcional \r.:((?:.|\r?\n)*)<foobar>
ssc-hrep3
0

Quería hacer coincidir un bloque if particular en Java

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

   }
...
...
}

Si uso el regExp

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

incluía la llave de cierre para el bloque de método, así que utilicé

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

para excluir la llave de cierre de la coincidencia con comodines.

Spangen
fuente
0

A menudo tenemos que modificar una subcadena con algunas palabras clave repartidas entre las líneas que preceden a la subcadena. Considere un elemento xml:

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

Supongamos que queremos modificar el 81, a algún otro valor, digamos 40. Primero identifique .UID.21..UID., luego omita todos los caracteres incluyendo \nhasta .PercentCompleted.. El patrón de expresión regular y la especificación de reemplazo son:

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>

El subgrupo (.|\n)es probablemente el grupo que falta $3. Si lo hacemos sin captura para (?:.|\n)entonces, el $3es (<PercentComplete>). Entonces el patrón y replaceSpectambién puede ser:

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

y el reemplazo funciona correctamente como antes.

usuario1348737
fuente
0

Normalmente, buscar tres líneas consecutivas en Powershell se vería así:

$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

Curiosamente, este sería el texto de Unix en el mensaje, pero el texto de Windows en un archivo:

$pattern = 'lineone
linetwo
linethree
'

Aquí hay una manera de imprimir los finales de línea:

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

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

Opción 1

Una forma sería usar la sbandera (al igual que la respuesta aceptada):

/(.*)<FooBar>/s

Demo 1

opcion 2

Una segunda forma sería usar la mbandera (multilínea) y cualquiera de los siguientes patrones:

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

o

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

o

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

Demo 2

Circuito RegEx

jex.im visualiza expresiones regulares:

ingrese la descripción de la imagen aquí

Emma
fuente