String في Julia هو تسلسل مرتّب من المحارف. وكما يشير الدليل: "المشكلة الحقيقية تظهر بالطبع عندما يسأل المرء عمّا هو المحرف."
سيُغطّى الكثير من هذا التعقيد بمزيد من التفصيل في مفهوم Chars، لكن باختصار:
بشكل عام، تُحاط السلاسل النصية بعلامتَي التنصيص المزدوجتين " ".
أما علامتا التنصيص المفردتان ' ' فلا تُستخدمان إلا مع Chars.
يمكنك أيضًا استخدام ثلاث علامات تنصيص مزدوجة """ """، مما يتيح استخدام " داخل السلسلة النصية دون إفلات.
والأهم من ذلك، تُجرَّد السلاسل النصية متعددة الأسطر من المسافات البادئة الزائدة غير المرغوب فيها.
julia> """
Multiline
"""
"Multiline\nString\n"
مثل معظم اللغات بدءًا من لغة C، تستخدم Julia الشرطة المائلة الخلفية \ للإفلات:
\n في المثال السابق.\$ (انظر أدناه).من حيث المبدأ، يمكن فهرسة السلاسل النصية تمامًا كما تُفهرس المتجهات:
julia> s[1], s[5]
('J', 'a')
وهذا يعمل بوضوح مع سلسلة نصية بترميز ASCII مثل "Julia"، لكنه يتجاهل معظم لغات العالم.
تأمّل أحد محارف Beowulf، المكتوبة بالإنجليزية القديمة منذ نحو 15 قرنًا.
من الواضح أن "Hrōðgār" تتكون من 7 محارف، لكن ليست كلها من محارف ASCII.
julia> king = "Hrōðgār"
"Hrōðgār"
julia> length(king)
7
julia> isascii(king)
false
# collect() converts to a Char vector:
julia> collect(king)
7-element Vector{Char}:
'H': ASCII/Unicode U+0048 (category Lu: Letter, uppercase)
'r': ASCII/Unicode U+0072 (category Ll: Letter, lowercase)
'ō': Unicode U+014D (category Ll: Letter, lowercase)
'ð': Unicode U+00F0 (category Ll: Letter, lowercase)
'g': ASCII/Unicode U+0067 (category Ll: Letter, lowercase)
'ā': Unicode U+0101 (category Ll: Letter, lowercase)
'r': ASCII/Unicode U+0072 (category Ll: Letter, lowercase)
تبدأ الفهرسة في سلسلة نصية كهذه بشكل جيد، لكنها تنهار عند نقطة ما:
julia> king[3]
'ō': Unicode U+014D (category Ll: Letter, lowercase)
julia> king[4]
ERROR: StringIndexError: invalid index [4], valid nearby indices [3]=>'ō', [5]=>'ð'
المشكلة أن الفهرسة بهذه الطريقة تحسب البايتات، وهذه لا تربطها بالمحارف علاقة 1:1 في السلاسل النصية غير المُرمَّزة بـ ASCII.
قد يحتاج محرف Unicode/UTF-8 إلى ما يصل إلى 4 بايتات لتمثيله، وسيتناول مفهوم Chars هذا الأمر بمزيد من التفصيل.
تستخدم لغات كثيرة + كعامل لدمج السلاسل النصية، لكن ليس Julia.
على الأقل رسالة الخطأ مفيدة.
julia> "s1 " + "s2"
ERROR: MethodError: no method matching +(::String, ::String)
The function `+` exists, but no method is defined for this combination of argument types.
String concatenation is performed with *
julia> "s1 " * "s2"
"s1 s2"
تدمج الدالة string() أي عدد من السلاسل النصية.
julia> string("lots ", "of ", "bits")
"lots of bits"
وتفعل الدالة join() الشيء نفسه، كما أنها تأخذ متجهًا من السلاسل النصية وتدمجها بفاصل معيّن (قيمته الافتراضية "").
julia> join(["lots", "of", "bits"], "_")
"lots_of_bits"
لاحظ أن السلاسل النصية غير قابلة للتعديل، وأن الدمج يستلزم نسخًا. لذلك، فإن عمليات الدمج المتكررة داخل حلقة غير فعّالة، والأفضل جمع الأجزاء في متجه ثم دمجها كلها في النهاية.
أمر بسيط:
julia> repeat("Abc", 5)
"AbcAbcAbcAbcAbc"
للغات كثيرة أسلوب لإدراج قيم محسوبة في السلاسل النصية، وإن كان هناك خلاف كبير على أفضل صيغة لاستخدامها.
في Julia، تُسبَق القيم المُدرَجة بالعلامة $.
julia> myvar = 42
42
julia> "myvar evaluates to $myvar"
"myvar evaluates to 42"
julia> r = 5.3
5.3
julia> "A circle of radius $r has area $(π * r^2)"
"A circle of radius 5.3 has area 88.24733763933729"
تكون الأقواس الهلالية مطلوبة عندما يكون حذفها موهمًا، لكنها اختيارية مع معرّف واحد يتبعه فراغ.
ولإدراج علامة $ داخل السلسلة النصية، افلتها بالشكل \$.
لاحظ القيد القائل بأن الإدراج لا يسمح حاليًا بالتنسيق، كتحديد عدد المنازل العشرية المعروضة.
@sprintf()للحصول على تحكم أدق في تجميع السلاسل النصية، تنسخ Julia الدالة sprintf من لغة C (وعدة لغات لاحقة: تسرد ويكيبيديا نحو 30 لغة).
وفي Julia، هذا مُنفَّذ على هيئة macro داخل وحدة Printf، ومن هنا تأتي البادئة @.
julia> using Printf
julia> r = 5.3
5.3
julia> @sprintf("A circle of radius %.1f has area %.2f", r, π * r^2)
"A circle of radius 5.3 has area 88.25"
من باب التبسيط، سيركّز هذا القسم على الدوال التي تُرجع سلسلة نصية جديدة وتترك المدخل دون تغيير.
وللكثير منها نظير يحمل لاحقة !، ويعدّل المدخل في مكانه.
ما مدى طول السلسلة النصية؟ أحيانًا يعتمد الأمر على كيفية العدّ.
الحالة البسيطة هي السلاسل النصية بترميز ASCII، حيث يشغل كل محرف بايتًا واحدًا.
julia> string_asc = "Julia"
"Julia"
julia> length(string_asc)
5
julia> sizeof(string_asc)
5
تُرجع الدالة length() عدد المحارف، وتُرجع الدالة sizeof() عدد البايتات.
ويصبح هذا التمييز مهمًا مع مجموعات المحارف الأخرى:
julia> king = "Hrōðgār"
"Hrōðgār"
julia> length(king)
7
julia> sizeof(king)
10
من العمليات الشائعة في البرمجة أخذ سلسلة نصية طويلة وتقسيمها إلى أجزاء، اعتمادًا على سلسلة نصية فاصلة من محرف واحد أو أكثر.
وعلى الأعم الأغلب، نستخدم الدالة split().
الفاصل الافتراضي هو أي مسافة بيضاء، لكن يمكن تحديد فواصل أخرى.
julia> split("my little string")
3-element Vector{SubString{String}}:
"my"
"little"
"string"
julia> split("My_snake_case_string", "_")
4-element Vector{SubString{String}}:
"My"
"snake"
"case"
"string"
تقسم هذه الأمثلة سلسلة نصية إلى سلاسل نصية أصغر. أما تقسيم سلسلة نصية إلى محارف فهو عملية مختلفة.
هل تحتوي سلسلة نصية على سلسلة نصية فرعية محددة؟
ومن الغريب أن هناك دالتين لاختبار ذلك: occursin() وcontains()، ولا تختلفان إلا في ترتيب وسائطهما.
julia> needle = "Julia"
"Julia"
julia> haystack = "Python, Julia and R"
"Python, Julia and R"
julia> occursin(needle, haystack)
true
julia> contains(haystack, needle)
true
ولمزيد من التحديد للموضع:
julia> startswith(haystack, "Python")
true
julia> endswith(haystack, "Python")
false
وللحصول على فهارس البداية:النهاية لسلسلة نصية فرعية، لدينا عدة دوال:
findfirst()findlast()findprev()findnext()julia> findfirst(needle, haystack)
9:13
ولاستبدال السلاسل النصية الفرعية، اسرد التغييرات باستخدام صيغة x => y.
julia> replace("abc", "a"=>"b", "b"=>"c", "c"=>"a")
"bca"
منذ الإصدار Julia 1.7، يمكن للدالة replace أن تأخذ أي عدد من التغييرات، مع ضمان ألا يتغير أي محرف أكثر من مرة واحدة أثناء الاستبدال.
تُسمّى صيغة x => y بـ pair، وهو نوع مهم في Julia يُغطّى بمزيد من التفصيل في مفهوم Dicts and Pairs.
نحتاج كثيرًا إلى إزالة المسافات البيضاء من بداية سلسلة نصية أو نهايتها.
الدوال المخصصة لذلك هي lstrip() (من اليسار)، وrstrip() (من اليمين)، وstrip() (من كليهما).
julia> strip("\n\t useful_bit\n\n")
"useful_bit"
لهذه الدوال أسماء واضحة إلى حد كبير:
julia> s = repeat("aBcD ", 5)
"aBcD aBcD aBcD aBcD aBcD "
julia> uppercase(s)
"ABCD ABCD ABCD ABCD ABCD "
julia> lowercase(s)
"abcd abcd abcd abcd abcd "
julia> titlecase(s) # Initial letter of each word uppercase
"Abcd Abcd Abcd Abcd Abcd "
في هذا التمرين ستتعامل مع أسطر السجل.
كل سطر سجل هو سلسلة نصية بتنسيق كالتالي: "[<LEVEL>]: <MESSAGE>".
هناك ثلاثة مستويات مختلفة للسجل:
INFOWARNINGERRORلديك ثلاث مهام، ستأخذ كل مهمة سطر سجل وتطلب منك أن تفعل به شيئًا.
نفّذ الدالة message لتُرجع رسالة سطر السجل:
julia> message("[ERROR]: Invalid operation")
"Invalid operation"
يجب إزالة أي مسافات بيضاء في البداية أو النهاية:
julia> message("[WARNING]: Disk almost full\r\n")
"Disk almost full"
نفّذ الدالة log_level لتُرجع مستوى السجل لسطر السجل، بأحرف صغيرة:
julia> log_level("[ERROR]: Invalid operation")
"error"
نفّذ الدالة reformat التي تعيد تنسيق سطر السجل، بحيث تأتي الرسالة أولًا ويأتي مستوى السجل بعدها بين الأقواس الهلالَين:
julia> reformat("[INFO]: Operation completed")
"Operation completed (info)"
ملاحظة: جميع السلاسل النصية في هذا التمرين بالإنجليزية ومحصورة في مجموعة أحرف ASCII. ستمهّد المفاهيم اللاحقة فرصة للعمل مع أحرف يونيكود.
سجّل في Exercism لتتعلّم وتتقن Julia عبر 35 مفهومًا128 تمرينًا، وإرشاد بشري حقيقي، وكل ذلك مجانًا.