স্ট্রিংবাফার / স্ট্রিংবিল্ডারের সমতুল্য সি?


184

এমন কি একটি সি ++ স্ট্যান্ডার্ড টেম্পলেট লাইব্রেরি ক্লাস যা দক্ষ স্ট্রিং কনটেনটেশন কার্যকারিতা সরবরাহ করে, সি # এর স্ট্রিংবিল্ডার বা জাভার স্ট্রিংবফারের মতো ?


3
সংক্ষিপ্ত উত্তরটি হ'ল: হ্যাঁ, এসটিএলের একটি ক্লাস রয়েছে এবং এটি std::ostringstream
CoffeDeveloper

আরে @ অ্যান্ড্রু আপনি দয়া করে গৃহীত উত্তর পরিবর্তন করতে পারেন? একটি সুস্পষ্ট বিজয়ী উত্তর আছে এবং এটি বর্তমান গৃহীত উত্তর নয়।
নাল

উত্তর:


53

দ্রষ্টব্য এই উত্তরটি সম্প্রতি কিছু মনোযোগ পেয়েছে। আমি এটিকে সমাধান হিসাবে পরামর্শ দিচ্ছি না (এটি একটি সমাধান যা আমি অতীতে দেখেছি, এসটিএলের আগে)। এটি একটি আকর্ষণীয় দৃষ্টিভঙ্গি এবং কেবলমাত্র আপনার কোডটি প্রোফাইলের পরে প্রয়োগ করা উচিত std::stringবা আপনি std::stringstreamযদি আবিষ্কার করেন যে এটি একটি উন্নতি করে।

আমি সাধারণত std::stringবা হয় ব্যবহার std::stringstream। এগুলি নিয়ে আমার কোনও সমস্যা হয়নি। আমি যদি আগে স্ট্রিংয়ের রুক্ষ আকার জানতাম তবে আমি প্রথমে কিছু কক্ষ সংরক্ষণ করি।

আমি অন্যান্য লোককে তাদের অতীত অতীতে নিজেরাই অনুকূলিত স্ট্রিং বিল্ডার তৈরি করতে দেখেছি।

class StringBuilder {
private:
    std::string main;
    std::string scratch;

    const std::string::size_type ScratchSize = 1024;  // or some other arbitrary number

public:
    StringBuilder & append(const std::string & str) {
        scratch.append(str);
        if (scratch.size() > ScratchSize) {
            main.append(scratch);
            scratch.resize(0);
        }
        return *this;
    }

    const std::string & str() {
        if (scratch.size() > 0) {
            main.append(scratch);
            scratch.resize(0);
        }
        return main;
    }
};

এটি দুটি স্ট্রিংয়ের বেশিরভাগ স্ট্রিংয়ের জন্য এবং অন্যটি সংক্ষিপ্ত স্ট্রিংগুলি সংক্ষেপণের জন্য স্ক্র্যাচ অঞ্চল হিসাবে ব্যবহার করে। এটি একটি ছোট স্ট্রিংয়ের মধ্যে সংক্ষিপ্ত সংযোজন ক্রিয়াকলাপগুলিকে ব্যাচ করে তারপরে এটি অনুকূল করে তোলে মূল স্ট্রিংয়ের সাথে এটি যুক্ত করে, ফলে এটি বড় হওয়ার সাথে সাথে মূল স্ট্রিংয়ে প্রয়োজনীয় পুনর্বিবেচনার সংখ্যা হ্রাস করে।

আমার সাথে std::stringবা এই কৌশলটি প্রয়োজন হয়নি std::stringstream। আমার মনে হয় এটি স্ট্যান্ড :: স্ট্রিংয়ের আগে একটি তৃতীয় পক্ষের স্ট্রিং লাইব্রেরি সহ ব্যবহৃত হয়েছিল, এটি ছিল অনেক আগে। আপনি যদি এই প্রোফাইলের মতো কৌশল গ্রহণ করেন তবে প্রথমে আপনার অ্যাপ্লিকেশনটি।


