Strings

السلاسل هي تسلسلات نهائية من الأحرف. بالطبع، تأتي المشكلة الحقيقية عندما يسأل المرء ما هو الحرف. الأحرف التي يعرفها المتحدثون باللغة الإنجليزية هي الحروف A، B، C، إلخ، جنبًا إلى جنب مع الأرقام ورموز الترقيم الشائعة. تم توحيد هذه الأحرف معًا مع خريطة إلى قيم صحيحة بين 0 و 127 بواسطة معيار ASCII. هناك، بالطبع، العديد من الأحرف الأخرى المستخدمة في اللغات غير الإنجليزية، بما في ذلك المتغيرات من أحرف ASCII مع الحركات والتعديلات الأخرى، والكتابات ذات الصلة مثل السيريليكية واليونانية، والكتابات غير المرتبطة بـ ASCII والإنجليزية، بما في ذلك العربية والصينية والعبرية والهندية واليابانية والكورية. يتناول معيار Unicode تعقيدات ما هو بالضبط الحرف، ويعتبر عمومًا المعيار النهائي الذي يتناول هذه المشكلة. اعتمادًا على احتياجاتك، يمكنك إما تجاهل هذه التعقيدات تمامًا والتظاهر بأن أحرف ASCII فقط موجودة، أو يمكنك كتابة كود يمكنه التعامل مع أي من الأحرف أو الترميزات التي قد تواجهها عند التعامل مع نص غير ASCII. تجعل جوليا التعامل مع نصوص ASCII العادية بسيطًا وفعالًا، والتعامل مع Unicode بسيط وفعال قدر الإمكان. على وجه الخصوص، يمكنك كتابة كود سلسلة على طراز C لمعالجة سلاسل ASCII، وستعمل كما هو متوقع، من حيث الأداء والدلالات. إذا واجه مثل هذا الكود نصًا غير ASCII، فسيفشل بشكل أنيق مع رسالة خطأ واضحة، بدلاً من تقديم نتائج فاسدة بصمت. عندما يحدث ذلك، فإن تعديل الكود للتعامل مع بيانات غير ASCII يكون مباشرًا.

هناك بعض الميزات البارزة على مستوى عالٍ حول سلاسل جوليّا:

  • نوع الخرسانة المدمجة المستخدم للسلاسل (والأدلة النصية) في جوليا هو String. هذا يدعم النطاق الكامل من Unicode الأحرف عبر ترميز UTF-8. (تم توفير دالة transcode للتحويل من/إلى ترميزات يونيكود الأخرى.)
  • جميع أنواع السلاسل هي أنواع فرعية من النوع المجرد AbstractString، وتقوم الحزم الخارجية بتعريف أنواع فرعية إضافية من AbstractString (على سبيل المثال، لرموز ترميز أخرى). إذا كنت تعرف دالة تتوقع وسيط سلسلة، يجب عليك إعلان النوع كـ AbstractString من أجل قبول أي نوع سلسلة.
  • مثل C و Java، ولكن على عكس معظم اللغات الديناميكية، تحتوي جوليا على نوع من الدرجة الأولى لتمثيل حرف واحد، يسمى AbstractChar. النوع الفرعي المدمج Char من AbstractChar هو نوع بدائي 32 بت يمكنه تمثيل أي حرف يونيكود (والذي يعتمد على ترميز UTF-8).
  • كما هو الحال في Java، فإن السلاسل غير قابلة للتغيير: لا يمكن تغيير قيمة كائن AbstractString. لإنشاء قيمة سلسلة مختلفة، تقوم بإنشاء سلسلة جديدة من أجزاء من سلاسل أخرى.
  • مفهومياً، السلسلة هي دالة جزئية من الفهارس إلى الأحرف: لبعض قيم الفهرس، لا يتم إرجاع قيمة حرف، وبدلاً من ذلك يتم طرح استثناء. هذا يسمح بالوصول الفعال إلى السلاسل من خلال فهرس البايت لتمثيل مشفر بدلاً من فهرس الحرف، والذي لا يمكن تنفيذه بكفاءة وببساطة لكل من الترميزات ذات العرض المتغير لسلاسل اليونيكود.

Characters

قيمة Char تمثل حرفًا واحدًا: إنها مجرد نوع بدائي بحجم 32 بت مع تمثيل حرفي خاص وسلوكيات حسابية مناسبة، ويمكن تحويلها إلى قيمة عددية تمثل Unicode code point. (قد تعرف حزم جوليا أنواعًا فرعية أخرى من AbstractChar، على سبيل المثال لتحسين العمليات لأنواع أخرى من text encodings.) إليك كيفية إدخال قيم Char وعرضها (لاحظ أن الأدبيات الحرفية محاطة بعلامات اقتباس مفردة، وليس مزدوجة):

julia> c = 'x'
'x': ASCII/Unicode U+0078 (category Ll: Letter, lowercase)

julia> typeof(c)
Char

يمكنك بسهولة تحويل Char إلى قيمته الصحيحة، أي نقطة الشيفرة:

julia> c = Int('x')
120

julia> typeof(c)
Int64

على المعماريات ذات 32 بت، typeof(c) سيكون Int32. يمكنك تحويل قيمة صحيحة مرة أخرى إلى Char بنفس السهولة:

julia> Char(120)
'x': ASCII/Unicode U+0078 (category Ll: Letter, lowercase)

ليست جميع القيم الصحيحة نقاط رموز يونيكود صالحة، ولكن لأغراض الأداء، فإن تحويل Char لا يتحقق من أن كل قيمة حرف صالحة. إذا كنت تريد التحقق من أن كل قيمة تم تحويلها هي نقطة رمز صالحة، استخدم الدالة isvalid:

julia> Char(0x110000)
'\U110000': Unicode U+110000 (category In: Invalid, too high)

julia> isvalid(Char, 0x110000)
false

اعتبارًا من كتابة هذا النص، فإن نقاط الشيفرة Unicode الصالحة هي U+0000 إلى U+D7FF و U+E000 إلى U+10FFFF. لم يتم تعيين معاني مفهومة لجميعها بعد، ولا يمكن بالضرورة تفسيرها بواسطة التطبيقات، ولكن تعتبر جميع هذه القيم شخصيات Unicode صالحة.

يمكنك إدخال أي حرف Unicode في علامات اقتباس مفردة باستخدام \u متبوعًا بما يصل إلى أربعة أرقام سداسية عشرية أو \U متبوعًا بما يصل إلى ثمانية أرقام سداسية عشرية (أطول قيمة صالحة تتطلب فقط ستة):

julia> '\u0'
'\0': ASCII/Unicode U+0000 (category Cc: Other, control)

julia> '\u78'
'x': ASCII/Unicode U+0078 (category Ll: Letter, lowercase)

julia> '\u2200'
'∀': Unicode U+2200 (category Sm: Symbol, math)

