31.03.2020 регулярные выражения исходники
Для форматирования вставок кода на этом сайте используется логика на основе регулярных выражений. Логика проста — вынимаем содержимое определенного тега, процессим его с помощью библиотеки подсветки синтаксиса и заменяем исходный тег на новый.
Много времени ушло именно на отладку данного регулярного выражения,
и даже дебаггер не помог, т.к. проблема была с самим регулярным выражением.
Например, как вы думаете, заматчит ли регулярное выражение (ниже) текст (еще ниже)?
Регулярка:
<tag>(.*)</tag>
Текст:
<tag>1 2 3</tag>
Если вы пришли из PHP (как и я), то скажете, что да.
Однако, простой пример с go playground опровергает данное предположение:
match, _ := regexp.MatchString("<tag>(.*)</tag>", "<tag>1\n2\n3</tag>") fmt.Println(match) // false
Исследование исходников пакета regexp навело на список флагов:
const ( FoldCase Flags = 1 << iota // case-insensitive match Literal // treat pattern as literal string ClassNL // allow character classes like [^a-z] and [[:space:]] to match newline DotNL // allow . to match newline OneLine // treat ^ and $ as only matching at beginning and end of text NonGreedy // make repetition operators default to non-greedy PerlX // allow Perl extensions UnicodeGroups // allow \p{Han}, \P{Han} for Unicode group and negation WasDollar // regexp OpEndText was $, not \z Simple // regexp contains no counted repetition MatchNL = ClassNL | DotNL Perl = ClassNL | OneLine | PerlX | UnicodeGroups // as close to Perl as possible POSIX Flags = 0 // POSIX syntax )
Согласно исходникам, GO работает с регуляркой следующим образом:
syntax.Parse
syntax.Parse
затем использует Flags, чтобы "распланировать"
выполнение регулярного выражения (определить, какие конкретно операции будут выполнены в зависимости от символов в регулярке)syntax.Parse
Итак, нам нужно найти как скомпилировать регулярное выражение с флагом DotNL
.
Поискав все вызовы функции compile из пакета Regex, прихожу к выводу, что на данный момент
я могу скомпилировать регулярное выражение только с флагами Posix
или Perl
,
те варианта с флагом DotNL или включающим его не существует.
Поэтому регулярное выражение, которое действительно будет матчить все символы внутри тега, будет выгрядеть примерно так:
<tag>([[:graph:]\\s]*?)</tag>
В GO есть классы символов, которые включают в себя много отдельных символов, это задокументировано здесь. Я использовал 2 из них, чтобы покрыть действительно все символы в группе [ ].