Треки
/
Julia
Julia
/
Салабус
/
Регулярні вирази
Ре

Регулярні вирази у Julia

4 вправи

Про концепцію Регулярні вирази

Регулярні вирази дають надзвичайно гнучкий спосіб шукати збіги за шаблоном у рядках тексту (англ. string) за допомогою спеціальної предметно-орієнтованої мови (DSL), призначеної саме для цього.

Як і чимало інших мов програмування, Julia не намагається реалізувати власну бібліотеку регулярних виразів. Замість цього вона використовує популярну бібліотеку PCRE2, завдяки чому синтаксис регулярних виразів такий самий, як (наприклад) у Gleam, і дуже схожий на JavaScript.

Note

Цей силабус Julia припускає, що ми вже знайомі з базовим синтаксисом регулярних виразів. Ми зосередимося виключно на особливостях, властивих Julia.

Нижче наведено кілька ресурсів, щоб освіжити знання про регулярні вирази.

Інтерфейс Julia до регулярних виразів описано в посібнику.

Регулярний вираз у Julia записують просто як рядок тексту з r перед відкривальною лапкою ". Уся базова функціональність входить до стандартної бібліотеки.

Насправді багато функцій, які ми вже розглядали в концепції Strings, стандартно розраховані на пошук за допомогою регулярних виразів, як-от occursin().

julia> re = r"test$"
r"test$"

julia> typeof(re)
Regex

# Does a string end with "test"?
julia> occursin(re, "this is a test")
true

julia> occursin(re, "these are tests")
false

Після закривальної лапки можуть стояти символи-модифікатори, як-от i для пошуку без урахування регістру.

julia> occursin(r"test", "Testing")
false

julia> occursin(r"test"i, "Testing")
true

Захоплення

Часто нам потрібно знати, що саме збіглося. Для цього в регулярний вираз додають групи захоплення в дужках, а потім використовують функцію match().

julia> m = match(r"(\d+g) .* (\d+ml)", "dissolve 25g sugar in 200ml water")
RegexMatch("25g sugar in 200ml", 1="25g", 2="200ml")

julia> m.captures
2-element Vector{Union{Nothing, SubString{String}}}:
 "25g"
 "200ml"

# how many matches?
julia> length(m.captures)
2

# what matched?
julia> m[1], m[2]
("25g", "200ml")

# Starting positions of the matches (character index)
julia> m.offsets
2-element Vector{Int64}:
 10
 23

Звісно, пошук збігу може не вдатися. Тоді результатом буде спеціальне значення Nothing замість RegexMatch, тож будьмо готові це перевіряти.

# failed match
m = match(r"(not here)", "dissolve 25g sugar in 200ml water")

julia> typeof(m)
Nothing

julia> isnothing(m)
true

Хоча match типово починає пошук із початку рядка тексту, ми також можемо вказати зсув n, щоб пропустити перші n символів.

# capture first match
julia> m = match(r"(\wat)", "cat, sat, mat")
RegexMatch("cat", 1="cat")

# ignore first 5 characters, then match
julia> m = match(r"(\wat)", "cat, sat, mat", 5)
RegexMatch("sat", 1="sat")

У Julia match() знаходить лише перший збіг у цільовому рядку тексту: глобального модифікатора, як у деяких інших мовах, тут немає.

Натомість є eachmatch(), яка повертає ітератор збігів. Він обчислюється ліниво, тож його, ймовірно, доведеться перетворити на потрібний нам формат.

julia> matches = eachmatch(r"(\wat)", "cat, sat, mat")
Base.RegexMatchIterator{String}(r"(\wat)", "cat, sat, mat", false)

# convert to vector
julia> collect(matches)
3-element Vector{RegexMatch}:
 RegexMatch("cat", 1="cat")
 RegexMatch("sat", 1="sat")
 RegexMatch("mat", 1="mat")

# convert with comprehension
julia> [m.match for m in matches]
3-element Vector{SubString{String}}:
 "cat"
 "sat"
 "mat"

# broadcast an anonymous function
julia> (m -> m.match).(matches)
3-element Vector{SubString{String}}:
 "cat"
 "sat"
 "mat"

Збіги, що перекриваються, типово не дозволені. Щоб це змінити, додамо overlap = true як ключовий аргумент.

julia> eachmatch(r"aba", "abababa") |> collect  # matches at positions 1, 5
2-element Vector{RegexMatch}:
 RegexMatch("aba")
 RegexMatch("aba")

julia> eachmatch(r"aba", "abababa"; overlap = true) |> collect  # also matches at position 3
3-element Vector{RegexMatch}:
 RegexMatch("aba")
 RegexMatch("aba")
 RegexMatch("aba")

Заміна

Одна з поширених причин скористатися регулярним виразом - замінити знайдений збіг на інший рядок тексту.

Функцію replace() ми вже розглядали в концепції Strings, коли шукали за рядковими літералами. Та сама функція може скористатися всією потугою пошуку за регулярними виразами.

julia> replace("some string", r"[aeiou]" => "*")
"s*m* str*ng"

julia> replace("first second", r"(\w+) (?<agroup>\w+)" => s"\g<agroup> \1")
"second first"

Другий приклад вище показує, як і в заміні, усередині рядка s" ", можна використати і нумеровані, і іменовані групи захоплення.

Докладніше про це можна прочитати в посібнику: це тема, яка постійно змушує більшість програмістів повертатися до документації!

Редагувати через GitHub Посилання відкривається в новому вікні або вкладці

Вивчити концепцію Регулярні вирази

Практика заблокована

Розблокуйте ще 3 вправи, щоб практикувати концепцію Регулярні вирази