13
চাকা পুনর্নবীকরণ। স্ট্যান্ড :: স্ট্রিংস্ট্রিম হ'ল সঠিক উত্তর। নীচে ভাল উত্তর দেখুন।
Kobor42

12
@ Kobor42 আমি আমার উত্তরটির প্রথম এবং শেষ লাইনের দিকে নির্দেশ করার সাথে সাথে আমি আপনার সাথে একমত হই।
আইয়েন

1
আমি মনে করি না scratchস্ট্রিংটি এখানে সত্যিই কিছু সম্পাদন করে। মূল স্ট্রিংয়ের পুনঃনির্ধারণের সংখ্যাটি মূলত এটি চূড়ান্ত আকারের একটি ফাংশন হতে চলেছে, সংযোজন পরিচালনের সংখ্যা নয়, যদি না stringবাস্তবায়নটি খুব কম হয় (যেমন, তাত্পর্যপূর্ণ বৃদ্ধি ব্যবহার করে না)। সুতরাং "ব্যাচিং" সাহায্য appendকরে না কারণ অন্তর্নিহিত একবার stringবড় হয়ে গেলে এটি কেবল মাঝে মধ্যেই দুভাবেই বাড়বে। এর এটি অপ্রয়োজনীয় কপি অপারেশন একটি গুচ্ছ যোগ করে, ও মে উপরে আরো reallocations (অত: পর কল new/ delete) যেহেতু আপনি একটি ছোট স্ট্রিং সংযোজন করা হয়।
BeeOnRope

@ বিওনরোপ আমি আপনার সাথে একমত
আইইন

আমি নিশ্চিত str.reserve(1024);যে এই জিনিসটির চেয়ে দ্রুততর হবে
হ্যানশেরিক

160

সি ++ উপায়টি হল std :: স্ট্রিংস্ট্রিম বা কেবল সরল স্ট্রিং কনট্যাঙ্কেশন ব্যবহার করা । সি ++ স্ট্রিংগুলি পারস্পরিক পরিবর্তনযোগ্য তাই কনক্যাটেনেশনের পারফরম্যান্স বিবেচনার বিষয়টি কম উদ্বেগের বিষয়।

ফর্ম্যাট করার ক্ষেত্রে আপনি একই স্ট্রিমিংটিতে কোনও স্ট্রিমে একই রকম করতে পারেন তবে ভিন্ন উপায়ে করতে পারেনcout । অথবা আপনি একটি দৃ strongly়ভাবে টাইপ করা ফান্টর ব্যবহার করতে পারেন যা এটি এনপ্যাপুলেট করে এবং একটি স্ট্রিং সরবরাহ করে interfaceএর জন্য ইন্টারফেসের মতো ফর্ম্যাট যেমন বুস্ট :: ফর্ম্যাট


59
সি ++ স্ট্রিংগুলি পরিবর্তনযোগ্য : ঠিক। পুরো কারণ StringBuilderবিদ্যমান জাভা অপরিবর্তনীয় বেসিক স্ট্রিং ধরণের অদক্ষতা আবরণ । অন্য কথায় StringBuilderপ্যাচওয়ার্ক, তাই আমাদের খুশী হওয়া উচিত যে আমাদের সি ++ তে এই জাতীয় শ্রেণির দরকার নেই।
বোবোবো

55
@ বোবোবো অপরিবর্তনীয় স্ট্রিংগুলির অন্যান্য সুবিধা রয়েছে যদিও এর কোর্সগুলির জন্য ঘোড়া
জে.কে.

8
সরল স্ট্রিং কনটেনটেশনগুলি কোনও নতুন অবজেক্ট তৈরি করে না, তাই জাভাতে স্থায়ীত্বের মতো একই সমস্যা? নীচের উদাহরণে সমস্ত ভেরিয়েবলগুলি স্ট্রিংগুলি বিবেচনা করুন: a = b + c + d + e + f; এটি কি অপারেটরকে + বি এবং সি-তে কল করবে না, তারপরে অপারেটর + ফলাফলের উপর এবং ডি ইত্যাদি?
সার্জ রোগাচ

