Разница между модификаторами регулярных выражений (или флагами) «m» и «s»?

Я часто забываю о модификаторах регулярных выражений m и s и их различиях. Как лучше их запомнить?

Насколько я понимаю, это:

'm' для многострочного, так что ^ и $ будут соответствовать началу строки и концу строки несколько раз. (деленное на \n)

's' означает, что точка будет соответствовать даже символу новой строки

Часто я просто использую

/some_pattern/ism

Но, вероятно, лучше использовать их соответственно (обычно «s» в моем случае).

Как вы думаете, что может быть хорошим способом запомнить их, вместо того, чтобы каждый раз забывать, что есть что?


person nonopolarity    schedule 28.05.2009    source источник


Ответы (3)


Нередко можно найти человека, годами использующего регулярные выражения, который до сих пор не понимает, как работают эти два модификатора. Как вы заметили, названия «многострочный» и «однострочный» не очень полезны. Звучит так, как будто они должны быть взаимоисключающими, но они полностью независимы. Я предлагаю вам игнорировать имена и сосредоточиться на том, что они делают: m меняет поведение якорей (^ и $), а s меняет поведение точки (.).

Одним из видных деятелей, перепутавших режимы, является автор Ruby. Он создал свою собственную реализацию регулярных выражений на основе Perl, за исключением того, что он решил, что ^ и $ всегда будут якорями строк, то есть многострочный режим всегда включен. К сожалению, он также неправильно назвал режим «совпадение точек со всем» multiline. Таким образом, в Ruby нет модификатора s, но его модификатор m делает то, что делает s в других разновидностях.

Что касается постоянного использования /ism, я не рекомендую этого делать. В основном это безвредно, как вы уже убедились, но оно посылает сбивающее с толку сообщение всем, кто пытается понять, что должно делать регулярное выражение (или даже вам самим, в будущем).

person Alan Moore    schedule 28.05.2009
comment
что, если в Ruby я хочу, чтобы он соответствовал только началу и концу строки, игнорируя \n ? - person nonopolarity; 28.05.2009
comment
Затем вы используете \A и \z. Они доступны и в большинстве других вкусов; вы просто не видите, что они используются очень часто. - person Alan Moore; 29.05.2009

Мне нравится объяснение в «man perlre»:

m Рассматривать строку как mнесколько строк.
s Рассматривать строку как sодну строку.

В случае нескольких строк ^ и $ применяются к отдельным строкам (т. е. непосредственно перед и после новой строки).
В случае одной строки ^ и $ применяются ко всему, а \n просто становится другим символом, с которым можно сопоставиться.

[Неправильно]Используя как m, так и s, как вы описали, я ожидаю, что второй будет иметь приоритет, поэтому вы всегда будете в многострочном режиме с /ism.[/Wrong]< /б>

Я не дочитал до конца:
Модификаторы "/s" и "/m" переопределяют параметр $*. То есть, независимо от того, что содержит $*, "/s" без "/m" заставит "^" соответствовать только началу строки, а "$" - только в конце (или непосредственно перед новой строкой в ​​строке). конец) строки. Вместе, как /ms, они позволяют "." совпадать с любым символом, при этом позволяя "^" и "$" совпадать, соответственно, сразу после и непосредственно перед новой строкой в ​​строке.

person JimG    schedule 28.05.2009
comment
хм... правда ли, что если мы не используем 'm' или 's', то это не будет ни многострочной, ни однострочной? вы могли бы подумать, что это должен быть любой из них. - person nonopolarity; 28.05.2009
comment
кстати, такой способ описания приведет к конфликту для ^ и $, если мы используем как «m», так и «s»... я использую определение из PHP... так что, возможно, определение немного отличается на другой платформе. - person nonopolarity; 28.05.2009

Обновление 2020:

