المسارات
/
jq
jq
/
المنهج
/
التعبيرات النمطية
ال

التعبيرات النمطية في jq

1 تمرين

نبذة عن التعبيرات النمطية

التعبيرات النمطية (regexes) هي تسلسلات من المحارف تحدّد نمط بحث في نصّ ما.

تعلّم صيغة التعبيرات النمطية خارج نطاق هذا الموضوع. سنركّز على التعبيرات التي يوفّرها jq للاستفادة من التعبيرات النمطية.

صيغة التعبيرات النمطية

تنفّذ الأدوات المختلفة إصدارات مختلفة من التعبيرات النمطية. يضمّ jq مكتبة التعبيرات النمطية Oniguruma، وهي متوافقة إلى حد كبير مع تعبيرات Perl v5.8 النمطية.

يمكن العثور على الصيغة المحددة التي يستخدمها jq في مستودع Oniguruma على GitHub.

Caution

لا يمتلك 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)

# 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. ولاstبدال جميع المطابقات، استخدم 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 تمرين إضافي لممارسة التعبيرات النمطية