Jak dopasować dowolny znak w wielu wierszach wyrażenia regularnego?

355

Na przykład to wyrażenie regularne

(.*)<FooBar>

będzie pasować:

abcde<FooBar>

Ale jak mogę dopasować go do wielu linii?

abcde
fghij<FooBar>
andyuk
źródło
1
Wyjaśnić; Pierwotnie korzystałem z Eclipse, aby znaleźć i zastąpić wiele plików. Poniżej odkryłem, że moim problemem było narzędzie, a nie wzorzec wyrażenia regularnego.
andyuk
2
Flaga „zaćmienie” powinna zostać usunięta, ponieważ osoba poszukująca rozwiązania zaćmienia znajdzie to pytanie (tak jak ja), a następnie znajdzie rozwiązanie inne niż zaćmienie jako zaakceptowane.
acme
2
Teraz znajduję to w wyszukiwarce, ponieważ wspomniano o zaćmieniu. Och, horror.
Brian Olsen

Odpowiedzi:

240

To zależy od języka, ale powinien istnieć modyfikator, który możesz dodać do wzorca wyrażenia regularnego. W PHP jest to:

/(.*)<FooBar>/s

Litera s na końcu powoduje, że kropka dopasowuje wszystkie znaki, w tym znaki nowej linii.

Jeremy Ruten
źródło
a co jeśli chciałbym tylko nowej linii, a nie wszystkich znaków?
Grace
3
@Grace: użyj \ n, aby dopasować nowy wiersz
Jeremy Ruten
5
Flaga s jest (teraz?) Nieprawidłowa, przynajmniej w Chrome / V8. Zamiast tego użyj / / [[\ s \ S] *) <FooBar> / class class (dopasuj spację i spację] zamiast dopasowywania kropek. Zobacz inne odpowiedzi, aby uzyskać więcej informacji.
Allen
8
@Allen - JavaScript nie obsługuje smodyfikatora. Zamiast tego zrób [^]*dla tego samego efektu.
Derek 朕 會 功夫
1
W Ruby użyj mmodyfikatora
Ryan Buckley
355

Spróbuj tego:

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

Mówi w zasadzie „dowolny znak lub nowa linia” powtarzane zero lub więcej razy.

levik
źródło
5
Zależy to od używanego języka i / lub narzędzia. Daj nam znać, czego używasz, np. Perl, PHP, CF, C #, sed, awk itp.
Ben Doom
39
W zależności od zakończeń linii, których możesz potrzebować((.|\n|\r)*)<FooBar>
Potherca,
3
Powiedział, że używa Eclipse. To moim zdaniem poprawne rozwiązanie. Mam ten sam problem i to go rozwiązało.
Danubian Sailor
4
Racja - pytanie dotyczy zaćmienia, podobnie jak tagi. Ale przyjętym rozwiązaniem jest rozwiązanie PHP. Twoje powinno być przyjętym rozwiązaniem ...
acme
16
Jest to najgorszy regex dla dopasowania wielu linii wejściowych. Nigdy nie używaj go, chyba że używasz ElasticSearch. Użyj [\s\S]*lub (?s).*.
Wiktor Stribiżew
88

Pytanie brzmi: czy .wzór może pasować do dowolnej postaci? Odpowiedź różni się w zależności od silnika. Główną różnicą jest to, czy wzorzec jest używany przez bibliotekę wyrażeń regularnych POSIX, czy nie.

Specjalna uwaga na temat : nie są uważane za wyrażenia regularne, ale . pasują do każdego tam znaku, tak samo jak silniki oparte na POSIX.

Kolejna uwaga na temat i : .domyślnie dopasowuje dowolny znak ( demo ): str = "abcde\n fghij<Foobar>"; expression = '(.*)<Foobar>*'; [tokens,matches] = regexp(str,expression,'tokens','match');( tokenszawiera abcde\n fghijelement).

Również we wszystkich Gramatyka wyrażeń regularnych kropka domyślnie dopasowuje podział linii. Gramatyka ECMAScript doładowania pozwala na wyłączenie tego za pomocą regex_constants::no_mod_m( źródła ).

Jeśli chodzi o (jest oparty na POSIX), użyj nopcji ( demo ):select regexp_substr('abcde' || chr(10) ||' fghij<Foobar>', '(.*)<Foobar>', 1, 1, 'n', 1) as results from dual

Silniki oparte na POSIX :

Zwykły . już pasuje do podziałów linii, nie trzeba używać żadnych modyfikatorów, patrz( demo ).

