Comment faire correspondre un caractère sur plusieurs lignes dans une expression régulière?


358

Par exemple, cette expression régulière

(.*)<FooBar>

correspondra:

abcde<FooBar>

Mais comment puis-je le faire correspondre sur plusieurs lignes?

abcde
fghij<FooBar>

1
Clarifier; J'utilisais à l'origine Eclipse pour effectuer une recherche et un remplacement dans plusieurs fichiers. Ce que j'ai découvert par les réponses ci-dessous, c'est que mon problème était l'outil et non le motif regex.
andyuk

2
Votre indicateur "eclipse" devrait alors être supprimé car celui qui cherche une solution d'éclipse trouvera cette question (comme je l'ai fait) et trouvera ensuite une solution non-éclipse comme acceptée.
acme

2
Maintenant, je trouve cela dans le moteur de recherche car l'éclipse a été mentionnée. Oh l'horreur.
Brian Olsen

Réponses:


240

Cela dépend de la langue, mais il devrait y avoir un modificateur que vous pouvez ajouter au modèle d'expression régulière. En PHP c'est:

/(.*)<FooBar>/s

Le s à la fin fait correspondre le point à tous les caractères, y compris les retours à la ligne.


Et si je voulais juste une nouvelle ligne et pas tous les personnages?
Grace

3
@Grace: utilisez \ n pour faire correspondre une nouvelle ligne
Jeremy Ruten

