স্ট্রিং হলো কোটেশনের মধ্যে থাকা এক টুকরো টেক্সট। R-এর বর্তমানে সমর্থিত যেকোনো সংস্করণ স্ট্রিংগুলোকে UTF-8-এ এনকোড করে, তাই বিশ্বের বেশিরভাগ ভাষাকে সমর্থন করা যায়।
mystring <- "some text"
mystring
#> [1] "some text"
typeof(mystring)
#> [1] "character"
দুর্ভাগ্যবশত, অন্য ভাষার সাথে পরিচিত প্রোগ্রামারদের কাছে এই পরিভাষা বিভ্রান্তিকর হতে পারে।
যদিও type হলো character, তবুও একটি অক্ষর (অনেক ভাষায় যাকে char বলা হয়, কিন্তু R-এ নয়) আর একটি দীর্ঘ স্ট্রিংয়ের মধ্যে 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(), কিন্তু এর জন্য নির্দিষ্ট কিছু ফাংশন দরকার, যা নিচে আলোচনা করা হবে।
বেস R-এ বেশিরভাগ কাজই সম্ভব, কিন্তু অনেক প্রোগ্রামার স্ট্রিং নাড়াচাড়ার সমর্থনকে বরং সীমিত ও বিভ্রান্তিকর মনে করেন।
ভাগ্যক্রমে, tidyverse প্যাকেজ আছে, যেগুলো অনেক বেশি স্বজ্ঞাত, নমনীয় ও স্ব-সামঞ্জস্যপূর্ণ।
পুরনো ধরনের স্ট্রিং ফাংশন ব্যবহার করা এখনো প্রচুর লিগ্যাসি কোড আছে। Exercism-এর বেশিরভাগ কমিউনিটি সলিউশনও এর অন্তর্ভুক্ত।
এছাড়া, ছোট কাজের জন্য একটি বড় লাইব্রেরি ইমপোর্ট করার বদলে বিল্ট-ইন ফাংশন ব্যবহার করা কাজে লাগতে পারে। অনুশীলনীগুলো যাচাই করার জন্য নমুনা সলিউশন লেখার সময় R ট্র্যাকের রক্ষণাবেক্ষণকারীরা সাধারণত এই পথই নেন।
তাই, পুরনো পদ্ধতিগুলোর কিছুটা জ্ঞান এখনো শেখার মতোই।
ncharএকটি স্ট্রিংয়ে ক্যারেক্টারের সংখ্যা রিটার্ন করে।
nchar("string")
#> [1] 6
nchar("Hrōðgār") # a person in Beowulf, written in Old English
#> [1] 7
"ক্যারেক্টার" বলতে আসলে কী বোঝায়, সেই (জটিল!) বিষয়টি আমাদের আলোচনার বাইরে। উপরের উদাহরণগুলোতে কেবল কয়েকটি বিষয় লক্ষ্য করুন:
\0 দিয়ে টার্মিনেট করা হয় না (যা হলে দৈর্ঘ্যে ১ যোগ হতো)।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, C থেকে sprintf ফাংশনটি নকল করেছে (এবং পরবর্তী বেশ কয়েকটি ভাষা থেকেও: Wikipedia প্রায় ৩০টির তালিকা দেয়)।
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"
সদয় হয়ে বললে, পুরনো ধরনের স্ট্রিং ফাংশনগুলো R-এর সেরা সামর্থ্য নয় (কম সদয় মতামতও পাওয়া যায়)।
পরিস্থিতির উন্নতি করতে, stringr লাইব্রেরিটি ২০০৯ সাল থেকে তৈরি হচ্ছে, এবং এখন Tidyverse সংকলনের একটি গুরুত্বপূর্ণ অংশ।
library(stringr) বা library(tidyverse) দিয়ে এটি আপনার নেমস্পেসে ইমপোর্ট করুন।
কিছু মূল বৈশিষ্ট্য হলো:
str_ প্রিফিক্স থাকে এবং নামটি উদ্দেশ্য স্পষ্ট করার চেষ্টা করে।Tidyverse-এর সব প্যাকেজের মতোই stringr-এর ভালো ডকুমেন্টেশন আছে, যা সাধারণত প্যাকেজের লেখকদের দ্বারা লেখা।
ফাংশন জুড়তে পাইপ |> ব্যবহার নিয়ে সিলেবাসের অন্য অংশে আরও বিস্তারিত আলোচনা হবে।
আপাতত এটুকু জানলে কাজে লাগবে:
নিচের উদাহরণটি সিনট্যাক্স দেখায়। আলাদা আলাদা ফাংশন পরে আলোচনা করা হবে।
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_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-এ সরল স্ট্রিং ইন্টারপোলেশন নিয়ে আসে।
ব্রেস { }-এর ভিতরের এক্সপ্রেশনগুলো ইন্টারপ্রেট করা হয় এবং টেক্সট রূপটি বসানো হয়।
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
R-এর একটি খুব সাধারণ ব্যবহার হলো এলোমেলো ডেটাকে বিশ্লেষণের জন্য একটি সুসংগত ফরম্যাটে সাজিয়ে নেওয়া। এতে সাহায্য করতে টেক্সটকে সাবস্ট্রিংয়ে ভাগ করার বেশ কয়েকটি উপায় আছে। বেশিরভাগই এখনকার আলোচনার বাইরে।
str_split_1 সবচেয়ে সরল, একটি একক স্ট্রিংকে টুকরোর ভেক্টরে রূপান্তর করে, কোনো নির্দিষ্ট প্যাটার্নে ভাগ করে (কোনো ডিফল্ট প্যাটার্ন নেই)।
s <- "R Julia Python"
str_split_1(s, " ")
#> [1] "R" "Julia" "Python"
কিছু ভাষা (মূলত ইউরোপীয়) বড় হাতের (UPPERCASE) ও ছোট হাতের অক্ষরের মধ্যে পার্থক্য করে।
এগুলোকে লোকেল-উপযোগী উপায়ে পরস্পরে রূপান্তর করার জন্য 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"
এই অনুশীলনীতে আপনি লগ লাইন প্রসেস করবেন।
প্রতিটি লগ লাইন একটি স্ট্রিং, যার ফরম্যাট নিচের মতো: "[<LEVEL>]: <MESSAGE>"।
তিনটি আলাদা লগ লেভেল আছে:
INFOWARNINGERRORআপনার সামনে তিনটি টাস্ক আছে, যার প্রত্যেকটি একটি লগ লাইন নেবে এবং সেটি দিয়ে কিছু করতে বলবে।
আপনি base R-এর লিগ্যাসি ফাংশন অথবা stringr লাইব্রেরি, দুটির যেকোনোটি বেছে নিতে পারেন; দুটিই ছোট ও কার্যকর সমাধান দিতে পারে।
message ফাংশনটি ইমপ্লিমেন্ট করুন যাতে এটি লগ লাইনের মেসেজ রিটার্ন করে:
message("[ERROR]: Invalid operation")
#> "Invalid operation"
শুরুতে বা শেষে থাকা অতিরিক্ত হোয়াইটস্পেস মুছে ফেলা উচিত:
message("[WARNING]: Disk almost full\r\n")
#> "Disk almost full"
log_level ফাংশনটি ইমপ্লিমেন্ট করুন যাতে এটি লগ লাইনের লগ লেভেল রিটার্ন করে, যা ছোট হাতের অক্ষরে রিটার্ন করা উচিত:
log_level("[ERROR]: Invalid operation")
#> "error"
reformat ফাংশনটি ইমপ্লিমেন্ট করুন, যা লগ লাইনটি নতুন করে সাজায়: মেসেজটি আগে বসায় আর তার পরে বন্ধনীর ভেতরে লগ লেভেলটি রাখে:
reformat("[INFO]: Operation completed")
#> "Operation completed (info)"
Exercism-এ সাইন আপ করুন, R ট্র্যাকের 21টি কনসেপ্ট111টি অনুশীলনী আর সত্যিকারের মানুষের মেন্টরিং দিয়ে শিখুন ও দক্ষ হয়ে উঠুন, সম্পূর্ণ বিনামূল্যে।