julia> '\U10ffff'
'\U10ffff': Unicode U+10FFFF (category Cn: Other, not assigned)

تستخدم جوليا إعدادات اللغة والمنطقة في نظامك لتحديد أي الأحرف يمكن طباعتها كما هي وأيها يجب أن يتم إخراجه باستخدام أشكال الإدخال العامة، الهاربة \u أو \U. بالإضافة إلى هذه الأشكال الهاربة من يونيكود، يمكن أيضًا استخدام جميع C's traditional escaped input forms:

julia> Int('\0')
0

julia> Int('\t')
9

julia> Int('\n')
10

julia> Int('\e')
27

julia> Int('\x7f')
127

julia> Int('\177')
127

يمكنك إجراء مقارنات وكمية محدودة من العمليات الحسابية باستخدام قيم Char:

julia> 'A' < 'a'
true

julia> 'A' <= 'a' <= 'Z'
false

julia> 'A' <= 'X' <= 'Z'
true

julia> 'x' - 'a'
23

julia> 'A' + 1
'B': ASCII/Unicode U+0042 (category Lu: Letter, uppercase)

String Basics

تُحدد السلاسل النصية بواسطة علامات الاقتباس المزدوجة أو علامات الاقتباس الثلاثية المزدوجة (وليس علامات الاقتباس المفردة):

julia> str = "Hello, world.\n"
"Hello, world.\n"

julia> """Contains "quote" characters"""
"Contains \"quote\" characters"

يمكن كسر الأسطر الطويلة في السلاسل عن طريق وضع شرطة مائلة عكسية (\) قبل السطر الجديد:

julia> "This is a long \
       line"
"This is a long line"

إذا كنت تريد استخراج حرف من سلسلة، يمكنك الوصول إليه عن طريق الفهرسة:

julia> str[begin]
'H': ASCII/Unicode U+0048 (category Lu: Letter, uppercase)

julia> str[1]
'H': ASCII/Unicode U+0048 (category Lu: Letter, uppercase)

julia> str[6]
',': ASCII/Unicode U+002C (category Po: Punctuation, other)

julia> str[end]
'\n': ASCII/Unicode U+000A (category Cc: Other, control)

يمكن فهرسة العديد من كائنات جوليا، بما في ذلك السلاسل النصية، باستخدام الأعداد الصحيحة. يتم إرجاع فهرس العنصر الأول (أول حرف في سلسلة نصية) بواسطة firstindex(str)، وفهرس العنصر الأخير (الحرف) بواسطة lastindex(str). يمكن استخدام الكلمات الرئيسية begin و end داخل عملية الفهرسة كاختصار للفهارس الأولى والأخيرة، على التوالي، على طول البعد المعطى. فهرسة السلاسل النصية، مثل معظم الفهرسة في جوليا، تعتمد على الأساس 1: firstindex دائمًا ما ترجع 1 لأي AbstractString. كما سنرى أدناه، ومع ذلك، فإن lastindex(str) ليس بشكل عام هو نفسه length(str) لسلسلة نصية، لأن بعض أحرف يونيكود يمكن أن تشغل عدة "وحدات كود".

يمكنك إجراء العمليات الحسابية وغيرها من العمليات باستخدام end، تمامًا مثل القيمة العادية:

julia> str[end-1]
'.': ASCII/Unicode U+002E (category Po: Punctuation, other)

julia> str[end÷2]
' ': ASCII/Unicode U+0020 (category Zs: Separator, space)

استخدام فهرس أقل من begin (1) أو أكبر من end يثير خطأ:

julia> str[begin-1]
ERROR: BoundsError: attempt to access 14-codeunit String at index [0]
[...]

julia> str[end+1]
ERROR: BoundsError: attempt to access 14-codeunit String at index [15]
[...]

يمكنك أيضًا استخراج جزء من السلسلة باستخدام فهرسة النطاق:

julia> str[4:9]
"lo, wo"

لاحظ أن التعبيرات str[k] و str[k:k] لا تعطي نفس النتيجة:

julia> str[6]
',': ASCII/Unicode U+002C (category Po: Punctuation, other)

julia> str[6:6]
","

الأول هو قيمة حرفية مفردة من نوع Char، بينما الثاني هو قيمة سلسلة تحدث أن تحتوي على حرف مفرد فقط. في جوليا، هذه أشياء مختلفة جدًا.

تقوم فهرسة النطاق بإنشاء نسخة من الجزء المحدد من السلسلة الأصلية. بدلاً من ذلك، من الممكن إنشاء عرض في سلسلة باستخدام النوع SubString. بشكل أبسط، باستخدام الماكرو @views على كتلة من الشيفرة، يتم تحويل جميع شرائح السلاسل إلى سلاسل فرعية. على سبيل المثال:

julia> str = "long string"
"long string"

julia> substr = SubString(str, 1, 4)
"long"

julia> typeof(substr)
SubString{String}

julia> @views typeof(str[1:4]) # @views converts slices to SubStrings
SubString{String}

عدة دوال قياسية مثل chop، chomp أو strip تعيد SubString.

Unicode and UTF-8

تدعم جوليا بالكامل أحرف Unicode والسلاسل. كـ discussed above، في الحروف الأدبية، يمكن تمثيل نقاط كود Unicode باستخدام تسلسلات الهروب Unicode \u و \U، بالإضافة إلى جميع تسلسلات الهروب القياسية في C. يمكن أيضًا استخدام هذه لكتابة الحروف الأدبية:

julia> s = "\u2200 x \u2203 y"
"∀ x ∃ y"

سواء تم عرض هذه الأحرف Unicode كإشارات هروب أو تم عرضها كأحرف خاصة يعتمد على إعدادات اللغة في الطرفية الخاصة بك ودعمها لـ Unicode. يتم ترميز السلاسل النصية باستخدام ترميز UTF-8. UTF-8 هو ترميز بعرض متغير، مما يعني أن ليس كل الأحرف يتم ترميزها بنفس عدد البايتات ("وحدات الشيفرة"). في UTF-8، يتم ترميز أحرف ASCII - أي تلك التي تحتوي على نقاط شيفرة أقل من 0x80 (128) - كما هي في ASCII، باستخدام بايت واحد، بينما يتم ترميز نقاط الشيفرة 0x80 وما فوق باستخدام عدة بايتات - حتى أربعة لكل حرف.

تشير مؤشرات السلسلة في جوليا إلى وحدات الشيفرة (= بايتات لـ UTF-8)، وهي الكتل الثابتة العرض التي تُستخدم لترميز الأحرف التعسفية (نقاط الشيفرة). هذا يعني أن ليس كل مؤشر في String هو بالضرورة مؤشرًا صالحًا لحرف. إذا قمت بالوصول إلى سلسلة عند مثل هذا المؤشر البايت غير الصالح، يتم طرح خطأ:

julia> s[1]
'∀': Unicode U+2200 (category Sm: Symbol, math)