5
Le drapeau s est (maintenant?) Invalide, au moins dans Chrome / V8. À la place, utilisez / ([\ s \ S] *) <FooBar> / classe de caractères (correspond à l'espace et au non-espace) à la place du pointeur. Consultez les autres réponses pour plus d'informations.
Allen

8
@Allen - JavaScript ne prend pas en charge le smodificateur. Au lieu de cela, faites [^]*pour le même effet.
Derek 朕 會 功夫

1
Dans Ruby, utilisez le mmodificateur
Ryan Buckley

358

Essaye ça:

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

Il dit essentiellement "n'importe quel caractère ou une nouvelle ligne" répété zéro ou plusieurs fois.


5
Cela dépend de la langue et / ou de l'outil que vous utilisez. Veuillez nous faire savoir ce que vous utilisez, par exemple Perl, PHP, CF, C #, sed, awk, etc.
Ben Doom

39
Selon les terminaisons de ligne dont vous pourriez avoir besoin((.|\n|\r)*)<FooBar>
Potherca

3
Il a dit qu'il utilisait Eclipse. C'est la bonne solution à mon avis. J'ai le même problème et cela l'a résolu.
Danubian Sailor

4
À droite - la question concerne l'éclipse, tout comme les balises. Mais la solution acceptée est une solution PHP. La solution devrait être la vôtre ...
acme

16
C'est le pire regex pour faire correspondre l'entrée de plusieurs lignes. Veuillez ne jamais l'utiliser sauf si vous utilisez ElasticSearch. Utilisez [\s\S]*ou (?s).*.
Wiktor Stribiżew

89

La question est: le .motif peut-il correspondre à n'importe quel caractère? La réponse varie d'un moteur à l'autre. La principale différence est de savoir si le modèle est utilisé par une bibliothèque d'expression régulière POSIX ou non POSIX.

Note spéciale sur : elles ne sont pas considérées comme des expressions régulières, mais .correspondent à n'importe quel caractère, comme les moteurs basés sur POSIX.

Une autre note sur et : le .correspond à n'importe quel caractère par défaut ( démo ): str = "abcde\n fghij<Foobar>"; expression = '(.*)<Foobar>*'; [tokens,matches] = regexp(str,expression,'tokens','match');( tokenscontient un abcde\n fghijélément).

Aussi, dans tous les grammaires regex du point correspondent aux sauts de ligne par défaut. La grammaire ECMAScript de Boost vous permet de désactiver cette option avec regex_constants::no_mod_m( source ).

Pour ce qui est de (il est basé sur POSIX), utilisez l' noption ( démo ):select regexp_substr('abcde' || chr(10) ||' fghij<Foobar>', '(.*)<Foobar>', 1, 1, 'n', 1) as results from dual

Moteurs basés sur POSIX :

Un simple .correspond déjà à des sauts de ligne, pas besoin d'utiliser de modificateurs, voir( démo ).

le ( démo ),( démo ),(TRE, moteur par défaut de la base R avec non perl=TRUE, pour la base R avec perl=TRUEou pour les modèles stringr / stringi , utilisez le (?s)modificateur inline) ( démo ) traiter également de .la même manière.

Cependant , la plupart des outils basés sur POSIX traitent les entrées ligne par ligne. Par conséquent, .ne correspond pas aux sauts de ligne simplement parce qu'ils ne sont pas dans la portée. Voici quelques exemples pour contourner cela:

  • - Il existe plusieurs solutions de contournement, la plus précise mais pas très sûre est sed 'H;1h;$!d;x; s/\(.*\)><Foobar>/\1/'( H;1h;$!d;x;glisse le fichier dans la mémoire). Si des lignes entières doivent être incluses, sed '/start_pattern/,/end_pattern/d' file(la suppression du début se terminera avec les lignes correspondantes incluses) ou sed '/start_pattern/,/end_pattern/{{//!d;};}' file(avec les lignes correspondantes exclues) peut être envisagée.
  • - perl -0pe 's/(.*)<FooBar>/$1/gs' <<< "$str"( -0met le fichier entier en mémoire, -pimprime le fichier après avoir appliqué le script donné par -e). Notez que l'utilisation -000peralentira le fichier et activera le «mode paragraphe» où Perl utilise des sauts de ligne consécutifs ( \n\n) comme séparateur d'enregistrement.
  • - grep -Poz '(?si)abc\K.*?(?=<Foobar>)' file. Ici, zactive le slurping de fichier, (?s)active le mode DOTALL pour le .motif, (?i)active le mode insensible à la casse, \Komet le texte correspondant jusqu'à présent, *?est un quantificateur paresseux, (?=<Foobar>)correspond à l'emplacement précédent <Foobar>.
  • - pcregrep -Mi "(?si)abc\K.*?(?=<Foobar>)" file( Mactive le fichier slurping ici). Remarque pcregrepest une bonne solution pour les greputilisateurs de Mac OS .

Voir les démos .

Moteurs non basés sur POSIX :

  • - Utiliser le smodificateur PCRE_DOTALL : preg_match('~(.*)<Foobar>~s', $s, $m)( démo )
  • - Utilisez le RegexOptions.Singlelinedrapeau ( démo ):
    - var result = Regex.Match(s, @"(.*)<Foobar>", RegexOptions.Singleline).Groups[1].Value;
    -var result = Regex.Match(s, @"(?s)(.*)<Foobar>").Groups[1].Value;
  • - Utiliser l' (?s)option en ligne:$s = "abcde`nfghij<FooBar>"; $s -match "(?s)(.*)<Foobar>"; $matches[1]
  • - Utilisez un smodificateur (ou une (?s)version en ligne au début) ( démo ):/(.*)<FooBar>/s
  • - Utilisez re.DOTALL(ou re.S) des drapeaux ou un (?s)modificateur en ligne ( démo ): m = re.search(r"(.*)<FooBar>", s, flags=re.S)(puis if m:, print(m.group(1)))
  • - Utilisez le Pattern.DOTALLmodificateur (ou le (?s)drapeau en ligne ) ( démo ):Pattern.compile("(.*)<FooBar>", Pattern.DOTALL)
  • - Utilisez le (?s)modificateur in-pattern ( démo ):regex = /(?s)(.*)<FooBar>/
  • - Utiliser le (?s)modificateur ( démo ):"(?s)(.*)<Foobar>".r.findAllIn("abcde\n fghij<Foobar>").matchData foreach { m => println(m.group(1)) }
  • - Utilisation [^]ou solutions [\d\D]/ [\w\W]/ [\s\S]( démo ):s.match(/([\s\S]*)<FooBar>/)[1]
  • ( std::regex) Utilisation [\s\S]ou solutions de contournement JS ( démo ):regex rex(R"(([\s\S]*)<FooBar>)");
  • - Utilisez la même approche que JavaScript, ([\s\S]*)<Foobar>. ( REMARQUE : la MultiLinepropriété de l' RegExpobjet est parfois considérée à tort comme l'option d'autoriser la .correspondance entre les sauts de ligne, alors qu'en fait, elle modifie uniquement le comportement ^et $pour correspondre au début / fin des lignes plutôt qu'aux chaînes , comme dans l'expression régulière JS ) comportement.)

  • - Utilisez le modificateur /m MULTILINE ( démo ):s[/(.*)<Foobar>/m, 1]

  • - Regexps PCRE Base R - utiliser (?s): regmatches(x, regexec("(?s)(.*)<FooBar>",x, perl=TRUE))[[1]][2]( démo )
  • - Les fonctions in stringr/ stringiregex qui sont alimentées par le moteur ICU regex, utilisent également (?s): stringr::str_match(x, "(?s)(.*)<FooBar>")[,2]( démo )
  • - Utilisez le modificateur en ligne (?s)au début ( démo ):re: = regexp.MustCompile(`(?s)(.*)<FooBar>`)
  • - Utilisez dotMatchesLineSeparatorsou (plus facile) passez le (?s)modificateur en ligne au motif:let rx = "(?s)(.*)<Foobar>"
  • - Identique à Swift, (?s)fonctionne le plus facilement, mais voici comment l' option peut être utilisée :NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionDotMatchesLineSeparators error:&regexError];
  • , - Utiliser le (?s)modificateur ( démo ): "(?s)(.*)<Foobar>"(dans Google Spreadsheets, =REGEXEXTRACT(A2,"(?s)(.*)<Foobar>"))