9
এক মিনিটের লোককে ধরে রাখুন, স্ট্যান্ডার্ড স্ট্রিং ক্লাস কীভাবে নিজেকে পরিবর্তন করতে জানে তবে এর অর্থ এই নয় যে অদক্ষতা সেখানে নেই। যতদূর আমি জানি স্টাডি :: স্ট্রিং এর অভ্যন্তরীণ চরটির আকারটি কেবল প্রসারিত করতে পারে না। এর অর্থ এটি এমনভাবে পরিবর্তন করা যাতে আরও অক্ষরের প্রয়োজন হয় তা পুনর্নির্ধারণ এবং অনুলিপি প্রয়োজন। এটি অক্ষরের ভেক্টরের চেয়ে আলাদা নয় এবং সেক্ষেত্রে আপনার প্রয়োজনীয় স্থানটি সংরক্ষণ করা অবশ্যই ভাল।
ট্রিগভে স্কোগশলম

7
@ ট্রাইগভেস্কোগশলম - এটি অক্ষরের একটি ভেক্টরের চেয়ে আলাদা নয়, তবে অবশ্যই স্ট্রিংয়ের "ক্ষমতা" তার আকারের চেয়ে বড় হতে পারে, সুতরাং সমস্ত সংযোজনগুলি পুনর্বিবেচনার প্রয়োজন হয় না। সাধারণ স্ট্রিংগুলিতে তাত্পর্যপূর্ণ বৃদ্ধির কৌশল ব্যবহার করা হবে যাতে লিনিয়ার ব্যয়ের ক্রিয়াকলাপের সাথে এখনও যুক্ত করা হয়। এটি জাভার অপরিবর্তনীয় স্ট্রিংগুলির চেয়ে আলাদা যেখানে প্রতিটি সংযোজন ক্রিয়াকলাপকে উভয় স্ট্রিংয়ের সমস্ত অক্ষরকে একটি নতুনতে অনুলিপি করতে হবে, তাই সংযোজনগুলির একটি সিরিজ O(n)সাধারণ হিসাবে শেষ হয় ।
BeeOnRope

93

std::string.appendফাংশন একটি ভাল বিকল্প কারণ এটা ডেটার নানা রূপে গ্রহণ করে না নয়। আরও কার্যকর বিকল্প হ'ল ব্যবহার করা std::stringstream; তাই ভালো:

#include <sstream>
// ...

std::stringstream ss;

//put arbitrary formatted data into the stream
ss << 4.5 << ", " << 4 << " whatever";

//convert the stream buffer into a string
std::string str = ss.str();

43

std::string হয় সি ++ সমতুল্য: এটা চপল আছে।


13

আপনি সহজভাবে স্ট্রিং স্ট্রিংয়ের জন্য .append () ব্যবহার করতে পারেন।

std::string s = "string1";
s.append("string2");

আমি মনে করি আপনি এমনকি করতে সক্ষম হতে পারে:

std::string s = "string1";
s += "string2";

সি # এর বিন্যাসকরণ ক্রিয়াকলাপ হিসাবে StringBuilder, আমি বিশ্বাস করি snprintf(বা sprintfআপনি যদি বগি কোড লেখার ঝুঁকি নিতে চান ;-)) একটি অক্ষর অ্যারে রূপান্তর করুন এবং স্ট্রিংয়ে ফিরে রূপান্তর একমাত্র বিকল্প সম্পর্কে about


প্রিন্টফ বা। নেট এর স্ট্রিংয়ের মতো নয় in ফর্ম্যাট যদিও সেগুলি?
অ্যান্ডি শেলাম

1
এটি একমাত্র উপায় যদিও এটি একেবারেই বিতর্কিত
জে কে।