julia> s[2]
ERROR: StringIndexError: invalid index [2], valid nearby indices [1]=>'∀', [4]=>' '
Stacktrace:
[...]

julia> s[3]
ERROR: StringIndexError: invalid index [3], valid nearby indices [1]=>'∀', [4]=>' '
Stacktrace:
[...]

julia> s[4]
' ': ASCII/Unicode U+0020 (category Zs: Separator, space)

في هذه الحالة، الشخصية هي شخصية مكونة من ثلاثة بايت، لذا فإن الفهارس 2 و3 غير صالحة والفهرس التالي للشخصية هو 4؛ يمكن حساب هذا الفهرس الصالح التالي بواسطة nextind(s,1)، والفهرس التالي بعد ذلك بواسطة nextind(s,4) وهكذا.

نظرًا لأن end هو دائمًا الفهرس الأخير الصالح في مجموعة، فإن end-1 يشير إلى فهرس بايت غير صالح إذا كان الحرف قبل الأخير متعدد البايت.

julia> s[end-1]
' ': ASCII/Unicode U+0020 (category Zs: Separator, space)

julia> s[end-2]
ERROR: StringIndexError: invalid index [9], valid nearby indices [7]=>'∃', [10]=>' '
Stacktrace:
[...]

julia> s[prevind(s, end, 2)]
'∃': Unicode U+2203 (category Sm: Symbol, math)

تعمل الحالة الأولى، لأن الحرف الأخير y والمسافة هما حرفان بحجم بايت واحد، بينما end-2 يشير إلى منتصف تمثيل متعدد البايت. الطريقة الصحيحة لهذه الحالة هي استخدام prevind(s, lastindex(s), 2) أو، إذا كنت تستخدم تلك القيمة للإشارة إلى s يمكنك كتابة s[prevind(s, end, 2)] وend يتوسع إلى lastindex(s).

استخراج جزء من سلسلة باستخدام فهرسة النطاق يتطلب أيضًا مؤشرات بايت صحيحة أو سيتم إلقاء خطأ:

julia> s[1:1]
"∀"

julia> s[1:2]
ERROR: StringIndexError: invalid index [2], valid nearby indices [1]=>'∀', [4]=>' '
Stacktrace:
[...]

julia> s[1:4]
"∀ "

بسبب الترميزات ذات الطول المتغير، فإن عدد الأحرف في سلسلة (المعطاة بواسطة length(s)) ليس دائمًا هو نفسه آخر فهرس. إذا قمت بالتكرار عبر الفهارس من 1 إلى lastindex(s) وفهرست في s، فإن تسلسل الأحرف المعادة عندما لا يتم طرح أخطاء هو تسلسل الأحرف التي تتكون منها السلسلة s. وبالتالي length(s) <= lastindex(s)، حيث يجب أن يكون لكل حرف في سلسلة فهرسه الخاص. الطريقة التالية هي طريقة غير فعالة وطويلة للتكرار عبر أحرف s:

julia> for i = firstindex(s):lastindex(s)
           try
               println(s[i])
           catch
               # ignore the index error
           end
       end
∀

x

∃

y

تحتوي الأسطر الفارغة في الواقع على مسافات. لحسن الحظ، فإن التعبير الغريب أعلاه غير ضروري للتكرار عبر الأحرف في سلسلة، حيث يمكنك ببساطة استخدام السلسلة ككائن قابل للتكرار، دون الحاجة إلى معالجة الاستثناءات:

julia> for c in s
           println(c)
       end
∀

x

∃

y

إذا كنت بحاجة إلى الحصول على مؤشرات صالحة لسلسلة نصية، يمكنك استخدام الدوال nextind و prevind للزيادة/النقصان إلى المؤشر الصالح التالي/السابق، كما هو مذكور أعلاه. يمكنك أيضًا استخدام الدالة eachindex للتكرار على مؤشرات الأحرف الصالحة:

julia> collect(eachindex(s))
7-element Vector{Int64}:
  1
  4
  5
  6
  7
 10
 11

للوصول إلى وحدات الشيفرة الخام (بايتات لـ UTF-8) من الترميز، يمكنك استخدام دالة codeunit(s,i)، حيث يتراوح الفهرس i بشكل متتابع من 1 إلى ncodeunits(s). تقوم دالة codeunits(s) بإرجاع غلاف AbstractVector{UInt8} الذي يتيح لك الوصول إلى هذه الوحدات الخام (بايتات) كمصفوفة.

يمكن أن تحتوي السلاسل في جوليا على تسلسلات وحدات UTF-8 غير صالحة. تسمح هذه القاعدة بمعالجة أي تسلسل بايت كسلسلة String. في مثل هذه الحالات، القاعدة هي أنه عند تحليل تسلسل من وحدات الشيفرة من اليسار إلى اليمين، يتم تشكيل الأحرف بواسطة أطول تسلسل من وحدات الشيفرة 8 بت التي تتطابق مع بداية أحد أنماط البت التالية (يمكن أن يكون كل x إما 0 أو 1):

  • 0xxxxxxx;
  • 110xxxxx 10xxxxxx;
  • 1110xxxx 10xxxxxx 10xxxxxx;
  • 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx;
  • 10xxxxxx;
  • 11111xxx.

بشكل خاص، يعني هذا أن تسلسلات وحدات الشيفرة الطويلة جدًا والمرتفعة جدًا والبادئات الخاصة بها تُعتبر حرفًا غير صالح واحدًا بدلاً من عدة أحرف غير صالحة. قد يكون من الأفضل توضيح هذه القاعدة بمثال:

julia> s = "\xc0\xa0\xe2\x88\xe2|"
"\xc0\xa0\xe2\x88\xe2|"

julia> foreach(display, s)
'\xc0\xa0': [overlong] ASCII/Unicode U+0020 (category Zs: Separator, space)
'\xe2\x88': Malformed UTF-8 (category Ma: Malformed, bad data)
'\xe2': Malformed UTF-8 (category Ma: Malformed, bad data)
'|': ASCII/Unicode U+007C (category Sm: Symbol, math)

julia> isvalid.(collect(s))
4-element BitArray{1}:
 0
 0
 0
 1

julia> s2 = "\xf7\xbf\xbf\xbf"
"\U1fffff"

julia> foreach(display, s2)
'\U1fffff': Unicode U+1FFFFF (category In: Invalid, too high)

يمكننا أن نرى أن وحدتي الشيفرة الأوليين في السلسلة s تشكلان ترميزًا طويلًا زائدًا لرمز المسافة. إنه غير صالح، ولكن يتم قبوله في سلسلة كحرف واحد. تشكل وحدتا الشيفرة التاليتان بداية صالحة لتسلسل UTF-8 مكون من ثلاثة بايت. ومع ذلك، فإن وحدة الشيفرة الخامسة \xe2 ليست استمرارًا صالحًا لها. لذلك، يتم تفسير وحدات الشيفرة 3 و 4 أيضًا على أنها أحرف غير صحيحة في هذه السلسلة. وبالمثل، تشكل وحدة الشيفرة 5 حرفًا غير صحيح لأن | ليس استمرارًا صالحًا لها. أخيرًا، تحتوي السلسلة s2 على نقطة شيفرة مرتفعة جدًا.

