المسارات
/
R
R
/
المنهج
/
السلاسل النصية
ال

السلاسل النصية في R

تمرينان

نبذة عن السلاسل النصية

خلفية

السلسلة النصية هي قطعة من النص توضع بين علامتي تنصيص.

mystring <- "some text"
mystring
#> [1] "some text"
typeof(mystring)
#> [1] "character"

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

رغم أن type هو character، فإن R لا تفرّق بين حرف واحد (يُسمى char في عدة لغات أخرى، ولكن ليس في R) وبين سلسلة نصية طويلة. كلاهما عنصر واحد، ويمكن كتابته بعلامات تنصيص مفردة ' ' أو مزدوجة " " بالتبادل.

يوصي دليل الأسلوب باستخدام علامات التنصيص المزدوجة، إلا عندما تريد استخدام علامات تنصيص مزدوجة داخل السلسلة النصية دون الهروب منها.

str2 <- 'String with "quotes"'  # convenience format
str2
#> [1] "String with \"quotes\""    # standard format

وهكذا فإن character vector هو متجه من السلاسل النصية، حيث تكون كل سلسلة نصية عنصرًا واحدًا.

s <- c("some", "strings")
s
#> [1] "some"    "strings"
length(s)
#> [1] 2
length(s[1])  # the number of vector elements, not the string length
#> [1] 1

nchar(s[1])  # the number of characters in the string "some"
#> [1] 4

في المثال أعلاه، length(s[1]) يكافئ length("some"): أي عدد عناصر المتجه، وليس طول السلسلة النصية.

من الواضح أنه يمكن التعامل مع سلسلة نصية فردية، كما في حالة nchar() في المثال أعلاه، لكن ذلك يتطلب دوال معينة سنناقشها أدناه.

الهروب

كما رأينا أعلاه، تحتاج بعض محارف الترقيم إلى "الهروب منها" بوضع شرطة مائلة عكسية \ قبلها.

لن تكون هذه القائمة مفاجئة لمعظم المبرمجين ذوي الخبرة، إذ إن هذا الموضوع موحّد إلى حد كبير بين اللغات.

إلى جانب علامات التنصيص (كما هو موضح أعلاه)، تشمل القائمة محارف السطر الجديد \n، والمسافات الجدولية \t، والشرطة المائلة العكسية \\، ورموز يونيكود التي تبدأ بـ \U أو \u.

لتجنّب استخدام الكثير من محارف الهروب، يمكن تعريف سلاسل نصية خام داخل r"( )":

r"(raw "string")"
#> [1] "raw \"string\""

بالنسبة للسلاسل النصية الخام التي تحتوي على أقواس هلالية ( )، هناك العديد من خيارات الصياغة الأخرى.

الإعدادات المحلية

أي إصدار مدعوم حاليًا من R يرمّز السلاسل النصية بترميز UTF-8، لذا يمكن دعم معظم لغات العالم.

"आधुनिक मानक हिन्दी"
#> [1] "आधुनिक मानक हिन्दी"

بالإضافة إلى ذلك، تدرك دوال R كثيرة locale، وتحاول اتباع الأعراف المحلية في ترتيب السلاسل النصية، والتحويل إلى الأحرف الكبيرة/الصغيرة، والتواريخ والأوقات، وما إلى ذلك.

سيعرض هذا المنهج أمثلة بالإنجليزية الأمريكية أساسًا: لأنها الإعداد الافتراضي الشائع، ولأن مؤلفي المنهج يقيمون في الولايات المتحدة.

الإعدادات المحلية موضوع معقّد، لذا يُرجى البحث عن وثائق تناسب حالتك.

دوال السلاسل النصية

معظم الأشياء ممكنة في R الأساسية، لكن كثيرًا من المبرمجين يجدون الدعم الخاص بالتعامل مع السلاسل النصية محدودًا ومربكًا.

لحسن الحظ، هناك حزم tidyverse أكثر بديهية ومرونة واتساقًا.

R الأساسية

