Регулярні вирази (regex) - це послідовності символів, які задають шаблон пошуку в тексті.
Вивчення синтаксису регулярних виразів виходить за межі цієї теми.
Ми зосередимося на виразах, які jq надає для роботи з регулярними виразами.
Різні інструменти реалізують різні версії регулярних виразів.
jq використовує бібліотеку регулярних виразів Oniguruma, яка здебільшого сумісна з регулярними виразами Perl v5.8.
Конкретний синтаксис, який використовує jq, можна знайти в репозиторії Oniguruma на GitHub.
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)
scan видає потік підрядків.
"Goodbye Mars" | scan("[aeiou]")
# => "o"
# "o"
# "e"
# "a"
Використовуємо конструктор масиву [...], щоб зібрати підрядки.
"Goodbye Mars" | [ scan("[aeiou]") ]
# => ["o", "o", "e", "a"]
Якщо відомо, які частини рядка тексту потрібно залишити, використовуємо match або scan.
Якщо відомо, які частини потрібно відкинути, використовуємо split.
STRING | split(REGEX; FLAGS)
Фільтр split з арністю 1 сприймає свій аргумент як фіксований рядок тексту.
Щоб використати регулярний вираз із split, потрібно надати другий аргумент; порожній рядок тексту цілком підходить.
Приклад, який розбиває рядок тексту за довільними пробільними символами.
"first second third fourth" | split("\\s+"; "")
# => ["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 і ms - Однорядковий режим ('^' -> '\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")
Нас найняли на посаду спеціаліста з регулярних виразів у компанію, яка розробляє Чатбота.
Він перебуває на дуже ранній стадії розробки. Наша місія - навчити Чатбота за допомогою регулярних виразів краще розуміти й генерувати природну мову.
Окрім того, що Чатбот розумний, він ще й відданий помічник. Щоб щось запитати в Чатбота, користувач має сказати слово «Chatbot» на першій позиції команди. Неважливо, чи записане ключове слово великими літерами, чи малими. Важлива саме позиція цього слова.
Реалізуйте функцію is_valid_command, яка допомагає Чатботу розпізнавати, коли користувач дає команду.
"Chatbot, play a song from the 80's." | is_valid_command
# => true
"Hey Chatbot, where is the closest pharmacy?" | is_valid_command
# => false
"CHATBOT, do you have a solution for this challenge?" | is_valid_command
# => true
Чатботу складно зрозуміти, як люди використовують емодзі, щоб виражати свої емоції. Коли Чатбот отримує повідомлення від користувача, кожне емодзі подане як рядок тексту (англ. string) "emoji", за яким іде номер id.
Реалізуйте метод remove_emoji, який приймає рядок тексту й видаляє всі емодзі з повідомлення.
Рядки, у яких немає емодзі, повертайте без змін. Просто видаліть рядок "emoji". Не змінюйте пробільні символи.
"I love playing videogames emoji3465 it's one of my hobbies" | remove_emoji
# => "I love playing videogames it's one of my hobbies"
У якийсь момент спілкування з Чатботом користувач надасть номер телефону. Чатбот може зателефонувати лише на номер певного формату.
Реалізуйте функцію check_phone_number.
Якщо номер коректний, Чатбот відповідає повідомленням, у якому дякує користувачеві й підтверджує номер. Якщо номер некоректний, Чатбот повідомляє користувачеві, що номер телефону неправильний.
Очікуваний формат - (+NN) NNN-NNN-NNN, де N позначає цифру.
"chatbot my phone number is (+34) 659-771-594" | check_phone_number
# => "Thanks! Your phone number is OK."
"chatbot, call me at 659-771-594" | check_phone_number
# => "Oops, it seems like I can't reach out to 659-771-594."
Чатбот вирізняється надзвичайною допитливістю. Хоча він уміє шукати в інтернеті інформацію на певну тему, він любить розпитувати користувачів про цікаві вебсайти, де можна знайти щось доречне.
Приклад розмови:
Чатбот: Вітаю! Я хотів би навчитися програмувати на JavaScript. Чи є цікавий вебсайт, де я міг би цього навчитися?
Користувач: Я багато чого навчився на exercism.org, там багато чудового матеріалу.
Реалізуйте функцію get_domains, яка повертає масив доменів вебсайтів.
"I learned a lot from exercism.org and google.com" | get_domains
# => ["exercism.org", "google.com"]
Ввічливий Чатбот звертається до користувачів на імʼя. Коли користувач представляється, наш Чатбот розпізнає його імʼя й відповідає привітанням.
Напишіть функцію nice_to_meet_you.
Якщо вхідний рядок тексту містить "My name is Someone.", захопіть імʼя та поверніть рядок "Nice to meet you, Someone.".
"My name is Jean-Luc" | nice_to_meet_you
# => "Nice to meet you, Jean-Luc"
Піддавшись «повзучому розростанню функцій», ми додамо до Чатбота функцію розбору CSV.
Реалізуйте функцію parse_csv, яка приймає рядок тексту й повертає масив отриманих полів.
Роздільником полів має бути «кома плюс необовʼязкові пробільні символи».
Не будемо перейматися крайніми випадками формату CSV (наприклад, полями, що містять коми).
"first, second,third, fourth" | parse_csv
# => ["first", "second", "third", "fourth"]
Зареєструйтеся на Exercism, щоб вивчати й опановувати jq, а також 12 концепцій75 вправ та справжнє наставництво від людей, і все це безкоштовно.