NOTES SUR(?s) :

Dans la plupart des moteurs non POSIX, (?s)le modificateur en ligne (ou l'option d'indicateur intégré) peut être utilisé pour appliquer .pour correspondre aux sauts de ligne.

S'il est placé au début du motif, (?s)change le comportement de tous .dans le motif. Si le (?s)est placé quelque part après le début, seuls ceux .qui sont situés à droite de celui-ci seront affectés, sauf s'il s'agit d'un modèle transmis à Python re. En Python re, quel que soit l' (?s)emplacement, l'ensemble du modèle .est affecté. L' (?s)effet n'est plus utilisé (?-s). Un groupe modifié peut être utilisé pour n'affecter qu'une plage spécifiée d'un modèle d'expression régulière (par exemple Delim1(?s:.*?)\nDelim2.*, fera la première .*?correspondance entre les nouvelles lignes et la seconde .*ne correspondra qu'au reste de la ligne).

Remarque POSIX :

Dans les moteurs d'expression régulière non POSIX, pour correspondre à n'importe quel caractère, les constructions [\s\S]/ [\d\D]/ [\w\W]peuvent être utilisées.

Dans POSIX, [\s\S]ne correspond à aucun caractère (comme dans JavaScript ou tout moteur non-POSIX) car les séquences d'échappement regex ne sont pas prises en charge dans les expressions entre crochets. [\s\S]est analysé comme des expressions entre crochets qui correspondent à un seul caractère, \ou sou S.


5
Vous devez créer un lien vers cet excellent aperçu depuis votre page de profil ou quelque chose (+1).
Jan

1
Vous voudrez peut-être l'ajouter à l' élément boost : dans l'espace de noms regex_constants, flag_type_'s: perl = ECMAScript = JavaScript = JScript = :: boost :: regbase :: normal = 0 qui est par défaut Perl. Les programmeurs définiront une définition de drapeau de base #define MOD regex_constants::perl | boost::regex::no_mod_s | boost::regex::no_mod_mpour leurs drapeaux d'expression régulière afin de refléter cela. Et l'arbitre est toujours les modificateurs en ligne. Où (?-sm)(?s).*réinitialise.

1
Pouvez-vous également ajouter pour bash s'il vous plaît?
Pasupathi Rajamanickam

2
@PasupathiRajamanickam Bash utilise un moteur d'expression régulière POSIX, qui .correspond à n'importe quel caractère (y compris les sauts de ligne). Voir cette démo Bash en ligne .
Wiktor Stribiżew

1
You rock - c'est le mini-tutoriel le plus complet sur les expressions rationnelles (relativement) complexes que j'ai jamais vu. Vous méritez que votre réponse devienne celle acceptée! Bravo et votes supplémentaires pour avoir inclus Godans la réponse!
Gwyneth Llewelyn

68

Si vous utilisez la recherche Eclipse, vous pouvez activer l'option "DOTALL" pour faire '.' faire correspondre n'importe quel caractère, y compris les délimiteurs de ligne: ajoutez simplement "(? s)" au début de votre chaîne de recherche. Exemple:

(?s).*<FooBar>

1
Pas n'importe où, uniquement dans les versions regex prenant en charge les modificateurs en ligne, et certainement pas dans Ruby où (?s)=>(?m)
Wiktor Stribiżew

Quelque chose pour bash?
Pasupathi Rajamanickam

38

Dans de nombreux dialectes regex, /[\S\s]*<Foobar>/fera exactement ce que vous voulez. La source


2
À partir de ce lien: "JavaScript et VBScript n'ont pas d'option pour faire en sorte que les caractères de saut de ligne correspondent. Dans ces langues, vous pouvez utiliser une classe de caractères telle que [\ s \ S] pour faire correspondre n'importe quel caractère." À la place du . utilisez plutôt [\ s \ S] (faire correspondre les espaces et les non-espaces).
Allen

32

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

Le point correspond à tous sauf les sauts de ligne (\ r \ n). Utilisez donc \ s \ S, qui correspondra à TOUS les caractères.


Cela résout le problème si vous utilisez l'Objective-C [text rangeOfString:regEx options:NSRegularExpressionSearch]. Merci!
J. Costa

1
Cela fonctionne dans la recherche et le remplacement d'expressions rationnelles d'intelliJ, merci.
barclay

Cela marche. Mais ce doit être la première occurrence de<FooBar>
Ozkan


13

on peut aussi utiliser

(.*?\n)*?

pour correspondre à tout, y compris la nouvelle ligne sans gourmandise

Cela rendra la nouvelle ligne facultative

(.*?|\n)*?

8

"."ne correspond normalement pas aux sauts de ligne. La plupart des moteurs d'expression régulière vous permettent d'ajouter le S-flag (également appelé DOTALLet SINGLELINE) pour faire "."correspondre également les nouvelles lignes. Si cela échoue, vous pourriez faire quelque chose comme [\S\s].


8

Pour Eclipse a travaillé l'expression suivante:

Foo

jadajada Bar "

Expression régulière:

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

5
/(.*)<FooBar>/s

le s fait que Dot (.) correspond aux retours chariot


Il semble que cela ne soit pas valide (Chrome): text.match (/ a / s) SyntaxError: drapeaux non valides fournis au constructeur RegExp '
Allen

Parce qu'il n'est pas pris en charge dans les moteurs JavaScript RegEx. Les sdrapeaux existent dans PCRE, le moteur le plus complet (disponible en Perl et PHP). PCRE a 10 drapeaux (et beaucoup d'autres fonctionnalités) tandis que JavaScript n'a que 3 drapeaux ( gmi).
Morgan Touverey Quilling

4

Dans l'expression régulière basée sur java, vous pouvez utiliser [\s\S]


1
Ne devrait-il pas s'agir de contre-obliques?
Paul Draper

Ils vont à la fin de l'expression régulière, pas à l'intérieur. Exemple: / blah / s
RandomInsano

Je suppose que vous voulez dire JavaScript, pas Java? Puisque vous pouvez simplement ajouter le sdrapeau au modèle en Java et JavaScript n'a pas le sdrapeau.
3limin4t0r

3

Notez que cela (.|\n)*peut être moins efficace que (par exemple) [\s\S]*(si les expressions rationnelles de votre langue prennent en charge ces échappements) et que de trouver comment spécifier le modificateur qui le fait. correspondent également aux nouvelles lignes. Ou vous pouvez aller avec des alternatives POSIXy comme [[:space:][:^space:]]*.


3

Utilisez RegexOptions.Singleline, cela change la signification de. pour inclure les nouvelles lignes

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



1

Dans le contexte d'une utilisation dans les langues, les expressions régulières agissent sur les chaînes, pas sur les lignes. Vous devriez donc pouvoir utiliser l'expression régulière normalement, en supposant que la chaîne d'entrée comporte plusieurs lignes.

Dans ce cas, l'expression régulière donnée correspondra à la chaîne entière, car "<FooBar>" est présent. Selon les spécificités de l'implémentation de l'expression régulière, la valeur $ 1 (obtenue à partir du "(. *)") Sera soit "fghij" soit "abcde \ nfghij". Comme d'autres l'ont dit, certaines implémentations vous permettent de contrôler si le "." correspondra à la nouvelle ligne, vous donnant le choix.

L'expression régulière basée sur la ligne est généralement utilisée pour les choses en ligne de commande comme egrep.


1

J'ai eu le même problème et je l'ai résolu probablement pas de la meilleure façon, mais cela fonctionne. J'ai remplacé tous les sauts de ligne avant de faire mon vrai match:

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

Je manipule du HTML, donc les sauts de ligne n'ont pas vraiment d'importance pour moi dans ce cas.

J'ai essayé toutes les suggestions ci-dessus sans succès, j'utilise .Net 3.5 FYI


J'utilise aussi .NET et (\s|\S)semble faire l'affaire pour moi!
Vamshi Krishna

@VamshiKrishna Dans .NET, utilisez (?s)pour faire .correspondre tous les caractères. N'utilisez pas (\s|\S)cela qui ralentira les performances.
Wiktor Stribiżew

1

En Javascript, vous pouvez utiliser [^] * pour rechercher des caractères de zéro à infini, y compris les sauts de ligne.

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

généralement . ne correspond pas aux nouvelles lignes, alors essayez((.|\n)*)<foobar>


3
Non, ne fais pas ça. Si vous devez faire correspondre quelque chose, y compris des séparateurs de lignes, utilisez le modificateur DOTALL (aka / s ou SingleLine). Non seulement le hack (. | \ N) rend le regex moins efficace, il n'est même pas correct. À tout le moins, il doit correspondre à \ r (retour chariot) ainsi qu'à \ n (saut de ligne). Il existe également d'autres caractères de séparation de ligne, bien que rarement utilisés. Mais si vous utilisez le drapeau DOTALL, vous n'avez pas à vous en préoccuper.
Alan Moore

1
\ R est la correspondance indépendante de la plate-forme pour les nouvelles lignes dans Eclipse.
opyate

@opyate Vous devriez poster ceci comme réponse car ce petit bijou est incroyablement utile.
jeckhart

Vous pouvez essayer ceci à la place. Il ne correspondra pas aux supports internes et considérera également l'option \r.:((?:.|\r?\n)*)<foobar>
ssc-hrep3

0

Je voulais faire correspondre un bloc if particulier en java

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

   }
...
...
}