لا يزال هناك الكثير من الكود القديم الذي يستخدم دوال السلاسل النصية بالأسلوب القديم. وهذا يشمل معظم الحلول المجتمعية على Exercism.

بالإضافة إلى ذلك، قد يكون من المفيد في المهام الصغيرة استخدام دوال مدمجة بدلًا من استيراد مكتبة كبيرة. يميل مشرفو مسار R إلى اتباع هذا النهج عند كتابة حلول نموذجية للتحقق من صحة التمارين على مستودع GitHub الخاص بنا.

لذلك لا يزال من المفيد تعلّم شيء عن الأساليب القديمة.

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

nchar

تُرجع عدد المحارف في السلسلة النصية.

nchar("string")
#> [1] 6

nchar("Hrōðgār") # a person in Beowulf, written in Old English
#> [1] 7

موضوع معنى "المحرف" (معقّد!) خارج نطاقنا. لاحظ فقط نقطتين في الأمثلة أعلاه:

  • رغم أنها منفّذة بلغة C داخليًا، فإن السلاسل النصية في R لا تنتهي بـ \0 (وهو ما كان سيضيف 1 إلى الطول).
  • تُعالج محارف يونيكود متعددة البايتات كما قد تتوقع/تأمل. فكلمة "Hrōðgār" تتكون من 7 محارف (تسمى أيضًا "رونات" أو "نقاط ترميز")، لكنها تحتاج 10 بايتات للتخزين.

كن حذرًا بشأن ما تمرره إلى nchar. قد تظل النتائج غير متوقعة للقيم غير المنتمية إلى character، وإن كان هذا قد تحسّن في إصدارات R الحديثة.

grep

تبحث عن نمط في متجه من السلاسل النصية، وتُرجع الفهارس التي تحققت فيها مطابقة.

grep("Jav", c("Java", "Python", "Javascript"))
#> [1] 1 3
paste وpaste0

تدمج عددًا اعتباطيًا من السلاسل النصية في سلسلة نصية واحدة.

اختياريًا، يمكن تحديد فاصل لـ paste: القيمة الافتراضية مسافة.

paste0(v) يكافئ paste(v, sep = "").

paste("bits", "of", "a", "string")
#> [1] "bits of a string"
paste0("bits", "of", "a", "string")
#> [1] "bitsofastring"
sprintf

للتحكم الدقيق في تجميع السلاسل النصية، نقلت R دالة sprintf من لغة C (وعدة لغات لاحقة: تسرد ويكيبيديا نحو 30 منها).

r <- 5.3
pi <- 3.14159
sprintf("A circle of radius %.1f has area %.2f", r, pi * r^2)
#> [1] "A circle of radius 5.3 has area 88.25"
trimws

يمكن إزالة المسافات البيضاء في البداية و/أو النهاية باستخدام trimws():

s <- "    messy string   "
trimws(s)
#> [1] "messy string"

هناك معامل which = "left" (أو "right") لتجنّب إزالة المسافات من الطرفين.

substr

تعمل على سلسلة نصية واحدة وتُرجع سلسلة نصية فرعية بين حدّين (شاملين للطرفين).

substr("Exercism", 5, 7)
#> [1] "cis"
strsplit

تقسم سلسلة نصية إلى قائمة R، بناءً على سلسلة نصية فاصلة. الفاصل مطلوب (ولا توجد قيمة افتراضية له)، لكن يمكن أن يكون سلسلة نصية فارغة.

strsplit("R, Python, Julia", ", ")
[[1]]
#> [1] "R"      "Python" "Julia" 

strsplit("Exercism", "")
[[1]]
#> [1] "E" "x" "e" "r" "c" "i" "s" "m"

ستُغطى القوائم في مفهوم منفصل. إن لم تكن قد وصلت بعد إلى ذلك الجزء من المنهج، فاعلم في الوقت الحالي أن دالة unlist() ستحوّل (في هذه الحالة) قائمة إلى متجه.

unlist(strsplit("Exercism", ""))
#> [1] "E" "x" "e" "r" "c" "i" "s" "m"
regexpr وregexpr

