التعبيرات النمطية (regexes) هي تسلسلات من الأحرف تحدد نمط بحث في النص.
تعلُّم صياغة التعبيرات النمطية أمر يتجاوز نطاق هذا الموضوع.
سنركز على التعبيرات التي يوفرها jq لاستخدام التعبيرات النمطية.
تطبّق الأدوات المختلفة إصدارات مختلفة من التعبيرات النمطية.
يضم jq مكتبة التعبيرات النمطية Oniguruma، وهي متوافقة إلى حد كبير مع تعبيرات Perl v5.8 النمطية.
يمكن العثور على الصياغة المحددة التي يستخدمها jq في مستودع Oniguruma على GitHub.
لا يمتلك jq أي صياغة خاصة للتعبيرات النمطية.
فهي تُعبَّر عنها ببساطة كسلاسل نصية.
وهذا يعني أن أي شرطة مائلة عكسية في التعبير النمطي يجب تهريبها داخل السلسلة النصية.
على سبيل المثال، يجب كتابة صنف الأحرف الرقمية (\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.
لا يزال Chatbot في مرحلة تطوير أولية جدًا. مهمتك هي استخدام التعبيرات النمطية لتحسين قدرة Chatbot على فهم اللغة الطبيعية وتوليدها.
إلى جانب كونه ذكيًا، فإن Chatbot مساعد مخلص أيضًا. لكي يسأل المستخدم Chatbot عن شيء ما، يجب أن ينطق بكلمة "Chatbot" في الموضع الأول من الأمر. لا يهم إن كانت الكلمة المفتاحية بأحرف كبيرة أو صغيرة. المهم هو موضع الكلمة.
نفّذ الدالة is_valid_command التي تساعد Chatbot على التعرّف على ما إذا كان المستخدم يعطي أمرًا.
"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
يجد Chatbot صعوبة في فهم كيفية استخدام البشر للرموز التعبيرية للتعبير عن مشاعرهم. عندما يتلقى Chatbot رسائل المستخدم، يُمثَّل كل رمز تعبيري بالسلسلة النصية "emoji" متبوعة بـ_رقم معرّف_.
نفّذ الطريقة 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"
في مرحلة ما من التفاعل مع Chatbot، سيقدّم المستخدم رقم هاتف. لا يستطيع Chatbot الاتصال إلا برقم بصيغة محددة.
نفّذ الدالة check_phone_number.
إذا كان الرقم صحيحًا، يردّ Chatbot برسالة تشكر المستخدم وتؤكد الرقم. وإذا كان الرقم غير صحيح، يُخبر Chatbot المستخدم بأن رقم الهاتف غير صحيح.
الصيغة المتوقعة هي (+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."
Chatbot برنامج فضولي للغاية. فرغم أنه يستطيع البحث في الإنترنت عن موضوع معيّن، إلا أنه يحب أن يسأل المستخدمين عن مواقع رائعة لزيارتها والعثور على معلومات مفيدة.
مثال على محادثة:
Chatbot: مرحبًا أيها المستخدم، أود أن أتعلّم كيفية البرمجة بـ JavaScript. هل تعرف موقعًا رائعًا يمكنني التعلّم منه؟
User: تعلّمت الكثير من exercism.org، فهناك الكثير من الأشياء الرائعة.
نفّذ الدالة get_domains التي تُرجع مصفوفة من نطاقات المواقع.
"I learned a lot from exercism.org and google.com" | get_domains
# => ["exercism.org", "google.com"]
Chatbot المهذّب يخاطب المستخدمين بأسمائهم. عندما يعرّف المستخدم عن نفسه، يكتشف Chatbot اسمه ويردّ بتحية ودّية.
اكتب الدالة 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 إلى Chatbot.
نفّذ الدالة parse_csv التي تأخذ سلسلة نصية وتُرجع مصفوفة بالحقول الناتجة.
يجب أن يكون فاصل الحقول "فاصلة متبوعة بمسافات بيضاء اختيارية".
لن نقلق بشأن أي من الحالات الحدّية في صيغة CSV (مثل الحقول التي تحتوي على فواصل).
"first, second,third, fourth" | parse_csv
# => ["first", "second", "third", "fourth"]
سجّل في Exercism لتتعلّم وتتقن jq عبر 12 مفهومًا75 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.