تستخدم جوليا ترميز UTF-8 بشكل افتراضي، ويمكن إضافة دعم لترميزات جديدة من خلال الحزم. على سبيل المثال، تقوم حزمة LegacyStrings.jl بتنفيذ أنواع UTF16String و UTF32String. النقاش الإضافي حول ترميزات أخرى وكيفية تنفيذ الدعم لها يتجاوز نطاق هذه الوثيقة في الوقت الحالي. لمزيد من المناقشة حول قضايا ترميز UTF-8، انظر القسم أدناه حول byte array literals. يتم توفير دالة transcode لتحويل البيانات بين ترميزات UTF-xx المختلفة، بشكل أساسي للعمل مع البيانات والمكتبات الخارجية.

Concatenation

واحدة من أكثر العمليات الشائعة والمفيدة على السلاسل هي الدمج:

julia> greet = "Hello"
"Hello"

julia> whom = "world"
"world"

julia> string(greet, ", ", whom, ".\n")
"Hello, world.\n"

من المهم أن تكون على دراية بالمواقف الخطيرة المحتملة مثل دمج سلاسل UTF-8 غير الصالحة. قد تحتوي السلسلة الناتجة على أحرف مختلفة عن سلاسل الإدخال، وقد يكون عدد الأحرف فيها أقل من مجموع أعداد الأحرف في السلاسل المدمجة، على سبيل المثال:

julia> a, b = "\xe2\x88", "\x80"
("\xe2\x88", "\x80")

julia> c = string(a, b)
"∀"

julia> collect.([a, b, c])
3-element Vector{Vector{Char}}:
 ['\xe2\x88']
 ['\x80']
 ['∀']

julia> length.([a, b, c])
3-element Vector{Int64}:
 1
 1
 1

يمكن أن تحدث هذه الحالة فقط لسلاسل UTF-8 غير صالحة. بالنسبة لسلاسل UTF-8 الصالحة، فإن الدمج يحافظ على جميع الأحرف في السلاسل ويضمن إضافة أطوال السلاسل.

تقدم جوليا أيضًا * لدمج السلاسل النصية:

julia> greet * ", " * whom * ".\n"
"Hello, world.\n"

بينما قد يبدو أن * خيارًا مفاجئًا لمستخدمي اللغات التي توفر + لدمج السلاسل، فإن استخدام * له سابقة في الرياضيات، لا سيما في الجبر المجرد.

في الرياضيات، + عادة ما يدل على عملية تبادلية، حيث لا تهم ترتيب المعاملات. مثال على ذلك هو جمع المصفوفات، حيث A + B == B + A لأي مصفوفتين A و B لهما نفس الشكل. في المقابل، * عادة ما يدل على عملية غير تبادلية، حيث يهم ترتيب المعاملات بالفعل. مثال على ذلك هو ضرب المصفوفات، حيث بشكل عام A * B != B * A. كما هو الحال مع ضرب المصفوفات، فإن دمج السلاسل غير تبادلي: greet * whom != whom * greet. وبالتالي، فإن * هو خيار أكثر طبيعية لعملية دمج السلاسل في الصيغة، متسق مع الاستخدام الرياضي الشائع.

بشكل أكثر دقة، فإن مجموعة جميع السلاسل ذات الطول المحدود S مع عامل الربط بين السلاسل * تشكل free monoid (S, *). العنصر المحايد لهذه المجموعة هو السلسلة الفارغة، "". كلما كانت المونيد الحرة غير تبادلية، يتم تمثيل العملية عادةً كـ \cdot، *، أو رمز مشابه، بدلاً من +، الذي كما ذُكر عادةً ما يعني التبادلية.

Interpolation

يمكن أن يصبح بناء السلاسل باستخدام الربط أمرًا مرهقًا بعض الشيء، ومع ذلك. لتقليل الحاجة إلى هذه المكالمات المطولة إلى string أو الضرب المتكرر، تسمح جوليا بالتداخل في النصوص الحرفية باستخدام $، كما في بيرل:

julia> greet = "Hello"; whom = "world";

julia> "$greet, $whom.\n"
"Hello, world.\n"

هذا أكثر قابلية للقراءة وملاءمة ومعادل لعملية ربط السلاسل المذكورة أعلاه - يقوم النظام بإعادة كتابة هذا النص الظاهر كسلسلة نصية واحدة إلى الاستدعاء string(greet, ", ", whom, ".\n").

أقصر تعبير كامل بعد $ يُعتبر التعبير الذي سيتم استبدال قيمته في السلسلة. وبالتالي، يمكنك استبدال أي تعبير في سلسلة باستخدام الأقواس:

julia> "1 + 2 = $(1 + 2)"
"1 + 2 = 3"

كلا من الربط والتداخل النصي يستدعي string لتحويل الكائنات إلى شكل نصي. ومع ذلك، فإن string في الواقع تعيد فقط ناتج print، لذا يجب على الأنواع الجديدة إضافة طرق إلى 4d61726b646f776e2e436f64652822222c20227072696e742229_40726566 أو show بدلاً من string.

تتحول معظم الكائنات غير AbstractString إلى سلاسل نصية تتوافق بشكل وثيق مع كيفية إدخالها كتعابير حرفية:

julia> v = [1,2,3]
3-element Vector{Int64}:
 1
 2
 3

julia> "v: $v"
"v: [1, 2, 3]"

string هو الهوية لقيم AbstractString و AbstractChar، لذا يتم تضمينها في السلاسل كما هي، بدون اقتباس أو هروب:

julia> c = 'x'
'x': ASCII/Unicode U+0078 (category Ll: Letter, lowercase)

julia> "hi, $c"
"hi, x"

لإدراج $ حرفيًا في سلسلة نصية، قم بالهروب منه باستخدام شرطة مائلة للخلف:

julia> print("I have \$100 in my account.\n")
I have $100 in my account.

Triple-Quoted String Literals

عندما يتم إنشاء السلاسل النصية باستخدام علامات الاقتباس الثلاثية ("""...""") فإن لها سلوكًا خاصًا يمكن أن يكون مفيدًا لإنشاء كتل نصية أطول.

أولاً، يتم تقليل السلاسل النصية المقتبسة ثلاث مرات إلى مستوى أقل سطر متداخل. هذا مفيد لتعريف السلاسل النصية داخل الكود الذي يتم تداخله. على سبيل المثال:

julia> str = """
           Hello,
           world.
         """
"  Hello,\n  world.\n"