The ( demo ),( demo ),(TRE, domyślny silnik bazowy R z nie perl=TRUE, dla bazowego R z perl=TRUElub dla wzorów stringr / stringi , użyj (?s)modyfikatora wbudowanego) ( demo ) również traktuj .to samo.

Jednak większość narzędzi opartych na POSIX przetwarza dane wejściowe linia po linii. Dlatego .nie pasuje do podziałów linii tylko dlatego, że nie są one objęte zakresem. Oto kilka przykładów, jak to zmienić:

  • - Istnieje wiele obejść, najbardziej precyzyjna, ale niezbyt bezpieczna jest sed 'H;1h;$!d;x; s/\(.*\)><Foobar>/\1/'( H;1h;$!d;x;zapisuje plik w pamięci). Jeśli trzeba uwzględnić całe linie, sed '/start_pattern/,/end_pattern/d' file(usunięcie od początku zakończy się dołączeniem pasujących linii) lub sed '/start_pattern/,/end_pattern/{{//!d;};}' file(z wyłączeniem pasujących linii) można rozważyć.
  • - perl -0pe 's/(.*)<FooBar>/$1/gs' <<< "$str"( -0umieszcza cały plik w pamięci, -pdrukuje plik po zastosowaniu skryptu podanego przez -e). Zauważ, że użycie -000pespowoduje zepsucie pliku i aktywację „trybu akapitowego”, w którym Perl używa kolejnych znaków nowej linii ( \n\n) jako separatora rekordów.
  • - grep -Poz '(?si)abc\K.*?(?=<Foobar>)' file . Tutaj zwłącza (?s)rozmycie plików, włącza tryb DOTALL dla .wzorca, (?i)włącza tryb bez rozróżniania wielkości liter, \Kpomija do tej pory dopasowany tekst, *?jest leniwym kwantyfikatorem, (?=<Foobar>)dopasowuje wcześniejszą lokalizację <Foobar>.
  • - pcregrep -Mi "(?si)abc\K.*?(?=<Foobar>)" file( Mumożliwia tutaj rozmazanie plików). Uwaga pcregrepjest dobrym rozwiązaniem dla grepużytkowników Mac OS .

Zobacz dema .

Silniki inne niż POSIX :

  • - Użyj smodyfikatora PCRE_DOTALL : preg_match('~(.*)<Foobar>~s', $s, $m)( demo )
  • - Użyj RegexOptions.Singlelineflagi ( demo ):
    -var result = Regex.Match(s, @"(.*)<Foobar>", RegexOptions.Singleline).Groups[1].Value;
    -var result = Regex.Match(s, @"(?s)(.*)<Foobar>").Groups[1].Value;
  • - Użyj (?s)opcji wbudowanej:$s = "abcde`nfghij<FooBar>"; $s -match "(?s)(.*)<Foobar>"; $matches[1]
  • - Użyj smodyfikatora (lub (?s)wersji inline na początku) ( demo ):/(.*)<FooBar>/s
  • - Użyj re.DOTALL(lub re.S) flag lub (?s)wbudowanego modyfikatora ( demo ): m = re.search(r"(.*)<FooBar>", s, flags=re.S)(a następnie if m:, print(m.group(1)))
  • - Użyj Pattern.DOTALLmodyfikatora (lub wbudowanej (?s)flagi) ( demo ):Pattern.compile("(.*)<FooBar>", Pattern.DOTALL)
  • - Użyj (?s)wbudowanego modyfikatora ( demo ):regex = /(?s)(.*)<FooBar>/
  • - Użyj (?s)modyfikatora ( demo ):"(?s)(.*)<Foobar>".r.findAllIn("abcde\n fghij<Foobar>").matchData foreach { m => println(m.group(1)) }
  • - Użyj [^]lub obejścia [\d\D]/ [\w\W]/ [\s\S]( demo ):s.match(/([\s\S]*)<FooBar>/)[1]
  • ( std::regex) Użyj [\s\S]lub obejścia JS ( wersja demonstracyjna ):regex rex(R"(([\s\S]*)<FooBar>)");
  • - Użyj tego samego podejścia jak w JavaScripcie ([\s\S]*)<Foobar>. ( UWAGA : MultiLineWłaściwość RegExpobiektu jest czasami mylnie uważana za opcję umożliwiającą .dopasowanie między podziałami linii, podczas gdy w rzeczywistości zmienia ona tylko zachowanie ^i $dopasowuje początek / koniec linii zamiast ciągów znaków , tak jak w wyrażeniu regularnym JS ) zachowanie).

  • - Użyj modyfikatora /m MULTILINE ( demo ):s[/(.*)<Foobar>/m, 1]

  • - Wyrażenia bazowe PCRE R - użyj (?s): regmatches(x, regexec("(?s)(.*)<FooBar>",x, perl=TRUE))[[1]][2]( demo )
  • - funkcje in stringr/ stringiregex, które są zasilane silnikiem regex ICU, również użyj (?s): stringr::str_match(x, "(?s)(.*)<FooBar>")[,2]( demo )
  • - Użyj wbudowanego modyfikatora (?s)na początku ( demo ):re: = regexp.MustCompile(`(?s)(.*)<FooBar>`)
  • - Użyj dotMatchesLineSeparatorslub (łatwiej) przekaż (?s)modyfikator wbudowany do wzorca:let rx = "(?s)(.*)<Foobar>"
  • - Podobnie jak Swift, (?s)działa najłatwiej, ale oto jak można użyć tej opcji :NSRegularExpression* regex = [NSRegularExpression regularExpressionWithPattern:pattern options:NSRegularExpressionDotMatchesLineSeparators error:&regexError];
  • , - Użyj (?s)modyfikatora ( demo ): "(?s)(.*)<Foobar>"(w arkuszach kalkulacyjnych Google =REGEXEXTRACT(A2,"(?s)(.*)<Foobar>"))