اختصار لـ "التعبير النمطي"، تبحث هذه الدوال عن مواضع ظهور نمط في سلسلة نصية، وتُرجع قائمة ببيانات (قد تكون) مفيدة. نذكرها لاكتمال الصورة فقط: هناك خيارات أفضل!

regexpr تُرجع أول ظهور فقط، وgregexpr تُرجع كل حالات الظهور ("g" تعني بحثًا "شاملًا").

وكما يوحي الاسمان، يمكن أن يكون النمط تعبيرًا نمطيًا، وهو ما سيُغطى في مفهوم لاحق.

Advanced

لماذا تبدو دوال السلاسل النصية القديمة هذه مثل أوامر صدفة يونكس؟

رغم أن R أُنشئت عام 1993، فقد صُممت لتكون متوافقة إلى حد كبير مع اللغة S.

صُممت S في مختبرات بيل، وظهرت لأول مرة عام 1976. كانت مختبرات بيل مسؤولة أيضًا عن يونكس (منذ 1969) واللغة C (1972).

فريق رائع من حقبة أقدم بكثير! لا يسعنا إلا أن نفترض أنهم تحدثوا مع بعضهم وتبادلوا الأفكار.

Tidyverse

وإن تحلّينا بحسن الظن، يمكننا القول إن دوال السلاسل النصية بالأسلوب القديم ليست أفضل ما تقدمه R (وهناك آراء أقل تسامحًا).

لتحسين الوضع، ما زالت مكتبة stringr قيد التطوير منذ عام 2009، وهي الآن جزء أساسي من مجموعة Tidyverse.

استوردها إلى نطاقك باستخدام library(stringr) أو library(tidyverse).

تشمل بعض الميزات الأساسية:

  • كل أسماء الدوال لها البادئة str_ وتحاول توضيح الغرض.
  • يحاول ترتيب الوسائط أن يكون متسقًا، مع بيانات الإدخال كوسيط أول.
  • يمكن أن تتراوح المدخلات من سلسلة نصية واحدة إلى بنى بيانات معقدة. وستحاول الدوال التكيّف واختيار أنسب تصرف.

ومثل كل حزم Tidyverse، تتمتع stringr بوثائق جيدة، يكتبها عادةً مؤلف الحزمة (أو المؤلفون).

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

فكّر فيما تريد أن تُدخله (سلسلة نصية واحدة؟ متجه؟ شيء أكثر تعقيدًا؟)، وما تريد أن تحصل عليه.

على الأرجح هناك طريقة سهلة لفعل ما تريد، إن استطعت إيجاد الدالة الصحيحة والوسائط الصحيحة.

استطراد: مقدمة سريعة عن الأنابيب

ستُناقش استخدام الأنابيب |> لربط الدوال بمزيد من التفصيل في أجزاء أخرى من المنهج.

في الوقت الحالي، من المفيد أن تعرف ما يلي:

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

يوضح المثال أدناه الصياغة. وستُناقش الدوال الفردية لاحقًا.

library(stringr)

"Monday-25-February" |> 
  str_split("-") |>     
  unlist() |>               # [1] "Monday"   "25"       "February"
  str_sub(1, 3) |>          # [1] "Mon" "25"  "Feb"
  str_to_upper() |>         # [1] "MON" "25"  "FEB"
  str_flatten(collapse = ", ")
#> [1] "MON, 25, FEB"
إدارة الأطوال

تحسب str_length نقاط الترميز (على غرار nchars).

تتعامل دوال أخرى متنوعة مع المسافات البيضاء، ومنها str_trim() وstr_squish(). غالبًا ما تكون هذه مهمة تنظيف أساسية في بداية أي مشروع لعلم البيانات.

# The first 3 kernels in JuPyteR notebooks:
str_length(c("Julia", "Python", "R"))           #> [1] 1 6 5
#> [1] 5 6 1
str_pad(c("Julia", "Python", "R"), 8, "right")  # add spaces if necessary
#> [1] "Julia   " "Python  " "R       "