Si j'utilise regExp

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

il comprenait l'accolade de fermeture pour le bloc de méthode, donc j'ai utilisé

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

pour exclure l'accolade de fermeture de la correspondance générique.


0

Souvent, nous devons modifier une sous-chaîne avec quelques mots clés répartis sur les lignes précédant la sous-chaîne. Considérons un élément xml:

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

Supposons que nous voulons modifier le 81, à une autre valeur, disons 40. Identifiez d'abord .UID.21..UID., puis ignorez tous les caractères, y compris \ntill .PercentCompleted.. Le modèle d'expression régulière et la spécification de remplacement sont les suivants:

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>

Le sous (.|\n)- groupe est probablement le groupe manquant $3. Si nous le rendons non capturable d'ici (?:.|\n)là, le $3est (<PercentComplete>). Ainsi, le motif et replaceSpecpeut également être:

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

et le remplacement fonctionne correctement comme avant.


0

En général, la recherche de trois lignes consécutives dans Powershell ressemblerait à ceci:

$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

Bizarrement, ce serait du texte Unix à l'invite, mais du texte Windows dans un fichier:

$pattern = 'lineone
linetwo
linethree
'

Voici un moyen d'imprimer les fins de ligne:

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

# output
lineone\nlinetwo\nlinethree\n

-2

Option 1

Une façon serait d'utiliser le sdrapeau (tout comme la réponse acceptée):

/(.*)<FooBar>/s

Démo 1

Option 2

Une deuxième façon serait d'utiliser l' mindicateur (multiligne) et l'un des modèles suivants:

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

ou

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

ou

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

Démo 2

Circuit RegEx

jex.im visualise les expressions régulières:

entrez la description de l'image ici

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.