UWAGI NA(?s) :

W większości silników innych niż POSIX (?s)można użyć wbudowanego modyfikatora (lub wbudowanej opcji flagi) w celu wymuszenia .dopasowania podziałów linii.

Umieszczony na początku wzoru (?s)zmienia zachowanie wszystkich elementów .we wzorze. Jeśli (?s)zostanie umieszczony gdzieś po początku, .wpłynie to tylko na te , które znajdują się po jego prawej stronie, chyba że jest to wzór przekazany Pythonowi re. W Pythonie re, niezależnie od (?s)lokalizacji, .wpływa to na cały wzorzec . (?s)Efekt jest zatrzymywany za pomocą (?-s). Zmodyfikowanej grupy można użyć, aby wpływała tylko na określony zakres wzorca wyrażenia regularnego (np. Dopasuje Delim1(?s:.*?)\nDelim2.*pierwsze .*?dopasowanie do nowych linii, a drugie .*dopasuje tylko resztę linii).

Uwaga POSIX :

W silnikach wyrażeń regularnych innych niż POSIX, aby dopasować dowolny znak, można użyć konstrukcji [\s\S]/ [\d\D]/ [\w\W].

W POSIX [\s\S]nie pasuje do żadnego znaku (jak w JavaScript lub innym silniku innym niż POSIX), ponieważ sekwencje specjalne wyrażeń regularnych nie są obsługiwane w wyrażeniach nawiasów. [\s\S]jest analizowany jako wyrażenia w nawiasach pasujące do jednego znaku \lub slub S.

Wiktor Stribiżew
źródło
5
Powinieneś link do tego doskonałego przeglądu ze strony swojego profilu lub czegoś (+1).
stycznia
1
Możesz dodać to do elementu boost : W przestrzeni nazw regex_constants flag_type_'s: perl = ECMAScript = JavaScript = JScript = :: boost :: regbase :: normal = 0, która domyślnie to Perl. Programiści ustawią podstawową definicję flagi #define MOD regex_constants::perl | boost::regex::no_mod_s | boost::regex::no_mod_mdla swoich flag wyrażenia regularnego, aby to odzwierciedlić. A arbitrem są zawsze wbudowane modyfikatory. Gdzie (?-sm)(?s).*resetuje się.
1
Czy możesz również dodać do bash?
Pasupathi Rajamanickam
2
@PasupathiRajamanickam Bash używa silnika regex POSIX, .dopasowuje dowolny znak tam (w tym podział wiersza). Zobacz to demo online Bash .
Wiktor Stribiżew,
1
Kołyszesz - to najbardziej wyczerpujący mini-poradnik na temat (względnie) złożonych wyrażeń regularnych, jaki kiedykolwiek widziałem. Zasługujesz na to, aby Twoja odpowiedź stała się odpowiedzią! Wyrazy uznania i dodatkowe głosy za uwzględnienie Gow odpowiedzi!
Gwyneth Llewelyn
68

Jeśli korzystasz z wyszukiwania Eclipse, możesz włączyć opcję „DOTALL”, aby utworzyć „.” dopasuj dowolny znak, w tym ograniczniki linii: po prostu dodaj „(? s)” na początku szukanego ciągu. Przykład:

(?s).*<FooBar>
Paulo Merson
źródło
1
Nigdzie, tylko w wersjach regularnych obsługujących wbudowane modyfikatory, a na pewno nie w Ruby gdzie (?s)=>(?m)
Wiktor Stribiżew
Coś na bash?
Pasupathi Rajamanickam
38

W wielu dialektach regularnych /[\S\s]*<Foobar>/zrobi to, co chcesz. Źródło

Abbas Shahzadeh
źródło
2
Z tego linku: „JavaScript i VBScript nie mają opcji dopasowania kropki do znaków podziału wiersza. W tych językach można użyć klasy znaków, takiej jak [\ s \ S], aby dopasować dowolny znak.” Zamiast tego . zamiast tego użyj [\ s \ S] (dopasuj spacje i spacje).
Allen
32

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

Kropka pasuje do wszystkich oprócz znaków nowej linii (\ r \ n). Więc użyj \ s \ S, który będzie pasował do WSZYSTKICH znaków.

samwize
źródło
To rozwiązuje problem, jeśli używasz Objective-C [text rangeOfString:regEx options:NSRegularExpressionSearch]. Dzięki!
J. Costa
1
Działa to w znajdowaniu i zastępowaniu wyrażenia regularnego intelliJ, dzięki.
barclay
To działa. Ale to musi być pierwsze wystąpienie<FooBar>
Ozkan
18

W Ruby możesz użyć opcji „ m” (wielowierszowa):

/YOUR_REGEXP/m

Aby uzyskać więcej informacji, zobacz dokumentację Regexp na ruby-doc.org.

wibrator
źródło
13

możemy również użyć

(.*?\n)*?

dopasować wszystko, w tym nową linię bez zachłanności

Dzięki temu nowa linia będzie opcjonalna

(.*?|\n)*?
Nambi_0915
źródło
8

"."zwykle nie pasuje do podziałów linii. Większość silników wyrażeń regularnych pozwala na dodanie opcji S-flag (nazywanej także DOTALLi SINGLELINE), aby "."dopasować także nowe znaki. Jeśli to się nie powiedzie, możesz zrobić coś takiego [\S\s].

Markus Jarderot
źródło
8

W przypadku Eclipse działało następujące wyrażenie:

bla

jadajada Bar "

Wyrażenie regularne:

Foo[\S\s]{1,10}.*Bar*
Gordon
źródło
5
/(.*)<FooBar>/s

s powoduje, że kropka (.) dopasowuje zwroty karetki

Rachunek
źródło
Wygląda na to, że to jest nieprawidłowe (Chrome): text.match (/ a / s) SyntaxError: Niepoprawne flagi dostarczone do konstruktora RegExp
Allen
Ponieważ nie jest obsługiwany w silnikach JavaScript RegEx. Te sflagi istnieje w PCRE, najbardziej kompletny silnik (dostępne w Perl i PHP). PCRE ma 10 flag (i wiele innych funkcji), podczas gdy JavaScript ma tylko 3 flagi ( gmi).
Morgan Touverey Quilling 20.04.16
4

W wyrażeniach regularnych opartych na języku Java można używać [\s\S]

Kamahire
źródło
1
Czy nie powinny to być odwrotne ukośniki?
Paul Draper,
Idą na końcu wyrażenia regularnego, ale nie w. Przykład: / blah / s
RandomInsano
Myślę, że masz na myśli JavaScript, a nie Javę? Ponieważ możesz po prostu dodać sflagę do wzorca w Javie, a JavaScript nie ma sflagi.
3limin4t0r
3

Zauważ, że (.|\n)*może to być mniej wydajne niż (na przykład) [\s\S]*(jeśli wyrażenia regularne w Twoim języku obsługują takie znaki ucieczki) i niż znalezienie sposobu na określenie modyfikatora, który czyni. pasują również do nowych linii. Możesz też skorzystać z alternatyw POSIXy, takich jak [[:space:][:^space:]]*.

tye
źródło
3

Użyj RegexOptions.Singleline, zmienia to znaczenie. zawierać nowe linie

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

Shmall
źródło
1

W kontekście użycia w językach, wyrażenia regularne działają na ciągi, a nie na wiersze. Powinieneś być w stanie normalnie używać wyrażenia regularnego, zakładając, że łańcuch wejściowy ma wiele wierszy.