في هذه الحالة، تحدد السطر النهائي (الفارغ) قبل إغلاق """ مستوى المسافة البادئة.

يتم تحديد مستوى التراجع كأطول تسلسل مشترك من المسافات أو علامات التبويب في جميع الأسطر، باستثناء السطر الذي يلي الافتتاحية """ والأسطر التي تحتوي فقط على مسافات أو علامات تبويب (السطر الذي يحتوي على الإغلاق """ مشمول دائمًا). ثم لجميع الأسطر، باستثناء النص الذي يلي الافتتاحية """، يتم إزالة التسلسل المشترك في البداية (بما في ذلك الأسطر التي تحتوي فقط على مسافات وعلامات تبويب إذا بدأت بهذا التسلسل)، على سبيل المثال:

julia> """    This
         is
           a test"""
"    This\nis\n  a test"

بعد ذلك، إذا كانت العلامة الافتتاحية """ متبوعة بسطر جديد، يتم إزالة سطر الجديد من السلسلة الناتجة.

"""hello"""

يساوي

"""
hello"""

لكن

"""

hello"""

سيحتوي على سطر جديد حرفي في البداية.

يتم تنفيذ إزالة الأسطر الجديدة بعد تقليل المسافة البادئة. على سبيل المثال:

julia> """
         Hello,
         world."""
"Hello,\nworld."

إذا تمت إزالة السطر الجديد باستخدام شرطة مائلة، فسيتم احترام تقليل المسافة البادئة أيضًا:

julia> """
         Averylong\
         word"""
"Averylongword"

تظل المسافات البيضاء المتبقية دون تغيير.

يمكن أن تحتوي السلاسل النصية المقتبسة ثلاث مرات على أحرف " دون الحاجة إلى الهروب.

لاحظ أن فواصل الأسطر في السلاسل الحرفية، سواء كانت مفردة أو ثلاثية الاقتباس، تؤدي إلى حرف سطر جديد (LF) \n في السلسلة، حتى لو كان محررك يستخدم عودة عربة \r (CR) أو مجموعة CRLF لإنهاء الأسطر. لتضمين CR في سلسلة، استخدم هروبًا صريحًا \r؛ على سبيل المثال، يمكنك إدخال السلسلة الحرفية "a CRLF line ending\r\n".

Common Operations

يمكنك مقارنة السلاسل بشكل قوامي باستخدام عوامل المقارنة القياسية:

julia> "abracadabra" < "xylophone"
true

julia> "abracadabra" == "xylophone"
false

julia> "Hello, world." != "Goodbye, world."
true

julia> "1 + 2 = 3" == "1 + 2 = $(1 + 2)"
true

يمكنك البحث عن فهرس حرف معين باستخدام دالة findfirst و دالة findlast:

julia> findfirst('o', "xylophone")
4

julia> findlast('o', "xylophone")
7

julia> findfirst('z', "xylophone")

يمكنك بدء البحث عن حرف في إزاحة معينة باستخدام الدوال findnext و findprev:

julia> findnext('o', "xylophone", 1)
4

julia> findnext('o', "xylophone", 5)
7

julia> findprev('o', "xylophone", 5)
4

julia> findnext('o', "xylophone", 8)

يمكنك استخدام دالة occursin للتحقق مما إذا كانت سلسلة فرعية موجودة داخل سلسلة:

julia> occursin("world", "Hello, world.")
true

julia> occursin("o", "Xylophon")
true

julia> occursin("a", "Xylophon")
false

julia> occursin('o', "Xylophon")
true

المثال الأخير يظهر أن occursin يمكن أن يبحث أيضًا عن حرف حرفي.

وظيفتان مفيدتان أخريان للسلاسل هما repeat و join:

julia> repeat(".:Z:.", 10)
".:Z:..:Z:..:Z:..:Z:..:Z:..:Z:..:Z:..:Z:..:Z:..:Z:."

julia> join(["apples", "bananas", "pineapples"], ", ", " and ")
"apples, bananas and pineapples"

بعض الوظائف المفيدة الأخرى تشمل:

  • firstindex(str) يعطي الحد الأدنى من الفهرس (بايت) الذي يمكن استخدامه للفهرسة في str (دائمًا 1 للسلاسل، وليس بالضرورة صحيحًا لبقية الحاويات).
  • lastindex(str) يعطي الفهرس الأقصى (بايت) الذي يمكن استخدامه للفهرسة في str.
  • length(str) عدد الأحرف في str.
  • length(str, i, j) عدد مؤشرات الأحرف الصالحة في str من i إلى j.
  • ncodeunits(str) عدد code units في سلسلة.
  • codeunit(str, i) يعطي قيمة وحدة الكود في السلسلة str عند الفهرس i.
  • thisind(str, i) أعطِ فهرسًا عشوائيًا في سلسلة وابحث عن أول فهرس للحرف الذي يشير إليه الفهرس.
  • nextind(str, i, n=1) العثور على بداية الحرف n بعد الفهرس i.
  • prevind(str, i, n=1) العثور على بداية الحرف n قبل الفهرس i.

Non-Standard String Literals

هناك حالات عندما تريد بناء سلسلة أو استخدام دلالات السلسلة، لكن سلوك بناء السلسلة القياسي ليس بالضبط ما تحتاجه. لهذه الأنواع من الحالات، توفر جوليا نصوصًا غير قياسية. يبدو النص غير القياسي مثل نص عادي محاط بعلامات اقتباس مزدوجة، لكنه يسبق على الفور بمعرف، وقد يتصرف بشكل مختلف عن النص العادي.

Regular expressions، byte array literals، و version number literals، كما هو موضح أدناه، هي بعض الأمثلة على السلاسل النصية غير القياسية. قد يقوم المستخدمون والحزم أيضًا بتعريف سلاسل نصية غير قياسية جديدة. يتم تقديم مزيد من الوثائق في قسم Metaprogramming.

Regular Expressions

أحيانًا لا تبحث عن سلسلة دقيقة، ولكن عن نمط معين. على سبيل المثال، افترض أنك تحاول استخراج تاريخ واحد من ملف نصي كبير. لا تعرف ما هو هذا التاريخ (لهذا السبب تبحث عنه)، ولكنك تعرف أنه سيبدو شيئًا مثل YYYY-MM-DD. تتيح لك التعبيرات العادية تحديد هذه الأنماط والبحث عنها.

تستخدم جوليا الإصدار 2 من التعبيرات العادية المتوافقة مع Perl (regexes)، كما هو موفر من قبل مكتبة PCRE (انظر PCRE2 syntax description لمزيد من التفاصيل). ترتبط التعبيرات العادية بالسلاسل النصية بطريقتين: الاتصال الواضح هو أن التعبيرات العادية تُستخدم للعثور على أنماط عادية في السلاسل النصية؛ والاتصال الآخر هو أن التعبيرات العادية تُدخل نفسها كسلاسل نصية، والتي يتم تحليلها إلى آلة حالة يمكن استخدامها للبحث بكفاءة عن الأنماط في السلاسل النصية. في جوليا، يتم إدخال التعبيرات العادية باستخدام نصوص غير قياسية مسبوقة بمحددات مختلفة تبدأ بـ r. أبسط نص للتعبير العادي بدون أي خيارات مفعلة يستخدم فقط r"...":

julia> re = r"^\s*(?:#|$)"
r"^\s*(?:#|$)"

julia> typeof(re)
Regex

للتحقق مما إذا كانت تعبيرات regex تتطابق مع سلسلة، استخدم occursin:

julia> occursin(r"^\s*(?:#|$)", "not a comment")
false

julia> occursin(r"^\s*(?:#|$)", "# a comment")
true

كما يمكن رؤيته هنا، occursin ببساطة يعيد true أو false، مما يدل على ما إذا كان هناك تطابق للتعبير العادي في السلسلة. ومع ذلك، عادةً ما يرغب المرء في معرفة ليس فقط ما إذا كانت السلسلة متطابقة، ولكن أيضًا كيف كانت متطابقة. لالتقاط هذه المعلومات حول التطابق، استخدم وظيفة match بدلاً من ذلك:

julia> match(r"^\s*(?:#|$)", "not a comment")

julia> match(r"^\s*(?:#|$)", "# a comment")
RegexMatch("#")

إذا لم يتطابق التعبير العادي مع السلسلة المعطاة، فإن match يعيد nothing - قيمة خاصة لا تطبع أي شيء في موجه التفاعل. بخلاف عدم الطباعة، فهي قيمة طبيعية تمامًا ويمكنك اختبارها برمجيًا:

m = match(r"^\s*(?:#|$)", line)
if m === nothing
    println("not a comment")
else
    println("blank or comment")
end

إذا كانت تعبيرًا منتظمًا يتطابق، فإن القيمة المعادة بواسطة match هي كائن RegexMatch. تسجل هذه الكائنات كيفية تطابق التعبير، بما في ذلك السلسلة الفرعية التي يتطابق معها النمط وأي سلاسل فرعية تم التقاطها، إذا كانت موجودة. هذا المثال يلتقط فقط الجزء من السلسلة الفرعية الذي يتطابق، ولكن ربما نريد التقاط أي نص غير فارغ بعد حرف التعليق. يمكننا القيام بما يلي:

julia> m = match(r"^\s*(?:#\s*(.*?)\s*$)", "# a comment ")
RegexMatch("# a comment ", 1="a comment")

عند استدعاء match، لديك خيار تحديد فهرس لبدء البحث. على سبيل المثال:

julia> m = match(r"[0-9]","aaaa1aaaa2aaaa3",1)
RegexMatch("1")

julia> m = match(r"[0-9]","aaaa1aaaa2aaaa3",6)
RegexMatch("2")

julia> m = match(r"[0-9]","aaaa1aaaa2aaaa3",11)
RegexMatch("3")

يمكنك استخراج المعلومات التالية من كائن RegexMatch:

  • المطابقة الفرعية الكاملة: m.match
  • المجموعات الملتقطة كصفيف من السلاسل: m.captures
  • الإزاحة التي تبدأ عندها المطابقة الكاملة: m.offset
  • إزاحات السلاسل الفرعية الملتقطة كمتجه: m.offsets

عندما لا يتطابق الالتقاط، بدلاً من سلسلة فرعية، يحتوي m.captures على nothing في تلك الموضع، و m.offsets لديه إزاحة صفرية (تذكر أن الفهارس في جوليا تبدأ من 1، لذا فإن الإزاحة الصفرية في سلسلة غير صالحة). إليك زوج من الأمثلة التي قد تبدو مصطنعة بعض الشيء:

julia> m = match(r"(a|b)(c)?(d)", "acd")
RegexMatch("acd", 1="a", 2="c", 3="d")

julia> m.match
"acd"

julia> m.captures
3-element Vector{Union{Nothing, SubString{String}}}:
 "a"
 "c"
 "d"

julia> m.offset
1

julia> m.offsets
3-element Vector{Int64}:
 1
 2
 3

julia> m = match(r"(a|b)(c)?(d)", "ad")
RegexMatch("ad", 1="a", 2=nothing, 3="d")

julia> m.match
"ad"

julia> m.captures
3-element Vector{Union{Nothing, SubString{String}}}:
 "a"
 nothing
 "d"

julia> m.offset
1

julia> m.offsets
3-element Vector{Int64}:
 1
 0
 2

من الملائم أن يتم إرجاع الالتقاطات كمصفوفة حتى يمكن استخدام بناء الجملة للتفكيك لربطها بالمتغيرات المحلية. كوسيلة راحة، يقوم كائن RegexMatch بتنفيذ طرق التكرار التي تمرر إلى حقل captures، بحيث يمكنك تفكيك كائن المطابقة مباشرة:

julia> first, second, third = m; first
"a"

يمكن أيضًا الوصول إلى الالتقاطات عن طريق فهرسة كائن RegexMatch باستخدام رقم أو اسم مجموعة الالتقاط:

julia> m=match(r"(?<hour>\d+):(?<minute>\d+)","12:45")
RegexMatch("12:45", hour="12", minute="45")

julia> m[:minute]
"45"

julia> m[2]
"45"

يمكن الإشارة إلى الالتقاطات في سلسلة الاستبدال عند استخدام replace من خلال استخدام \n للإشارة إلى مجموعة الالتقاط nth وإضافة بادئة سلسلة الاستبدال بـ s. تشير مجموعة الالتقاط 0 إلى كائن المطابقة بالكامل. يمكن الإشارة إلى مجموعات الالتقاط المسماة في الاستبدال باستخدام \g<groupname>. على سبيل المثال:

julia> replace("first second", r"(\w+) (?<agroup>\w+)" => s"\g<agroup> \1")
"second first"

يمكن أيضًا الإشارة إلى مجموعات الالتقاط المرقمة كـ \g<n> للتوضيح، كما في:

julia> replace("a", r"." => s"\g<0>1")
"a1"

يمكنك تعديل سلوك التعبيرات العادية من خلال بعض التركيبات من العلامات i، m، s، و x بعد علامة الاقتباس المزدوجة المغلقة. هذه العلامات لها نفس المعنى كما هو الحال في Perl، كما هو موضح في هذا الاقتباس من perlre manpage:

i   Do case-insensitive pattern matching.

    If locale matching rules are in effect, the case map is taken
    from the current locale for code points less than 255, and
    from Unicode rules for larger code points. However, matches
    that would cross the Unicode rules/non-Unicode rules boundary
    (ords 255/256) will not succeed.

m   Treat string as multiple lines.  That is, change "^" and "$"
    from matching the start or end of the string to matching the
    start or end of any line anywhere within the string.

s   Treat string as single line.  That is, change "." to match any
    character whatsoever, even a newline, which normally it would
    not match.

    Used together, as r""ms, they let the "." match any character
    whatsoever, while still allowing "^" and "$" to match,
    respectively, just after and just before newlines within the
    string.

x   Tells the regular expression parser to ignore most whitespace
    that is neither backslashed nor within a character class. You
    can use this to break up your regular expression into
    (slightly) more readable parts. The '#' character is also
    treated as a metacharacter introducing a comment, just as in
    ordinary code.

على سبيل المثال، يحتوي التعبير النمطي التالي على جميع العلامات الثلاثة مفعلة:

julia> r"a+.*b+.*d$"ism
r"a+.*b+.*d$"ims

julia> match(r"a+.*b+.*d$"ism, "Goodbye,\nOh, angry,\nBad world\n")
RegexMatch("angry,\nBad world")

يتم إنشاء الرمز r"..." بدون استبدال أو فك تشفير (باستثناء علامة الاقتباس " التي لا تزال بحاجة إلى الهروب). إليك مثال يوضح الفرق عن السلاسل النصية القياسية:

julia> x = 10
10

julia> r"$x"
r"$x"

julia> "$x"
"10"

julia> r"\x"
r"\x"

julia> "\x"
ERROR: syntax: invalid escape sequence

تدعم أيضًا سلاسل التعبيرات العادية المقتبسة ثلاثيًا، بالشكل r"""..."""، (وقد تكون مريحة للتعبيرات العادية التي تحتوي على علامات اقتباس أو أسطر جديدة).

يمكن استخدام مُنشئ Regex() لإنشاء سلسلة regex صالحة برمجيًا. يسمح ذلك باستخدام محتويات متغيرات السلسلة وعمليات السلسلة الأخرى عند بناء سلسلة regex. يمكن استخدام أي من رموز regex المذكورة أعلاه ضمن وسيط السلسلة المفردة لـ Regex(). إليك بعض الأمثلة:

julia> using Dates

julia> d = Date(1962,7,10)
1962-07-10

julia> regex_d = Regex("Day " * string(day(d)))
r"Day 10"

julia> match(regex_d, "It happened on Day 10")
RegexMatch("Day 10")

julia> name = "Jon"
"Jon"

julia> regex_name = Regex("[\"( ]\\Q$name\\E[\") ]")  # interpolate value of name
r"[\"( ]\QJon\E[\") ]"

julia> match(regex_name, " Jon ")
RegexMatch(" Jon ")

julia> match(regex_name, "[Jon]") === nothing
true

لاحظ استخدام تسلسل الهروب \Q...\E. يتم تفسير جميع الأحرف بين \Q و \E كأحرف حرفية. هذا مفيد لمطابقة الأحرف التي ستكون خلاف ذلك أحرف ميتا في التعبيرات العادية. ومع ذلك، يجب توخي الحذر عند استخدام هذه الميزة مع استبدال السلاسل، حيث قد تحتوي السلسلة المستبدلة نفسها على تسلسل \E، مما ينهي مطابقة الأحرف الحرفية بشكل غير متوقع. يجب تطهير مدخلات المستخدمين قبل تضمينها في تعبير عادي.

Byte Array Literals

سلسلة حرفية غير قياسية مفيدة أخرى هي سلسلة حرفية لمصفوفة بايت: b"...". تتيح لك هذه الصيغة استخدام تدوين السلسلة للتعبير عن مصفوفات بايت حرفية للقراءة فقط - أي مصفوفات من القيم UInt8. نوع تلك الكائنات هو CodeUnits{UInt8, String}. القواعد الخاصة بسلاسل مصفوفة البايت هي كما يلي:

  • تنتج أحرف ASCII وهروب ASCII بايت واحد فقط.
  • \x وتسلسلات الهروب الثمانية تنتج بايت يتوافق مع قيمة الهروب.
  • تنتج تسلسلات الهروب Unicode سلسلة من البايتات التي تشفر تلك النقطة الرمزية في UTF-8.

هناك بعض التداخل بين هذه القواعد حيث أن سلوك \x والهروب الثماني الأقل من 0x80 (128) مشمول في كلا القاعدتين الأوليين، ولكن هنا تتفق هذه القواعد. معًا، تسمح هذه القواعد باستخدام أحرف ASCII، وقيم بايت عشوائية، وتسلسلات UTF-8 لإنتاج مصفوفات من البايتات. إليك مثال يستخدم الثلاثة:

julia> b"DATA\xff\u2200"
8-element Base.CodeUnits{UInt8, String}:
 0x44
 0x41
 0x54
 0x41
 0xff
 0xe2
 0x88
 0x80

السلسلة ASCII "DATA" تتوافق مع البايتات 68، 65، 84، 65. \xff ينتج البايت الفردي 255. الهروب Unicode \u2200 مشفر في UTF-8 كالثلاث بايتات 226، 136، 128. لاحظ أن مصفوفة البايت الناتجة لا تتوافق مع سلسلة UTF-8 صالحة:

julia> isvalid("DATA\xff\u2200")
false

كما تم ذكره، فإن نوع CodeUnits{UInt8, String} يتصرف مثل مصفوفة للقراءة فقط من UInt8 وإذا كنت بحاجة إلى متجه قياسي يمكنك تحويله باستخدام Vector{UInt8}:

julia> x = b"123"
3-element Base.CodeUnits{UInt8, String}:
 0x31
 0x32
 0x33

julia> x[1]
0x31

julia> x[1] = 0x32
ERROR: CanonicalIndexError: setindex! not defined for Base.CodeUnits{UInt8, String}
[...]

julia> Vector{UInt8}(x)
3-element Vector{UInt8}:
 0x31
 0x32
 0x33

أيضًا لاحظ التمييز الكبير بين \xff و \uff: تسلسل الهروب الأول يشفر البايت 255، بينما تسلسل الهروب الأخير يمثل نقطة الشيفرة 255، والتي يتم تشفيرها كـ بايتين في UTF-8:

julia> b"\xff"
1-element Base.CodeUnits{UInt8, String}:
 0xff

julia> b"\uff"
2-element Base.CodeUnits{UInt8, String}:
 0xc3
 0xbf

تستخدم الحروف الأدبية نفس السلوك.

بالنسبة لنقاط الشيفرة الأقل من \u80، يحدث أن ترميز UTF-8 لكل نقطة شيفرة هو ببساطة البايت الفردي الناتج عن الهروب المقابل \x، لذا يمكن تجاهل التمييز بأمان. بالنسبة للهروب \x80 إلى \xff مقارنةً بـ \u80 إلى \uff، ومع ذلك، هناك فرق كبير: الهروب الأول جميعها ترمز إلى بايتات فردية، والتي - ما لم تتبعها بايتات استمرارية محددة جدًا - لا تشكل بيانات UTF-8 صالحة، في حين أن الهروب الأخير جميعها تمثل نقاط شيفرة يونيكود بترميزات ذات بايتين.

إذا كان كل هذا مربكًا للغاية، حاول قراءة "The Absolute Minimum Every Software Developer Absolutely, Positively Must Know About Unicode and Character Sets". إنه مقدمة ممتازة لـ Unicode و UTF-8، وقد يساعد في تخفيف بعض الارتباك بشأن هذا الموضوع.

Version Number Literals

يمكن التعبير عن أرقام الإصدارات بسهولة باستخدام نصوص غير قياسية من الشكل v"...". تخلق نصوص أرقام الإصدارات كائنات VersionNumber التي تتبع مواصفات semantic versioning، وبالتالي تتكون من قيم عددية رئيسية وثانوية وتصحيح، تليها تعليقات توضيحية للأصدارات السابقة والبناء. على سبيل المثال، يتم تقسيم v"0.2.1-rc1+win64" إلى إصدار رئيسي 0، إصدار ثانوي 2، إصدار تصحيح 1، إصدار سابق rc1 وبناء win64. عند إدخال نص إصدار، فإن كل شيء باستثناء رقم الإصدار الرئيسي هو اختياري، لذلك على سبيل المثال v"0.2" يعادل v"0.2.0" (مع تعليقات توضيحية فارغة للإصدار السابق/البناء)، و v"2" يعادل v"2.0.0"، وهكذا.

تعتبر كائنات VersionNumber مفيدة بشكل أساسي لمقارنة إصدارين (أو أكثر) بسهولة وبدقة. على سبيل المثال، الثابت VERSION يحتفظ برقم إصدار جوليا ككائن VersionNumber، وبالتالي يمكن للمرء تعريف بعض السلوكيات الخاصة بالإصدار باستخدام عبارات بسيطة مثل:

if v"0.2" <= VERSION < v"0.3-"
    # do something specific to 0.2 release series
end

لاحظ أنه في المثال أعلاه يتم استخدام رقم الإصدار غير القياسي v"0.3-"، مع وجود - في النهاية: هذه الصيغة هي امتداد لجوليا للمعيار، وتستخدم للإشارة إلى إصدار أقل من أي إصدار 0.3، بما في ذلك جميع إصداراته السابقة. لذا في المثال أعلاه، سيعمل الكود فقط مع إصدارات 0.2 المستقرة، ويستبعد مثل هذه الإصدارات كـ v"0.3.0-rc1". للسماح أيضًا بإصدارات 0.2 غير المستقرة (أي الإصدارات السابقة)، يجب تعديل فحص الحد الأدنى على النحو التالي: v"0.2-" <= VERSION.

تسمح إضافة غير قياسية أخرى لمواصفات الإصدارات باستخدام علامة + في النهاية للتعبير عن حد أعلى لإصدارات البناء، على سبيل المثال، يمكن استخدام VERSION > v"0.2-rc1+" لتعني أي إصدار أعلى من 0.2-rc1 وأي من بنياته: ستعيد false للإصدار v"0.2-rc1+win64" و true للإصدار v"0.2-rc2".

من الممارسات الجيدة استخدام مثل هذه الإصدارات الخاصة في المقارنات (خصوصًا، يجب دائمًا استخدام - في النهاية على الحدود العليا ما لم يكن هناك سبب وجيه لعدم القيام بذلك)، ولكن لا ينبغي استخدامها كرقم إصدار فعلي لأي شيء، لأنها غير صالحة في نظام إصدار الدلالات.

بجانب استخدامها للثابت VERSION، تُستخدم كائنات VersionNumber على نطاق واسع في وحدة Pkg، لتحديد إصدارات الحزم واعتمادياتها.

Raw String Literals

يمكن التعبير عن السلاسل النصية الخام بدون التداخل أو فك التشفير باستخدام الحروف النصية غير القياسية بالشكل raw"...". تخلق الحروف النصية الخام كائنات String عادية تحتوي على المحتويات المحاطة بها تمامًا كما تم إدخالها دون أي تداخل أو فك تشفير. هذا مفيد للسلاسل النصية التي تحتوي على كود أو ترميز في لغات أخرى تستخدم $ أو \ كحروف خاصة.

الاستثناء هو أنه يجب الهروب من علامات الاقتباس، على سبيل المثال raw"\"" يعادل "\"". لجعل من الممكن التعبير عن جميع السلاسل، يجب أيضًا الهروب من الشرطات العكسية، ولكن فقط عند ظهورها مباشرة قبل حرف الاقتباس:

julia> println(raw"\\ \\\"")
\\ \"

لاحظ أن أول اثنين من شرطات الإرجاع تظهران كما هما في الناتج، حيث إنهما لا يسبقان حرف اقتباس. ومع ذلك، فإن حرف الإرجاع التالي يهرب من حرف الإرجاع الذي يليه، والشرط الأخير يهرب من اقتباس، حيث إن هذه الشرطات تظهر قبل اقتباس.

Annotated Strings

Note

واجهة برمجة التطبيقات لـ AnnotatedStrings تعتبر تجريبية وقابلة للتغيير بين إصدارات جوليا.

من المفيد أحيانًا أن تكون قادرًا على الاحتفاظ بالبيانات الوصفية المتعلقة بمناطق من سلسلة نصية. AnnotatedString يلتف حول سلسلة نصية أخرى ويسمح بتعليق مناطق منها بقيم معنونة (:label => value). يتم تطبيق جميع عمليات السلسلة النصية العامة على السلسلة الأساسية. ومع ذلك، عندما يكون ذلك ممكنًا، يتم الحفاظ على معلومات التنسيق. هذا يعني أنه يمكنك معالجة 4d61726b646f776e2e436f64652822222c2022416e6e6f7461746564537472696e672229_4072656620426173652e416e6e6f7461746564537472696e67 —أخذ أجزاء فرعية منها، وتوسيعها، ودمجها مع سلاسل نصية أخرى— وستأتي تعليقات البيانات الوصفية "مع الرحلة".

هذا النوع من السلاسل أساسي لـ StyledStrings stdlib، الذي يستخدم تعليقات معنونة بـ :face للاحتفاظ بمعلومات التنسيق.

عند دمج AnnotatedString، تأكد من استخدام annotatedstring بدلاً من string إذا كنت ترغب في الاحتفاظ بتعليقات السلسلة.

julia> str = Base.AnnotatedString("hello there",
               [(1:5, :word, :greeting), (7:11, :label, 1)])
"hello there"

julia> length(str)
11

julia> lpad(str, 14)
"   hello there"

julia> typeof(lpad(str, 7))
Base.AnnotatedString{String}

julia> str2 = Base.AnnotatedString(" julia", [(2:6, :face, :magenta)])
" julia"

julia> Base.annotatedstring(str, str2)
"hello there julia"

julia> str * str2 == Base.annotatedstring(str, str2) # *-concatenation still works
true

يمكن الوصول إلى التعليقات الخاصة بـ AnnotatedString وتعديلها عبر دوال annotations و annotate!.