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>

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.


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.


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.


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>

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


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.


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


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)*?

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].


8

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

bla

jadajada Bar "

Wyrażenie regularne:

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

5
/(.*)<FooBar>/s

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


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]


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:]]*.


3

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

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



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.


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


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>


0

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


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.


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.


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

-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

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.