Julia-তে একটি String হলো ক্যারেক্টারের একটি ক্রমবদ্ধ সিকোয়েন্স। ম্যানুয়াল-এ বলা হয়েছে: "অবশ্যই, আসল সমস্যা শুরু হয় যখন কেউ জিজ্ঞেস করে ক্যারেক্টার আসলে কী।"
এই জটিলতার অনেকটাই Chars কনসেপ্টে আরও বিস্তারিতভাবে আলোচনা করা হবে, তবে সংক্ষেপে:
সাধারণভাবে স্ট্রিংগুলো ডাবল কোট " "-এর মধ্যে রাখা হয়।
সিঙ্গেল কোট ' ' শুধু Chars-এর জন্য ব্যবহৃত হয়।
তিনটি ডাবল কোট """ """-ও ব্যবহার করা যায়, যা স্ট্রিংয়ের ভেতরে " ব্যবহার করতে দেয় এস্কেপিং ছাড়াই।
আরও গুরুত্বপূর্ণ, মাল্টিলাইন স্ট্রিং থেকে অবাঞ্ছিত অতিরিক্ত ইন্ডেন্টেশন বাদ দেওয়া হয়।
julia> """
Multiline
String
"""
"Multiline\nString\n"
C থেকে শুরু করে বেশিরভাগ ভাষার মতো, Julia এস্কেপিংয়ের জন্য ব্যাকস্ল্যাশ \ ব্যবহার করে:
\n।\$ (নিচে দেখুন)।একটি স্ট্রিং হলো একটি ইটারেবল কালেকশন, তাই ক্যারেক্টারের ভেক্টরে রূপান্তর করার কোনো প্রয়োজন ছাড়াই for ... in লুপ কাজ করবে।
julia> s = "Julia"
"Julia"
# admittedly, this is a silly example
julia> for c in s; print(c, "; "); end
J; u; l; i; a;
এভাবে ইটারেট করলে সুবিধা হলো Julia ক্যারেক্টারের সীমানা সঠিকভাবে সামলাবে, এমনকি নন-ASCII স্ট্রিংয়ের ক্ষেত্রেও। এর গুরুত্ব পরের অংশে আরও স্পষ্ট হবে।
নীতিগতভাবে, স্ট্রিংগুলোকে ভেক্টরের মতোই ইনডেক্স করা যায়:
julia> s[1], s[5]
('J', 'a')
এটি "Julia"-র মতো একটি ASCII স্ট্রিংয়ের জন্য স্পষ্টভাবে কাজ করে, কিন্তু এতে বিশ্বের বেশিরভাগ ভাষা উপেক্ষিত হয়।
প্রায় ১৫ শতাব্দী আগে প্রাচীন ইংরেজিতে লেখা Beowulf-এর একটি ক্যারেক্টার বিবেচনা করুন।
"Hrōðgār" স্পষ্টতই ৭টি ক্যারেক্টার, কিন্তু তাদের সবগুলো 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]=>'ð'
সমস্যা হলো এভাবে ইনডেক্স করলে বাইট গোনা হয়, যেগুলোর নন-ASCII স্ট্রিংয়ে ক্যারেক্টারের সাথে ১:১ সম্পর্ক থাকে না।
একটি Unicode/UTF-8 ক্যারেক্টার উপস্থাপন করতে ৪টি পর্যন্ত বাইট লাগতে পারে, এবং 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"
মনে রাখবেন, স্ট্রিং ইমিউটেবল, আর কনক্যাটেনেশনে কপি করা জড়িত থাকে। তাই লুপের ভেতরে বারবার কনক্যাটেনেশন করলে অদক্ষ হয়; বরং টুকরোগুলো একটি ভেক্টরে জমিয়ে শেষে সবগুলোকে join করা ভালো।
সহজ:
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"
যখন বন্ধনী বাদ দিলে অস্পষ্টতা তৈরি হবে তখন বন্ধনী আবশ্যক, কিন্তু একটি আইডেন্টিফায়ারের পরে হোয়াইটস্পেস থাকলে তা ঐচ্ছিক।
স্ট্রিংয়ে একটি $ রাখতে হলে সেটিকে \$ হিসেবে এস্কেপ করুন।
মনে রাখবেন, ইন্টারপোলেশনে বর্তমানে ফরম্যাটিং সমর্থিত নয়, যেমন কত দশমিক স্থান দেখানো হবে তা নির্ধারণ করা।
বিকল্প উপায়গুলো হলো:
round() ফাংশনের ভেতরে রেখে দিন।@sprintf() ম্যাক্রোটি নিচে বর্ণনা করা হয়েছে।@sprintf()স্ট্রিং অ্যাসেম্বলির আরও সূক্ষ্ম নিয়ন্ত্রণের জন্য, Julia C থেকে sprintf ফাংশনটি নকল করেছে (এবং পরবর্তী বেশ কয়েকটি ভাষাও: উইকিপিডিয়া তালিকা প্রায় ৩০টি ভাষার তালিকা দেয়)।
Julia-তে এটি Printf মডিউলের ভেতরে একটি macro হিসেবে বাস্তবায়িত, তাই @ প্রিফিক্স।
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"
এই উদাহরণগুলো একটি স্ট্রিংকে ছোট ছোট স্ট্রিং-এ ভাগ করে। একটি স্ট্রিংকে ক্যারেক্টার-এ ভাগ করা ভিন্ন অপারেশন।
julia> collect("input string")
12-element Vector{Char}:
'i': ASCII/Unicode U+0069 (category Ll: Letter, lowercase)
'n': ASCII/Unicode U+006E (category Ll: Letter, lowercase)
'p': ASCII/Unicode U+0070 (category Ll: Letter, lowercase)
'u': ASCII/Unicode U+0075 (category Ll: Letter, lowercase)
't': ASCII/Unicode U+0074 (category Ll: Letter, lowercase)
' ': ASCII/Unicode U+0020 (category Zs: Separator, space)
's': ASCII/Unicode U+0073 (category Ll: Letter, lowercase)
't': ASCII/Unicode U+0074 (category Ll: Letter, lowercase)
'r': ASCII/Unicode U+0072 (category Ll: Letter, lowercase)
'i': ASCII/Unicode U+0069 (category Ll: Letter, lowercase)
'n': ASCII/Unicode U+006E (category Ll: Letter, lowercase)
'g': ASCII/Unicode U+0067 (category Ll: Letter, lowercase)
একটি স্ট্রিংয়ে নির্দিষ্ট কোনো সাবস্ট্রিং আছে কি?
অদ্ভুতভাবে, এটি পরীক্ষা করার দুটি ফাংশন আছে: 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
একটি সাবস্ট্রিংয়ের start:end ইনডেক্স পেতে, আমাদের কয়েকটি ফাংশন আছে:
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 কনসেপ্টে আরও বিস্তারিতভাবে আলোচনা করা হয়েছে।
নোট: সহজতার জন্য, উপরের সব উদাহরণে স্ট্রিং লিটারাল ব্যবহার করা হয়েছে। অনেক ফাংশন আরও সাধারণ, এবং Regular Expressions-এর সাথেও কাজ করবে। আমরা পরের একটি কনসেপ্টে এটিতে ফিরে আসব।
আমাদের প্রায়ই একটি স্ট্রিং থেকে শুরু এবং/অথবা শেষের হোয়াইটস্পেস সরাতে হয়।
এর জন্য ফাংশনগুলো হলো 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 "
সংখ্যা এবং তাদের স্ট্রিং উপস্থাপনার মধ্যে পারস্পরিক রূপান্তর প্রায়ই উপকারী।
সংখ্যা থেকে স্ট্রিং করা সহজ, তবে ডিফল্ট ১০ ছাড়া অন্য কোনো বেসের জন্য একটি অতিরিক্ত প্যারামিটার দরকার:
julia> string(42)
"42"
julia> string(42, base=16)
"2a"
julia> string(42, base=2)
"101010"
স্ট্রিং পার্স করে একটি সাংখ্যিক মান পেতে একটু বেশি জটিল, এবং ভুলের সম্ভাবনাও বেশি। লক্ষ্য সাংখ্যিক টাইপ উল্লেখ করা বাধ্যতামূলক। বেস উল্লেখ করা ঐচ্ছিক এবং ডিফল্টভাবে ১০।
julia> parse(Int, "42")
42
julia> parse(Float64, "42")
42.0
julia> parse(Int, "42?")
ERROR: ArgumentError: invalid base 10 digit '?' in "42?"
julia> parse(Int, "42 ") # whitespace may be tolerated
42
কখনও কখনও স্ট্রিংয়ের ভেতরে স্বাভাবিক ইন্টারপোলেশন এবং এস্কেপিং বন্ধ করা কাজে লাগে।
এর জন্য, শুরুর কোটের আগে raw বসান।
julia> x = 10
10
julia> "x = $x if we use \$ for interpolation"
"x = 10 if we use \$ for interpolation"
julia> raw"x = $x if we use \$ for interpolation"
"x = \$x if we use \\\$ for interpolation"
রেগুলার এক্সপ্রেশনের নিজস্ব জটিল সিনট্যাক্স আছে, এবং Julia দুটি সহায়ক স্ট্রিং টাইপ দেয়: Regex-এর জন্য r" " এবং ক্যাপচার করা টুকরো নিয়ে কাজ করার জন্য s" "।
আরও নানা বিশেষ স্ট্রিং Exercism-ধাঁচের প্রোগ্রামিংয়ে কম দেখা যায়, যদিও লাইব্রেরি তৈরিতে ব্যাপকভাবে ব্যবহৃত হয়।
v"x.y.z" সফটওয়্যার রিলিজের major.minor.patch নম্বরিং সামলায়।b" " Unicode ক্যারেক্টার সীমানা এড়িয়ে বাইট স্তরে কাজ করে।