عبارات باقاعده (رجکسها) دنبالههایی از نویسهها هستند که یک الگوی جستوجو در متن را مشخص میکنند.
یادگیری نحوهی نگارش عبارات باقاعده فراتر از محدودهی این مبحث است.
ما روی عباراتی تمرکز میکنیم که jq برای استفاده از عبارات باقاعده فراهم میکند.
ابزارهای مختلف نسخههای متفاوتی از عبارات باقاعده را پیادهسازی میکنند.
jq کتابخانهی عبارات باقاعدهی Oniguruma را به کار میگیرد که تا حد زیادی با عبارات باقاعدهی Perl v5.8 سازگار است.
نحوهی نگارش خاصی که jq استفاده میکند را میتوان در مخزن GitHub Oniguruma یافت.
jq هیچ نحوهی نگارش خاصی برای عبارات باقاعده ندارد.
آنها بهسادگی بهصورت رشته بیان میشوند.
این یعنی هر بکاسلش در عبارت باقاعده باید در رشته escape شود.
برای مثال، کلاس نویسهی رقم (\d) باید بهصورت "\\d" نوشته شود.
عبارات باقاعده در jq محدود به مجموعهای از فیلترها هستند.
وقتی میخواهید بدانید آیا رشتهای با الگویی تطبیق پیدا میکند، از فیلتر test استفاده کنید.
STRING | test(REGEX)
STRING | test(REGEX; FLAGS)
STRING | test([REGEX, FLAGS])
این فیلتر یک نتیجهی منطقی تولید میکند.
"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 با یک آرگومان آرگومانش را بهعنوان یک رشتهی ثابت در نظر میگیرد.
برای استفاده از یک عبارت باقاعده با 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 و 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")
شما بهعنوان متخصص عبارتهای باقاعده در شرکتی استخدام شدهاید که در حال توسعهی یک چتبات است.
این شرکت در مرحلهی بسیار ابتدایی توسعه قرار دارد. مأموریت شما این است که با استفاده از عبارتهای باقاعده، توانایی چتبات را در درک و تولید زبان طبیعی بهبود دهید.
چتبات علاوه بر باهوش بودن، دستیاری وفادار هم هست. برای پرسیدن چیزی از چتبات، کاربر باید واژهی «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
چتبات بهسختی میتواند درک کند که انسانها چگونه از ایموجیها برای بیان احساسات خود استفاده میکنند. وقتی چتبات پیامهای کاربر را دریافت میکند، هر ایموجی بهصورت رشتهی «emoji» بههمراه یک شمارهی شناسه نمایش داده میشود.
متد remove_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"]