Я могу более четко написать, что это такое, и способ их запомнить, и я пишу это как относящееся к JavaScript:

  1. традиционно регулярное выражение JS не имеет флага s. Он имеет только флаг m. По состоянию на январь 2020 года в Firefox его все еще нет, а в Chrome он есть. И в NodeJS это есть. Это указано в спецификациях ES2018.
  2. s также называют dotall или singleline. И действительно, . соответствует любому (ASCII) символу, включая \n, \r, \u2028 (разрыв строки), \u2029 (разрыв абзаца). Когда люди спрашивают вас, чему соответствует .? А если ответить "любой символ", то это не совсем правильно. Это все (ASCII) символы, кроме символа новой строки, \r и разрыва строки в Юникоде и разрыва абзаца. Чтобы он соответствовал действительно всем символам ASCII, он должен иметь включенный флаг s.
  3. Чтобы избежать отсутствия флага s в Firefox или на любой другой платформе, это могут быть [^], [\s\S], [\d\D] и т. д. или (.|\s).
  4. Это все. Речь идет о флаге s, который отсутствует в традиционном JavaScript.
  5. Теперь флаг m. Это означает многострочный. И это действительно очень просто: без флага m ^ и $ будут соответствовать только началу и концу всей строки. Так что "John Doe\nMary Lee".match(/^John Doe$/) не совпадет, а "John Doe\nMary Lee".match(/^John Doe$/m) совпадет. Это все. Не думайте об этом слишком сложно. Это просто меняет способ сопоставления ^ и $.
  6. Так являются ли «однострочный» и «многострочный» взаимоисключающими? Нет, они не. Например, если я хочу сопоставить a, а затем любые символы, включая новую строку и f, но a должно быть в начале строки, а f должно быть в конце строки, даже если из 2000 строк текста, тогда "a b c \n d e f\nha".match(/^a.*f$/ms) это то, что нужно использовать. Оба . соответствуют \n, а ^ и $ соответствуют началу строки и концу строки.

Вот и все. Вышеприведенное было протестировано на NodeJS и Chrome, которые уже поддерживают флаг s. (а флаг m уже давно поддерживается). И помните, вы всегда можете решить проблему отсутствия флага s, используя [^]

Теперь, почему ms или ism часто использовались в прошлом? Потому что во многих случаях, когда у нас есть очень длинная строка (например, 2000 строк HTML), например, в случае некоторого веб-контента, который мы возвращаем, мы редко хотим сопоставить ^ с началом всей строки и $ с конец всей строки. Вот почему мы используем флаг m. Теперь мы, вероятно, хотим сопоставить строки, потому что (хотя и не рекомендуется использовать регулярное выражение для сопоставления HTML), мы можем использовать /<h1>.*?</h1>/, например, для нежадного сопоставления заголовка. Мы не возражаем против \n в содержании, потому что у автора HTML вполне может быть \n (или нет). Вот почему мы используем флаг «dotall» s.

Но если вы пытаетесь извлечь некоторую информацию с веб-страницы, вам, вероятно, все равно, находится ли что-то в начале строки или в конце строки (поскольку файлы HTML могут содержать пробелы в них (или в виде отступа), и это не имеет значения). не влияет на содержимое страницы (обычно, если нет <pre> и т. д.)), поэтому вам не нужно будет использовать ^ или $, а значит, вы можете забыть о флаге m. И если вы не возражаете против использования [^]*? вместо .*?, то можете забыть и о флаге s — конец истории.

Perl Cookbook сказал это в двух предложениях:

Разница между /m и /s важна: /m заставляет ^ и $ соответствовать новой строке, а /s заставляет . соответствовать новой строке. Вы даже можете использовать их вместе — это не взаимоисключающие варианты.


может так, я никогда не забуду:

когда я хочу сопоставить несколько строк (обычно используя .*? для сопоставления чего-то, что не имеет значения, если оно охватывает несколько строк), я, естественно, подумаю о многострочном и, следовательно, «m». Ну, на самом деле «м» не тот, так что это «с».

(так как я уже так хорошо помню "изм"... так что я всегда могу вспомнить, что это не "м", тогда это должно быть "с").

другая неудачная попытка включает в себя:

s для DOTALL, это для того, чтобы DOT соответствовал ВСЕМ.
m является многострочным -- это для ^ и $ для многократного совпадения.

person nonopolarity    schedule 28.05.2009
comment
s для суперсопоставления, так что вы можете сопоставлять даже невидимые символы;) - person JimG; 28.05.2009
comment
@JimG, это одна из самых забавных вещей, которые я когда-либо читал в stackoverflow. - person Dmiters; 21.03.2016