str_trim("Julia    Python    R    ")   # remove leading & trailing whitespace
#> [1] "Julia    Python    R"
str_squish("Julia   Python  R    ")  # trim, collapse multiple spaces 
#> [1] "Julia Python R"
استخراج أجزاء من السلاسل النصية

ستُرجع الدالة str_sub(string, start, end) السلسلة النصية الفرعية من start إلى end (شاملًا الطرفين)، بينما يمكن لـ str_sub_all() العمل على عدة سلاسل نصية.

هناك مرونة كبيرة. القيمة الافتراضية لـ start وend هي المحرف الأول والأخير على التوالي. يمكن أن يكونا متجهين من الفهارس. وعلى عكس فهرسة المتجهات (وهو أمر مرحّب به)، تعدّ القيم السالبة من نهاية السلسلة النصية، على طريقة Python.

s <- "abcdefgh"
str_sub(s, 2, 4)
#> [1] "bcd"
str_sub(s, 5)
#> [1] "efgh"
str_sub(s, -3, -2)
#> [1] "fg"
str_sub(s, c(1, 4), c(2, 5))
#> [1] "ab" "de"
str_sub(s, c(1, 4, 6), c(2, 5, -1))
#> [1] "ab"  "de"  "fgh"

s |> str_sub(2, 4) # these functions are designed for pipes
#> [1] "bcd"

# str_sub_all takes vector input, gives list output
str_sub_all(c(s, "ijklmnop"), c(1, 4, 6), c(2, 5, -1))
[[1]]
#> [1] "ab"  "de"  "fgh"

[[2]]
#> [1] "ij"  "lm"  "nop"
دمج السلاسل النصية

تحوّل str_flatten متجه محارف إلى سلسلة نصية واحدة. يُدرج وسيط collapse بين كل عنصرين، وقيمته الافتراضية "".

c("R", "Julia", "Python") |> str_flatten()
#> [1] "RJuliaPython"
c("R", "Julia", "Python") |> str_flatten(collapse = " - ")
#> [1] "R - Julia - Python"

تحوّل str_c عدة متجهات محارف إلى متجه محارف واحد، مع إعادة التدوير عند الحاجة.

str_c(LETTERS[1:8], 1:8, sep = ":")
#> [1] "A:1" "B:2" "C:3" "D:4" "E:5" "F:6" "G:7" "H:8"

تجلب str_glue إقحام السلاسل النصية البسيط إلى R. تُفسَّر التعبيرات داخل الأقواس المعقوفة { } ويُستبدل بها تمثيلها النصي.

إذا قُدّمت عدة تعبيرات نصية، فسيتم دمجها أيضًا كما في str_flatten. يمكن أيضًا إزالة المسافات البيضاء في البداية/النهاية من السلاسل النصية اختياريًا.

r <- 5.3
pi <- 3.14159
str_glue("A circle of radius {r} has area {pi * r^2}")
A circle of radius 5.3 has area 88.2472631

رغم أن str_glue مرنة (ومعقدة إلى حد كبير)، فإن نطاقًا أوسع من الإمكانات متاح في مكتبة glue الأساسية.

تقسيم السلاسل النصية

من الاستخدامات الشائعة جدًا لـ R تنسيق البيانات الفوضوية في صيغة متسقة للتحليل. وللمساعدة في ذلك، هناك عدة طرق لتقسيم النص إلى سلاسل نصية فرعية.

str_split_1 هي الأبسط، إذ تحوّل سلسلة نصية واحدة إلى متجه من القطع، مقسّمة عند نمط محدد (ولا يوجد نمط افتراضي).

s <- "R Julia Python"
str_split_1(s, " ")
#> [1] "R"      "Julia"  "Python"

تأخذ str_split_i متجه محارف وفهرسًا n، وتُرجع متجهًا بالسلسلة النصية الفرعية رقم n من كل سلسلة نصية مُدخلة.

s2 <- c(s, "C Kotlin, F#")  #> [1] "R Julia Python" "C Kotlin, F#"
str_split_i(s2, " ", 2)  # 2nd element of each
#> [1] "Julia"   "Kotlin,"