W takim przypadku podany regex będzie pasował do całego łańcucha, ponieważ „<FooBar>” jest obecny. W zależności od specyfiki implementacji wyrażenia regularnego wartość 1 USD (uzyskana z „(. *)”) Będzie albo „fghij”, albo „abcde \ nfghij”. Jak powiedzieli inni, niektóre implementacje pozwalają kontrolować, czy „.” dopasuje nową linię, dając ci wybór.

Wyrażenia regularne oparte na liniach są zwykle używane do wiersza poleceń, np. Egrep.

nsayer
źródło
1

Miałem ten sam problem i rozwiązałem go prawdopodobnie nie w najlepszy sposób, ale działa. Zastąpiłem wszystkie podziały linii, zanim wykonałem mój prawdziwy mecz:

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

Manipuluję HTML, więc podział wiersza tak naprawdę nie ma dla mnie znaczenia w tym przypadku.

Wypróbowałem wszystkie powyższe sugestie bez powodzenia, używam .Net 3.5 FYI

Slee
źródło
Używam również .NET i (\s|\S)wydaje mi się, że załatwił sprawę!
Vamshi Krishna
@VamshiKrishna W .NET użyj, (?s)aby .dopasować dowolne znaki. Nie używaj (\s|\S), aby spowolnić działanie.
Wiktor Stribiżew
1

W Javascripcie możesz użyć [^] * do wyszukiwania od zera do nieskończonych znaków, w tym do łamania linii.

$("#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
źródło
0

ogólnie. nie pasuje do nowych linii, więc spróbuj((.|\n)*)<foobar>

tloach
źródło
3
Nie rób tego. Jeśli chcesz dopasować cokolwiek, w tym separatory linii, użyj modyfikatora DOTALL (aka / s lub SingleLine). Hack (. | \ N) nie tylko sprawia, że ​​regex jest mniej wydajny, ale nawet nie jest poprawny. Przynajmniej powinien on pasować do \ r (powrót karetki), a także \ n (linefeed). Istnieją również inne znaki separatora wierszy, choć rzadko używane. Ale jeśli użyjesz flagi DOTALL, nie musisz się o nie martwić.
Alan Moore,
1
\ R jest niezależnym od platformy dopasowaniem dla nowych linii w Eclipse.
opyate
@opyate Powinieneś opublikować to jako odpowiedź, ponieważ ten mały klejnot jest niezwykle przydatny.
jeckhart
Możesz zamiast tego spróbować. Nie będzie pasował do nawiasów wewnętrznych, a także będzie opcjonalny \r:((?:.|\r?\n)*)<foobar>
ssc-hrep3
0

Chciałem dopasować konkretny blok if w Javie

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

   }
...
...
}

Jeśli użyję regExp

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

zawierało nawias zamykający dla bloku metody, więc użyłem

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

aby wykluczyć nawias zamykający z dopasowania z symbolem wieloznacznym.

Spangen
źródło
0

Często musimy zmodyfikować podciąg za pomocą kilku słów kluczowych rozmieszczonych w liniach poprzedzających podłańcuch. Rozważ element xml:

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

Załóżmy, że chcemy zmodyfikować 81 do innej wartości, powiedzmy 40. Najpierw zidentyfikuj .UID.21..UID., a następnie pomiń wszystkie znaki, w tym \nkasę .PercentCompleted.. Wzorem wyrażeń regularnych i specyfikacją zamiany są:

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>

Podgrupa (.|\n)jest prawdopodobnie brakującą grupą $3. Jeśli sprawimy, że do tej (?:.|\n)pory nie będzie przechwytywany, to $3znaczy, że jest (<PercentComplete>). Więc wzór i replaceSpecmoże być również:

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

a zamiennik działa poprawnie jak poprzednio.

użytkownik1348737
źródło
0

Zwykle wyszukiwanie trzech kolejnych wierszy w PowerShell wygląda następująco:

$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

Dziwnie, to byłby tekst unix po znaku zachęty, ale tekst Windows w pliku:

$pattern = 'lineone
linetwo
linethree
'

Oto sposób wydrukowania zakończeń linii:

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

# output
lineone\nlinetwo\nlinethree\n
js2010
źródło
-2

opcja 1

Jednym ze sposobów byłoby użycie sflagi (podobnie jak zaakceptowana odpowiedź):

/(.*)<FooBar>/s

Demo 1

Opcja 2

Drugim sposobem byłoby użycie mflagi (wielowierszowej) i dowolnego z następujących wzorców:

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

lub

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

lub

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

Demo 2

RegEx Circuit

jex.im wizualizuje wyrażenia regularne:

wprowadź opis zdjęcia tutaj

Emma
źródło