2
@ জে কে -। নেট এর স্ট্রিংবিল্ডারের বিন্যাসকরণের দক্ষতার তুলনা করার সময় এগুলিই একমাত্র উপায়, যা মূল প্রশ্নটি বিশেষত জিজ্ঞাসা করেছিল। আমি "আমি বিশ্বাস করি" বলেছিলাম যাতে আমি ভুল হতে পারি, তবে আপনি কি আমাকে প্রিন্টফ ব্যবহার না করে স্ট্রিংবিল্ডারের কার্যকারিতা সি ++ এ পাওয়ার জন্য কোনও উপায় দেখাতে পারেন?
অ্যান্ডি শেলাম

কিছু বিকল্প ফর্ম্যাটিং বিকল্পগুলি অন্তর্ভুক্ত করার জন্য আমার উত্তর আপডেট করেছে
জে.কে.

6

যেহেতু std::stringসি ++ তে পরিবর্তনযোগ্য আপনি এটি ব্যবহার করতে পারেন। এটি একটি += operatorএবং একটি appendফাংশন আছে।

আপনার যদি সংখ্যার তথ্য সংযোজন করতে হয় তবে std::to_stringফাংশনগুলি ব্যবহার করুন ।

আপনি যদি কোনও স্ট্রিংয়ে কোনও বস্তু সিরিয়াল করতে সক্ষম হয়ে থাকেন তবে আরও ক্লাসিকটি চাইলে std::stringstreamক্লাসটি ব্যবহার করুন । তবে এটি আপনার নিজস্ব কাস্টম ক্লাসগুলির সাথে কাজ করার জন্য আপনার নিজের স্ট্রিমিং অপারেটর ফাংশনগুলি প্রয়োগ করতে হবে।


4

স্ট্যান্ড :: স্ট্রিংয়ের + = কনস্ট চর * এর সাথে কাজ করে না ("স্ট্রিংয়ের মতো" স্ট্রিংয়ের মতো জিনিসগুলি কী বলে মনে হচ্ছে), তাই স্পষ্টভাবে স্ট্রিংস্ট্রিম ব্যবহার করা প্রয়োজনীয়টির নিকটতম - আপনি কেবল << পরিবর্তে ব্যবহার করুন


3

সি ++ এর জন্য একটি সুবিধাজনক স্ট্রিং বিল্ডার

আগে উত্তর দেওয়া অনেক লোকের মতো, std :: স্ট্রিংস্ট্রিম হল পছন্দের পদ্ধতি। এটি ভাল কাজ করে এবং প্রচুর রূপান্তর এবং ফর্ম্যাটিং বিকল্প রয়েছে। আইএমওতে এটির একটির অসুবিধাজনক ত্রুটি রয়েছে: আপনি এটি ওয়ান লাইনার বা এক্সপ্রেশন হিসাবে ব্যবহার করতে পারবেন না। আপনাকে সর্বদা লিখতে হবে:

std::stringstream ss;
ss << "my data " << 42;
std::string myString( ss.str() );

যা বেশ বিরক্তিকর, বিশেষত যখন আপনি কনস্ট্রাক্টরের স্ট্রিংগুলি শুরু করতে চান।

কারণটি হ'ল, ক) স্টাডি :: স্ট্রিংস্ট্রিম স্ট্যান্ড :: স্ট্রিং এবং বি তে কোনও রূপান্তর অপারেটর নেই) স্ট্রিং স্ট্রিমের অপারেটর << () এর স্ট্রিংস্ট্রিম রেফারেন্সটি ফেরত দেয় না, তবে একটি এসটিডি :: অস্ট্রি রেফারেন্স পরিবর্তে দেয় - যা স্ট্রিং স্ট্রিম হিসাবে আরও গণনা করা যাবে না।

সমাধানটি হল std :: স্ট্রিংস্ট্রিমকে ওভাররাইড করা এবং এটি আরও ভাল মিলে যাওয়া অপারেটরগুলি দেওয়া:

