Golang regexp: сопоставление символа новой строки

30 марта 2020 г. Regular Expressions Sources


Golang regexp: сопоставление символа новой строки

Почему регулярные выражения с точкой (".") работают по-другому в Go по сравнению с PHP и JavaScript.

Для включения подсветки синтаксиса кода на этом сайте я использую регулярные выражения. Логика проста — я помещаю исходный код в специальные HTML-теги. Когда пост загружается, я обрабатываю эти теги — ищу их с помощью регулярных выражений и заменяю исходный код на подсвеченные версии.

Я потратил много времени, пытаясь понять, почему некоторые примеры кода не совпадали с регулярными выражениями. Я использовал специальный символ точки “.” для сопоставления любого символа внутри моего тега. Посмотрите на следующий пример регулярного выражения и текста и угадайте, совпадает ли он или нет:

Регулярное выражение:

<tag>(.*?)</tag>

Текст:

<tag>1
2
3</tag>

Если у вас есть опыт работы с PHP, ваш ответ, вероятно, будет “да”.

Однако простой пример из Go Playground ясно показывает, что ответ на самом деле “нет”:

match, _ := regexp.MatchString("<tag>(.*)</tag>", "<tag>1\n2\n3</tag>")
fmt.Println(match)

// false

Затем я искал в исходниках Go, пытаясь понять, как Go работает с классами символов, и нашел следующий список флагов:

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 для “планирования” выполнения регулярного выражения (для сопоставления символов регулярного выражения с операциями)
  • regexp.Regexp (публичная структура) создается с использованием результатов syntax.Parse

Таким образом, нам нужно скомпилировать регулярное выражение с флагом DotNL.

Когда я искал все случаи использования функции regexp.Compile, я обнаружил, что доступны только два варианта флагов регулярных выражений — POSIX и Perl. Это означает, что в Go нет возможности сопоставлять символы новой строки с точкой.

Итак, регулярное выражение, которое на самом деле работает, приведено ниже:

<tag>([[:graph:]\\s]*?)</tag>

Также существует множество предопределенных классов символов, документированных здесь. Я использовал два из них, чтобы покрыть все символы в скобках [].

Tags:

Похожие статьи

9 Apr 2020

Шаблоны GO: принципы и использование

Шаблоны GO: принципы и использование

Пакеты text/template и html/template являются частью стандартной библиотеки Go. Шаблоны Go используются во многих программах, написанных на Go — Docker, Kubernetes, Helm. Многие сторонние библиотеки интегрированы с шаблонами Go, например Echo. Знание синтаксиса шаблонов Go очень полезно.

Эта статья состоит из документации пакета text/template и нескольких решений автора. После описания синтаксиса шаблонов Go мы погрузимся в исходники text/template и html/template.

Read More → Templates Html Text Sources
4 Apr 2020

Принципы работы типа slice в GO

Принципы работы типа slice в GO

В блоге Go описывается, как использовать срезы. Давайте посмотрим на внутреннее устройство срезов.

Read More → Slice Allocation Sources
2 Apr 2020

Обработка данных в конкурентных программах

Обработка данных в конкурентных программах

В Go у нас есть функциональность горутин из коробки. Мы можем запускать код параллельно. Однако в нашем параллельно выполняющемся коде мы можем работать с общими переменными, и не совсем понятно, как именно Go обрабатывает такие ситуации.

Read More → Map Sources
2 Apr 2020

Принципы работы типа map в GO

Принципы работы типа map в GO

Программный интерфейс map в Go описан в блоге Go. Нам просто нужно вспомнить, что map — это хранилище ключ-значение, и оно должно извлекать значения по ключу как можно быстрее.

Read More → Map Sources
30 Mar 2020

Golang regexp: matching newline

Why PHP- and JavaScript-like regular expressions work with dot (".") work differently in GO.

Read More → Regular Expressions Sources