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

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

1 вправа

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

Регулярні вирази (regex) - це послідовності символів, які задають шаблон пошуку в тексті.

Вивчення синтаксису регулярних виразів виходить за межі цієї теми. Ми зосередимося на виразах, які jq надає для роботи з регулярними виразами.

Різновид регулярних виразів

Різні інструменти реалізують різні версії регулярних виразів. jq використовує бібліотеку регулярних виразів Oniguruma, яка значною мірою сумісна з регулярними виразами Perl v5.8.

Конкретний синтаксис, який використовує jq, можна знайти в репозиторії Oniguruma на GitHub.

Caution

У jq немає якогось спеціального синтаксису для регулярних виразів. Їх просто записують як рядки тексту (англ. string). Це означає, що будь-які зворотні косі риски в регулярному виразі потрібно екранувати в рядку.

Наприклад, клас символів для цифр (\d) потрібно записати як "\\d".

Функції для регулярних виразів

Робота з регулярними виразами в jq зводиться до набору фільтрів.

Простий пошук збігів

Коли потрібно дізнатися, чи рядок тексту відповідає шаблону, скористаймося фільтром test.

STRING | test(REGEX)
STRING | test(REGEX; FLAGS)
STRING | test([REGEX, FLAGS])

Цей фільтр видає булеве значення (англ. Boolean).

"Hello World!" | test("W")    # => true
"Goodbye Mars" | test("W")    # => false

Інформація про збіг

Коли потрібно видобути підрядок, який насправді збігся з шаблоном, скористаймося фільтром match.

STRING | match(REGEX)
STRING | match(REGEX; FLAGS)
STRING | match([REGEX, FLAGS])

Цей фільтр видає:

  • нічого, якщо збігу не було, або
  • обʼєкт із різними властивостями, якщо збіг був.

У цьому прикладі ми шукаємо дві однакові голосні підряд, використовуючи синтаксис зворотних посилань \1.

"Hello World!" | match("([aeiou])\\1")
# => empty

"Goodbye Mars" | match("([aeiou])\\1")
# => {
#      "offset": 1,
#      "length": 2,
#      "string": "oo",
#      "captures": [
#        {
#          "offset": 1,
#          "length": 1,
#          "string": "o",
#          "name": null
#        }
#      ]
#    }

Фільтр match повертає обʼєкт для кожного збігу. У цьому прикладі в дії показано прапорець "g", який знаходить усі голосні.

"Goodbye Mars" | match("[aeiou]"; "g")
# => { "offset": 1, "length": 1, "string": "o", "captures": [] }
#    { "offset": 2, "length": 1, "string": "o", "captures": [] }
#    { "offset": 6, "length": 1, "string": "e", "captures": [] }
#    { "offset": 9, "length": 1, "string": "a", "captures": [] }

Захоплені підрядки

Подібно до фільтра match, фільтр capture повертає обʼєкт, якщо збіг був.

STRING | capture(REGEX)
STRING | capture(REGEX; FLAGS)
STRING | capture([REGEX, FLAGS])

Повернений обʼєкт містить відображення іменованих захоплень.

"JIRAISSUE-1234" | capture("(?<project>\\w+)-(?<issue_num>\\d+)")
# => {
#      "project": "JIRAISSUE",
#      "issue_num": "1234"
#    }

Лише підрядки

Фільтр scan подібний до match із прапорцем "g".

STRING | scan(REGEX)
STRING | scan(REGEX; FLAGS)

# note, there is no scan([REGEX, FLAGS]) version, unlike other filters

scan видає потік підрядків.

"Goodbye Mars" | scan("[aeiou]")
# => "o"
#    "o"
#    "e"
#    "a"

Скористаймося конструктором масиву [...], щоб захопити підрядки.

"Goodbye Mars" | [ scan("[aeiou]") ]
# => ["o", "o", "e", "a"]

Розбиття рядка тексту

Якщо відомо, які частини рядка тексту потрібно зберегти, скористаймося match або scan. Якщо відомо, які частини потрібно відкинути, скористаймося split.

STRING | split(REGEX; FLAGS)
Caution

Фільтр split з одним аргументом трактує свій аргумент як фіксований рядок тексту.

Щоб скористатися регулярним виразом із split, потрібно передати другий аргумент; порожній рядок тексту теж підійде.

Приклад, який розбиває рядок тексту за довільними пробільними символами.

"first   second           third fourth" | split("\\s+"; "")
# => ["first", "second", "third", "fourth"]
Note

Ось що станеться, якщо забути про аргумент із прапорцями.

"first   second           third fourth" | split("\\s+")
# => ["first   second           third fourth"]

Лише один результат: фіксованого рядка тексту \s+ у вхідних даних не було.

split з одним аргументом не впорається з довільними пробільними символами. Розбиття за пробілом дає такий результат.

"first   second           third fourth" | split(" ")
# => ["first", "", "", "second", "", "", "", "",
#     "", "", "", "", "", "", "third", "fourth" ]

Заміни

Фільтри sub і gsub можуть перетворювати вхідний рядок тексту, замінюючи знайдені частини вхідних даних на рядок заміни.

Щоб замінити лише перший збіг, скористаймося sub. Щоб замінити всі збіги, скористаймося gsub.

STRING | sub(REGEX; REPLACEMENT)
STRING | sub(REGEX; REPLACEMENT; FLAGS)
STRING | gsub(REGEX; REPLACEMENT)
STRING | gsub(REGEX; REPLACEMENT; FLAGS)
"Goodnight kittens. Goodnight mittens." | sub("night"; " morning")
# => "Good morning kittens. Goodnight mittens."

"Goodnight kittens. Goodnight mittens." | gsub("night"; " morning")
# => "Good morning kittens. Good morning mittens."

Текст заміни може посилатися на знайдені підрядки; скористаймося іменованими захопленнями та інтерполяцією рядків.

"Some 3-letter acronyms: gnu, csv, png"
| gsub( "\\b(?<tla>[[:alpha:]]{3})\\b";     # find words 3 letters long
        "\(.tla | ascii_upcase)" )          # upper-case the match
# => "Some 3-letter acronyms: GNU, CSV, PNG"

Прапорці

У всіх наведених вище фільтрах FLAGS - це рядок тексту, що складається з нуля або більше підтримуваних прапорців.

  • g - глобальний пошук (знайти всі збіги, а не лише перший)
  • i - пошук без урахування регістру
  • m - багаторядковий режим (. збігається також із символами нового рядка)
  • n - ігнорувати порожні збіги
  • p - увімкнено обидва режими, s і m
  • s - однорядковий режим ('^' -> '\A', '$' -> '\Z')
  • l - шукати найдовші можливі збіги
  • x - розширений формат регулярного виразу (ігнорувати пробільні символи та коментарі)

Наприклад

"JIRAISSUE-1234" | capture("(?<project>\\w+)-(?<issue_num>\\d+)")

# or with Extended formatting

"JIRAISSUE-1234" | capture("
                     (?<project>   \\w+ )  # the Jira project
                     -                     # followed by a hyphen
                     (?<issue_num> \\d+ )  # followed by digits
                   "; "x")
Редагувати через GitHub Посилання відкривається в новому вікні або вкладці

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

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

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