تأخذ str_split متجه محارف، وتُرجع قائمة من المتجهات. هناك مفهوم القوائم في منهجنا يشرح المخرجات.

str_split(s2, " ")
[[1]]
#> [1] "R"      "Julia"  "Python"

[[2]]
#> [1] "C"       "Kotlin," "F#"     

هناك أيضًا str_split_fixed، التي تُرجع مصفوفة. ستُغطى المصفوفات في مفهوم لاحق.

مطابقة السلاسل النصية

لمطابقة السلسلة النصية كاملة، يمكن لـ str_equal() استخدام تعريفات متنوعة لـ "التساوي". وفي أبسط الحالات، يراعي الوضع الافتراضي حالة الأحرف لكن يمكن تغييره. تخلق الإعدادات المحلية ويونيكود إمكانات أكثر بكثير.

str_equal("Odin", "odin")
#> [1] FALSE
str_equal("Odin", "odin", ignore_case = TRUE)
#> [1] TRUE

هناك دوال أخرى كثيرة، ستُناقش بمزيد من التفصيل في مفهوم التعبيرات النمطية.

تعديل السلاسل النصية

تميّز بعض اللغات (الأوروبية أساسًا) بين الأحرف الكبيرة والأحرف الصغيرة. وهناك مجموعة من دوال stringr للتحويل بينها، بطرق مناسبة للإعدادات المحلية:

  • str_to_lower()
  • str_to_upper()
  • str_to_title() : تحويل الحرف الأول من كل كلمة إلى حرف كبير
  • str_to_sentence() : تحويل الحرف الأول من الجملة إلى حرف كبير
str_to_upper("elixir")    # defaults to locale = "en"
#> [1] "ELIXIR"
str_to_upper("ελληνικά")  # infers Greek
#> [1] "ΕΛΛΗΝΙΚΆ"

str_to_title("the cat sat on the mat")
#> [1] "The Cat Sat On The Mat"
str_to_sentence("the cat sat on the mat")
#> [1] "The cat sat on the mat"

لاستبدال السلاسل النصية الفرعية بناءً على مطابقة النمط، تُغيّر str_replace() أول ظهور للنمط، وتُغيّر str_replace_all() كل حالات الظهور.

لاستبدال السلاسل النصية الفرعية بناءً على فهرسة المحارف، هناك صيغة إسناد لـ str_sub().

 s <- "abcde"
str_sub(s, 3, 4) <- "yz"
s
#> [1] "abyze"
متفرقات

تنشئ str_dup نسخًا مكررة وتزيل str_unique النسخ المكررة.

افتراضيًا لا يوجد فاصل في str_dup، لكن يمكن تحديد واحد.

str_dup("xyz", 4, sep = ", ")
#> [1] "xyz, xyz, xyz, xyz"

str_unique امتداد لـ unique(). كلتاهما تزيل المدخلات المكررة من متجه، لكن دالة stringr يمكنها أيضًا التعامل مع حساسية حالة الأحرف ومطابقات متنوعة خاصة بالإعدادات المحلية.

inp <- c("some", "strings", "Some", "Strings")
str_unique(inp)
#> [1] "some"    "strings" "Some"    "Strings"
str_unique(inp, ignore_case = TRUE)
#> [1] "some"    "strings"
Advanced

مكتبة stringr مبنية على مكتبة stringi ذات الدوال الأدنى مستوى.

إن كانت لديك احتياجات محددة لم تُناقش أعلاه، فحينئذ:

  1. تحقق من وثائق stringr، التي تغطي خيارات أكثر مما يمكننا ذكره حتى في هذا المستند الطويل للمفهوم.
  2. تحقق من وثائق stringi، التي تصف وظائف أكثر بكثير.
تعديل عبر GitHub يفتح الرابط في نافذة أو علامة تبويب جديدة

تعلّم السلاسل النصية

التدريب مقفل

افتح 1 تمرين إضافي لممارسة السلاسل النصية