namespace NsStringBuilder {
template<typename T> class basic_stringstream : public std::basic_stringstream<T>
{
public:
    basic_stringstream() {}

    operator const std::basic_string<T> () const                                { return std::basic_stringstream<T>::str();                     }
    basic_stringstream<T>& operator<<   (bool _val)                             { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (char _val)                             { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (signed char _val)                      { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned char _val)                    { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (short _val)                            { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned short _val)                   { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (int _val)                              { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned int _val)                     { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (long _val)                             { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned long _val)                    { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (long long _val)                        { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (unsigned long long _val)               { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (float _val)                            { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (double _val)                           { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (long double _val)                      { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (void* _val)                            { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (std::streambuf* _val)                  { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (std::ostream& (*_val)(std::ostream&))  { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (std::ios& (*_val)(std::ios&))          { std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (std::ios_base& (*_val)(std::ios_base&)){ std::basic_stringstream<T>::operator << (_val); return *this; }
    basic_stringstream<T>& operator<<   (const T* _val)                         { return static_cast<basic_stringstream<T>&>(std::operator << (*this,_val)); }
    basic_stringstream<T>& operator<<   (const std::basic_string<T>& _val)      { return static_cast<basic_stringstream<T>&>(std::operator << (*this,_val.c_str())); }
};

typedef basic_stringstream<char>        stringstream;
typedef basic_stringstream<wchar_t>     wstringstream;
}

এটির সাহায্যে আপনি পছন্দ মতো জিনিস লিখতে পারেন

std::string myString( NsStringBuilder::stringstream() << "my data " << 42 )

এমনকি নির্মাণকারী মধ্যে।

আমাকে স্বীকার করতে হবে যে আমি পারফরম্যান্সটি পরিমাপ করিনি, যেহেতু আমি এটি এমন পরিবেশে ব্যবহার করি নি যা স্ট্রিং বিল্ডিংয়ের এখনও ভারী ব্যবহার করে, তবে আমি ধরে নিচ্ছি যে এটি স্ট্যান্ড :: স্ট্রিংস্ট্রিমের চেয়ে বেশি খারাপ হবে না, যেহেতু সবকিছু সম্পন্ন হয়েছে রেফারেন্সের মাধ্যমে (স্ট্রিংয়ে রূপান্তর ব্যতীত, তবে স্ট্যান্ড স্ট্রিং স্ট্রিম স্ট্রিমেও একটি অনুলিপি অপারেশন চালিয়ে যায়)


এটা ঝরঝরে। আমি কেন std::stringstreamদেখছি না যে এইভাবে আচরণ করে না।
einpoklum

1

দড়ি যদি গন্তব্য স্ট্রিং এর র্যান্ডম স্থানে বা একটি দীর্ঘ গৃহস্থালির কাজ সিকোয়েন্স জন্য সন্নিবেশ করতে / মুছুন স্ট্রিং আছে ধারক মূল্য হতে পারে। এসজিআই এর বাস্তবায়ন থেকে এখানে একটি উদাহরণ দেওয়া হল:

crope r(1000000, 'x');          // crope is rope<char>. wrope is rope<wchar_t>
                                // Builds a rope containing a million 'x's.
                                // Takes much less than a MB, since the
                                // different pieces are shared.
crope r2 = r + "abc" + r;       // concatenation; takes on the order of 100s
                                // of machine instructions; fast
crope r3 = r2.substr(1000000, 3);       // yields "abc"; fast.
crope r4 = r2.substr(1000000, 1000000); // also fast.
reverse(r2.mutable_begin(), r2.mutable_end());
                                // correct, but slow; may take a
                                // minute or more.

0

নিম্নলিখিতগুলির কারণে আমি নতুন কিছু যুক্ত করতে চেয়েছিলাম:

প্রথম প্রয়াসে আমি মারতে ব্যর্থ হয়েছি

std::ostringstream এর operator<<

দক্ষতা, কিন্তু আরও প্রচেষ্টার সাহায্যে আমি একটি স্ট্রিংবিল্ডার তৈরি করতে সক্ষম হয়েছি যা কিছু ক্ষেত্রে দ্রুত।

আমি যখনই কোনও স্ট্রিং যুক্ত করি আমি কেবল এটির কোনও রেফারেন্স সঞ্চয় করি এবং মোট আকারের কাউন্টার বাড়িয়ে তুলি।

অবশেষে আমি এটি বাস্তবায়ন করার জন্য (হরর!) একটি অস্বচ্ছ বাফার (স্ট্যান্ড :: ভেক্টর <চার>) ব্যবহার করা:

  • 1 বাইট শিরোলেখ (নিম্নলিখিত বিস্তৃত ডেটা কিনা তা জানাতে 2 বিট: সরানো স্ট্রিং, স্ট্রিং বা বাইট [])
  • বাইট দৈর্ঘ্য বলতে 6 টি বিট []

বাইট জন্য []

  • আমি সরাসরি সংক্ষিপ্ত স্ট্রিংয়ের বাইটগুলি সঞ্চয় করি (ক্রমানুসারে মেমরি অ্যাক্সেসের জন্য)

সরানো স্ট্রিংগুলির জন্য (স্ট্রিংগুলি যুক্ত করা হয় std::move)

  • কোনও std::stringবস্তুর পয়েন্টার (আমাদের মালিকানা রয়েছে)
  • ক্লাসে একটি পতাকা সেট করুন যদি সেখানে অব্যবহৃত সংরক্ষিত বাইট থাকে

স্ট্রিং জন্য

  • একটি পয়েন্টার std::stringবস্তু (কোন মালিকানা)

একটি ছোট অপ্টিমাইজেশন রয়েছে, যদি সর্বশেষ stringোকানো স্ট্রিংটি মুভ করে দেওয়া হত, এটি নিখরচায় বাফার ব্যবহার না করে নিখরচায় তবে অব্যবহৃত বাইটগুলি পরীক্ষা করে এবং সেখানে আরও বাইট সংরক্ষণ করে (এটি কিছু স্মৃতি সঞ্চয় করতে পারে, এটি এটি এটিকে কিছুটা ধীর করে তোলে , সম্ভবত সিপিইউতেও নির্ভর করে এবং যেভাবেই হোক অতিরিক্ত সংরক্ষিত জায়গার সাথে স্ট্রিং দেখা বিরল)

এটি শেষ পর্যন্ত তুলনায় কিছুটা দ্রুত ছিল std::ostringstreamতবে এর কয়েকটি ডাউনসাইড রয়েছে:

  • আমি স্থির দৈর্ঘ্য চর প্রকারগুলি ধরে নিয়েছি (সুতরাং 1,2 বা 4 বাইট, ইউটিএফ 8 এর পক্ষে ভাল নয়), আমি বলছি না এটি ইউটিএফ 8 এর পক্ষে কাজ করবে না, কেবল আমি অলসতার জন্য এটি পরীক্ষা করিনি।
  • আমি খারাপ কোডিং অনুশীলন ব্যবহার করেছি (অস্বচ্ছ বাফার, এটি পোর্টেবল না করার পক্ষে সহজ, আমি বিশ্বাস করি আমার উপায়টি বহনযোগ্য)
  • সমস্ত বৈশিষ্ট্য অভাব ostringstream
  • কিছু রেফারেন্সযুক্ত স্ট্রিং মার্জিনের আগে সমস্ত স্ট্রিং মুছে ফেলা হয়: অপরিজ্ঞাত আচরণ behavior

উপসংহার? ব্যবহার std::ostringstream

এটি ইতিমধ্যে বৃহত্তম বাধা স্থির করে যখন খনি বাস্তবায়নের সাথে গতিতে কয়েক% পয়েন্ট গ্যান করে তোলা ডাউনসাইডগুলির পক্ষে উপযুক্ত নয়।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.