التعبيرات النمطية طريقة متعددة الاستخدامات لمطابقة أنماط السلاسل النصية، وذلك باستخدام لغة خاصة بالمجال (DSL) مصممة لهذا الغرض.
وكما في عدة لغات برمجة أخرى، لا تحاول Julia تنفيذ مكتبة تعبيرات نمطية خاصة بها، بل تغلّف مكتبة PCRE2 الشائعة، فتوفّر بذلك صيغة تعبيرات نمطية مطابقة لتلك المستخدمة في (مثلًا) Gleam، وقريبة جدًا من Javascript.
يفترض منهج Julia هذا أنك على دراية أساسية بصيغة التعبيرات النمطية. وسنركّز حصرًا على الميزات الخاصة بـ Julia.
وفيما يلي بعض المصادر لتحديث معرفتك بالتعبيرات النمطية.
وُصفت واجهة Julia للتعبيرات النمطية في الدليل.
التعبير النمطي في Julia ليس سوى سلسلة نصية تسبقها r قبل علامة " الافتتاحية.
وكل الوظائف الأساسية جزء من المكتبة القياسية.
في الواقع، كثير من الدوال التي سبق أن نوقشت في مفهوم Strings مصممة أصلًا للبحث بالتعبيرات النمطية، مثل occursin().
julia> re = r"test$"
r"test$"
julia> typeof(re)
Regex
# Does a string end with "test"?
julia> occursin(re, "this is a test")
true
julia> occursin(re, "these are tests")
false
يمكن أن تتبع محارف التعديل علامة الاقتباس الختامية، مثل i للمطابقة غير الحساسة لحالة الأحرف.
julia> occursin(r"test", "Testing")
false
julia> occursin(r"test"i, "Testing")
true
غالبًا ما نريد معرفة ما الذي يطابق. ويتحقق ذلك بتضمين مجموعات التقاط بين الأقواس الهلالية داخل التعبير النمطي، ثم استخدام الدالة match().
julia> m = match(r"(\d+g) .* (\d+ml)", "dissolve 25g sugar in 200ml water")
RegexMatch("25g sugar in 200ml", 1="25g", 2="200ml")
julia> m.captures
2-element Vector{Union{Nothing, SubString{String}}}:
"25g"
"200ml"
# how many matches?
julia> length(m.captures)
2
# what matched?
julia> m[1], m[2]
("25g", "200ml")
# Starting positions of the matches (character index)
julia> m.offsets
2-element Vector{Int64}:
10
23
وبالطبع قد تفشل المطابقة.
ستكون النتيجة عندئذٍ القيمة الخاصة Nothing بدلًا من RegexMatch، فكن مستعدًا لاختبار ذلك.
# failed match
m = match(r"(not here)", "dissolve 25g sugar in 200ml water")
julia> typeof(m)
Nothing
julia> isnothing(m)
true
وعلى الرغم من أن match تبدأ افتراضيًا من بداية السلسلة النصية، يمكننا أيضًا تحديد إزاحة n لتجاهل أول n من المحارف.
# capture first match
julia> m = match(r"(\wat)", "cat, sat, mat")
RegexMatch("cat", 1="cat")
# ignore first 5 characters, then match
julia> m = match(r"(\wat)", "cat, sat, mat", 5)
RegexMatch("sat", 1="sat")
في Julia، لن تجد match() إلا المطابقة الأولى داخل السلسلة النصية الهدف: فلا يوجد معدِّل شامل كما في بعض اللغات الأخرى.
بدلًا من ذلك، لدينا eachmatch()، وهي تُرجع مُكرِّرًا للمطابقات.
ويُقيَّم هذا بتكاسل، لذا قد تحتاج إلى تحويله إلى الصيغة التي تريدها.
julia> matches = eachmatch(r"(\wat)", "cat, sat, mat")
Base.RegexMatchIterator{String}(r"(\wat)", "cat, sat, mat", false)
# convert to vector
julia> collect(matches)
3-element Vector{RegexMatch}:
RegexMatch("cat", 1="cat")
RegexMatch("sat", 1="sat")
RegexMatch("mat", 1="mat")
# convert with comprehension
julia> [m.match for m in matches]
3-element Vector{SubString{String}}:
"cat"
"sat"
"mat"
# broadcast an anonymous function
julia> (m -> m.match).(matches)
3-element Vector{SubString{String}}:
"cat"
"sat"
"mat"
لا يُسمح بالمطابقات المتراكبة افتراضيًا.
أضف overlap = true كوسيط مفتاحي لتجاوز ذلك.
julia> eachmatch(r"aba", "abababa") |> collect # matches at positions 1, 5
2-element Vector{RegexMatch}:
RegexMatch("aba")
RegexMatch("aba")
julia> eachmatch(r"aba", "abababa"; overlap = true) |> collect # also matches at position 3
3-element Vector{RegexMatch}:
RegexMatch("aba")
RegexMatch("aba")
RegexMatch("aba")
من الأسباب الشائعة لاستخدام التعبير النمطي استبدال المطابقة بسلسلة نصية مختلفة.
نوقشت الدالة replace() في مفهوم Strings، حيث كانت تستخدم سلاسل نصية حرفية للبحث فيها.
ويمكن للدالة نفسها أن تستفيد من كامل قوة مطابقة التعبيرات النمطية.
julia> replace("some string", r"[aeiou]" => "*")
"s*m* str*ng"
julia> replace("first second", r"(\w+) (?<agroup>\w+)" => s"\g<agroup> \1")
"second first"
يوضح المثال الثاني أعلاه كيف يمكن استخدام مجموعات الالتقاط المرقّمة والمسمّاة معًا في الاستبدال، داخل سلسلة نصية s" ".
راجع الدليل لمزيد من التفاصيل: فهذا موضوع يعيد أغلب المبرمجين إلى التوثيق باستمرار!