সি # ব্যবহার করে সিএসভি ফাইল পড়া


169

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

StreamReader sr = new StreamReader(FilePath);
importingData = new Account();
string line;
string[] row = new string [5];
while ((line = sr.ReadLine()) != null)
{
    row = line.Split(',');

    importingData.Add(new Transaction
    {
        Date = DateTime.Parse(row[0]),
        Reference = row[1],
        Description = row[2],
        Amount = decimal.Parse(row[3]),
        Category = (Category)Enum.Parse(typeof(Category), row[4])
    });
}

তবে এ ক্ষেত্রে অ্যারে পরিচালনা করা খুব কঠিন difficult মানগুলি বিভক্ত করার আরও ভাল উপায় আছে কি?


আপনার সমাধানের জন্য ধন্যবাদ। এটিকে উত্তর পোস্ট হিসাবে পোস্ট করা বিবেচনা করুন - এটিকে প্রশ্নের মধ্যে অন্তর্ভুক্ত করা তার পাঠযোগ্যতায় সহায়তা করে না।
বার্তোসকেপিপি

উত্তর:


363

চাকা পুনরুদ্ধার করবেন না। নেট নেট বিসিএলে ইতিমধ্যে যা রয়েছে তার সুবিধা নিন।

  • একটি রেফারেন্স যোগ Microsoft.VisualBasic(হ্যাঁ, এটা VisualBasic বলেছেন কিন্তু এটা ঠিক যেমন ভাল C # কাজ করে - মনে রাখবেন যে শেষে এটা সব ঠিক আইএল হয়)
  • Microsoft.VisualBasic.FileIO.TextFieldParserCSV ফাইল পার্স করতে ক্লাসটি ব্যবহার করুন

নমুনা কোডটি এখানে:

using (TextFieldParser parser = new TextFieldParser(@"c:\temp\test.csv"))
{
    parser.TextFieldType = FieldType.Delimited;
    parser.SetDelimiters(",");
    while (!parser.EndOfData) 
    {
        //Processing row
        string[] fields = parser.ReadFields();
        foreach (string field in fields) 
        {
            //TODO: Process field
        }
    }
}

এটি আমার সি # প্রকল্পগুলিতে আমার জন্য দুর্দান্ত কাজ করে।

এখানে আরও কয়েকটি লিঙ্ক / তথ্য দেওয়া আছে:


18
আমি সত্যিই ইচ্ছুক এমন কোনও উপায় ছিল যা ভিবি লাইব্রেরি ব্যবহার না করে, তবে এটি পুরোপুরি কার্যকর হয়েছিল! ধন্যবাদ!
গিলোনবা

5
+1: আমি সবেমাত্র একটি 53Mb ফাইলে লুমেনওয়ার্কস ফাস্ট সিএসভি পাঠককে ভেঙে ফেলেছি। দেখে মনে হচ্ছে যে 43,000 সারি পরে লাইন ক্যাশিং ব্যর্থ হয়েছে এবং বাফারটিকে স্ক্র্যাম্ব করেছে। ভিবি চেষ্টা করে TextFieldParserএবং এটি কৌশলটি করেছে। ধন্যবাদ
কোডিং হয়ে গেছে

10
+1 দুর্দান্ত উত্তর, যেহেতু আমি দেখতে পেয়েছি যে অনেকেই জানেন না এই শ্রেণিটি বিদ্যমান। ভবিষ্যতের দর্শকদের জন্য একটি বিষয় লক্ষ্য রাখবেন যে পদ্ধতিটি আপনার জন্য সম্পত্তি সেট করে parser.TextFieldType = FieldType.Delimited;বলে আপনি কল করলে সেটিংসের প্রয়োজন হয় না । parser.SetDelimiters(",");TextFieldType
ব্রায়ান

10
এটিও দেখুন : dotnetperls.com/textfieldparser । স্ট্রিং.স্প্লিট এবং স্ট্রিমরেডারের চেয়ে টেক্সটফিল্ড পার্সারের আরও খারাপ পারফরম্যান্স রয়েছে। তবে স্ট্রিং.স্প্লিট এবং টেক্সটফিল্ড পার্সারের মধ্যে একটি বড় পার্থক্য রয়েছে। টেক্সটফিল্ড পার্সার কলামে কমা থাকার মতো অদ্ভুত মামলাগুলি পরিচালনা করে: আপনি একটি কলামের নাম "text with quote"", and comma"দিতে পারেন এবং আপনি text with quote", and commaভুলভাবে পৃথক হওয়া মানগুলির পরিবর্তে সঠিক মান পেতে পারেন । সুতরাং আপনি সিএসভি খুব সহজ হলে স্ট্রিংয়ের জন্য বেছে নিতে পারেন pস্প্লিট।
ইওংওয়ে উ

5
নোট করুন যে আপনার মাইক্রোসফ্টে একটি রেফারেন্স যুক্ত করতে হতে পারে Vএটি ব্যবহার করার জন্য ভিজুয়ালবাসিক। ভিজ্যুয়াল স্টুডিওতে আপনার প্রকল্পে ডান ক্লিক করুন, তারপরে অ্যাড> রেফারেন্স নির্বাচন করুন এবং মাইক্রোসফ্টের জন্য বাক্সটি দেখুন। ভিজুয়ালবাসিক।
ডেরেক কুর্থ

37

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

এগুলি আমি রূপ নিয়েছি ran

  • উদ্ধৃতিগুলি উদ্ধৃত এবং দ্বিগুণ (এক্সেল) অর্থাৎ 15 "-> ফিল্ড 1," 15 "" ", ফিল্ড 3
  • ক্ষেত্রটি অন্য কোনও কারণে উদ্ধৃত না করা হলে উদ্ধৃতি পরিবর্তন করা হয় না। অর্থাৎ 15 "-> ফিল্ড 1,15", ফিল্ড 3 3
  • উদ্ধৃতিগুলি \ সহ পালিয়ে যায় \ যেমন 15 "-> ফিল্ড 1," 15 \ "", ফিল্ড 3
  • উদ্ধৃতিগুলি মোটেও পরিবর্তিত হয় না (সঠিকভাবে বিশ্লেষণ করা এটি সর্বদা সম্ভব নয়)
  • ডিলিমিটারটি উদ্ধৃত হয় (এক্সেল)। যেমন একটি, খ -> ফিল্ড 1, "ক, খ", ফিল্ড 3
  • ডিলিমিটার \ সহ পালিয়ে যায় \ যেমন একটি, খ -> ফিল্ড 1, এ b, বি, ফিল্ড 3

আমি বিদ্যমান সিএসভি পার্সারগুলির অনেকগুলি চেষ্টা করে দেখেছি তবে এমন একটিও নেই যা আমার কাছে চলে আসা রূপগুলি পরিচালনা করতে পারে। যে দস্তাবেজগুলি পার্সার্স সমর্থনগুলি থেকে রক্ষা পাওয়া যায় তা থেকে এটি খুঁজে পাওয়াও কঠিন।

আমার প্রকল্পগুলিতে আমি এখন ভিবি টেক্সটফিল্ড পার্সার বা কাস্টম বিভাজন ব্যবহার করি।


1
আপনার দেওয়া পরীক্ষার মামলার জন্য এই উত্তরটি ভালবাসুন!
ম্যাথিউ রডাটাস

2
মূল সমস্যাটি হ'ল বেশিরভাগ বাস্তবায়নগুলি আরএফসি 4180 এর বিষয়ে চিন্তা করে না যা CSV ফর্ম্যাটটি বর্ণনা করে এবং কীভাবে ডিলিমিটরদের এড়ানো উচিত।
জেনি ও'রিলি

আরএফসি -১৪০০ ২০০৫-এর, যা এখন পুরানো বলে মনে হয় তবে মনে রাখবেন: নেট ফ্রেমওয়ার্কটি ২০০১ সালে প্রথম শুরু হয়েছিল Also এছাড়াও, আরএফসিগুলি সর্বদা সরকারী মান নয়, এবং এই ক্ষেত্রে এটির মতো ওজন বহন করে না বলে, , আইএসও -8601 বা আরএফসি -761।
জোয়েল কোহোর্ন 20

23

আমি নুগেট থেকে সিএসভিহেল্পারের প্রস্তাব দিই

(মাইক্রোসফ্টে একটি রেফারেন্স যুক্ত করা। ভিজুয়ালবাসিক ঠিক ঠিক মনে হয় না, এটি কেবল কুরুচিপূর্ণ নয়, এটি সম্ভবত ক্রস প্ল্যাটফর্মও নয়))


2
এটি ঠিক সি # এর মতো ক্রস-প্ল্যাটফর্ম।
PRMan

: ভুল, Linux এ Microsoft.VisualBasic.dll মনো উত্স, Microsoft এর চেয়ে ভিন্ন বাস্তবায়ন হয়েছে যা কিছু বিষয় আছে যা বাস্তবায়িত হয় না, উদাহরণস্বরূপ থেকে আসে stackoverflow.com/questions/6644165/...
knocte

(প্লাস, ভিবি ভাষার মনো-প্রকল্প তৈরি / বিকাশের ক্ষেত্রে জড়িত সংস্থাগুলির অধীনে কখনও মনোনিবেশ করা হয়নি, সুতরাং সি # বাস্তুতন্ত্র / টুলিংয়ের তুলনায় প্রচেষ্টার দিক থেকে এটি পিছিয়ে রয়েছে।)
নট

1
উভয়ের সাথে খেলে আমি CsvHelperক্লাস ম্যাপারে অন্তর্নির্মিত সারির সাথে যুক্ত করব; এটি কলামের শিরোনামগুলিতে (উপস্থিত থাকলে) এবং এমনকি কলামের ক্রমে স্পষ্টতই পরিবর্তনের জন্য অনুমতি দেয় (যদিও আমি নিজেও পরবর্তীটি পরীক্ষা করি নি)। সব মিলিয়ে এটি "হাই-লেভেল" এর চেয়ে অনেক বেশি বোধ করে TextFieldParser
ডেভিড

1
হ্যাঁ, মাইক্রোসফ্ট.ভিউজুয়াল
বেসিক

13

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

  1. এই উদাহরণে, আমি ফাইলটি পড়তে স্ট্রিমরিডার ব্যবহার করি
  2. প্রতিটি রেখা (গুলি) থেকে ডিলিমিটার সনাক্ত করতে রেজেক্স।
  3. সূচক 0 থেকে এন পর্যন্ত কলামগুলি সংগ্রহ করার জন্য একটি অ্যারে

using (StreamReader reader = new StreamReader(fileName))
    {
        string line; 

        while ((line = reader.ReadLine()) != null)
        {
            //Define pattern
            Regex CSVParser = new Regex(",(?=(?:[^\"]*\"[^\"]*\")*(?![^\"]*\"))");

            //Separating columns to array
            string[] X = CSVParser.Split(line);

            /* Do something with X */
        }
    }

4
অবশ্যই এটিতে নতুন লাইন রয়েছে এমন ডেটা নিয়ে সমস্যা আছে?
ডুগাল

এখন সিএসভি ডেটাফাইলে তথ্যগুলির মধ্যে ফাঁকা লাইন থাকার জন্য জানেন না, তবে আপনার যদি এমন একটি উত্স থাকে যা সে ক্ষেত্রে থাকে তবে আমি কেবল সহজ পাঠককে চালানোর আগে হোয়াইটস্পেসগুলি বা কিছুই না থাকা লাইনগুলি সরাতে একটি সাধারণ রেজিেক্স পরীক্ষা করতাম। বিভিন্ন উদাহরণের জন্য এখানে চেক করুন: stackoverflow.com/questions/7647716/…
মানা

1
রেজেক্সের চেয়ে এই ধরণের সমস্যার জন্য অবশ্যই একটি চর-ভিত্তিক পদ্ধতির বেশি প্রাকৃতিক। উদ্ধৃতি চিহ্নের উপস্থিতির উপর নির্ভর করে আচরণটি আলাদা বলে মনে করা হচ্ছে।
কেসি

6

সিএসভি জটিল রিয়েল দ্রুত পেতে পারে । দৃ rob়

এবং ভাল-পরীক্ষিত কিছু ব্যবহার করুন:
ফাইলহেল্পার্স: www.filehelpers.net

ফাইলহেল্পার্স একটি ফ্রি এবং সহজেই ব্যবহার করা যায়। নেট, গ্রন্থাগারটি ফাইল, স্ট্রিং বা স্ট্রিমে স্থির দৈর্ঘ্য বা সীমিত রেকর্ড থেকে ডেটা আমদানি / রফতানি করতে library


5
আমি মনে করি ফাইলহেল্পার একসাথে অনেক কিছু করার চেষ্টা করছে। ফাইলগুলি পার্সিং 2 পদক্ষেপের প্রক্রিয়া যেখানে আপনি প্রথমে লাইনগুলিকে ক্ষেত্রগুলিতে বিভক্ত করেন এবং তারপরে ক্ষেত্রগুলিকে ডেটাতে পার্স করেন। ফাংশনগুলির সংমিশ্রণে মাস্টার-বিশদ এবং লাইন ফিল্টারিংয়ের মতো জিনিসগুলি পরিচালনা করা কঠিন হয়ে পড়ে।
অ্যাড্রিনিম


4

এই তালিকার আর একটি, সিনচু ইটিএল - সিএসভি ফাইলগুলি পড়তে এবং লেখার জন্য একটি ওপেন সোর্স লাইব্রেরি

নীচে একটি নমুনা সিএসভি ফাইলের জন্য

Id, Name
1, Tom
2, Mark

দ্রুত আপনি নীচের মতো লাইব্রেরি ব্যবহার করে এগুলি লোড করতে পারেন

using (var reader = new ChoCSVReader("test.csv").WithFirstLineHeader())
{
   foreach (dynamic item in reader)
   {
      Console.WriteLine(item.Id);
      Console.WriteLine(item.Name);
   }
}

আপনার যদি সিএসভি ফাইলের সাথে মেলে পোকো ক্লাস

public class Employee
{
   public int Id { get; set; }
   public string Name { get; set; }
}

আপনি সিএসভি ফাইলটি নীচের মতো লোড করতে এটি ব্যবহার করতে পারেন

using (var reader = new ChoCSVReader<Employee>("test.csv").WithFirstLineHeader())
{
   foreach (var item in reader)
   {
      Console.WriteLine(item.Id);
      Console.WriteLine(item.Name);
   }
}

কীভাবে এটি ব্যবহার করবেন সে সম্পর্কে দয়া করে কোডপ্রজেক্টে নিবন্ধগুলি দেখুন ।

দাবি অস্বীকার: আমি এই গ্রন্থাগারের লেখক


হাই, আপনি কি এসএসএল সারণিতে সিএসভি লোড করতে পারেন - আমি সিএসভি টেবিলের শিরোনামটি হাতের আগে জানিনা know এসএসএল টেবিলটিতে
সিএসভিতে

হ্যা, তুমি পারো. দয়া করে এই লিঙ্কটি দেখুন stackoverflow.com/questions/20759302/...
RajN

2
private static DataTable ConvertCSVtoDataTable(string strFilePath)
        {
            DataTable dt = new DataTable();
            using (StreamReader sr = new StreamReader(strFilePath))
            {
                string[] headers = sr.ReadLine().Split(',');
                foreach (string header in headers)
                {
                    dt.Columns.Add(header);
                }
                while (!sr.EndOfStream)
                {
                    string[] rows = sr.ReadLine().Split(',');
                    DataRow dr = dt.NewRow();
                    for (int i = 0; i < headers.Length; i++)
                    {
                        dr[i] = rows[i];
                    }
                    dt.Rows.Add(dr);
                }

            }

            return dt;
        }

        private static void WriteToDb(DataTable dt)
        {
            string connectionString =
                "Data Source=localhost;" +
                "Initial Catalog=Northwind;" +
                "Integrated Security=SSPI;";

            using (SqlConnection con = new SqlConnection(connectionString))
                {
                    using (SqlCommand cmd = new SqlCommand("spInsertTest", con))
                    {
                        cmd.CommandType = CommandType.StoredProcedure;

                        cmd.Parameters.Add("@policyID", SqlDbType.Int).Value = 12;
                        cmd.Parameters.Add("@statecode", SqlDbType.VarChar).Value = "blagh2";
                        cmd.Parameters.Add("@county", SqlDbType.VarChar).Value = "blagh3";

                        con.Open();
                        cmd.ExecuteNonQuery();
                    }
                }

         }

আপনি কোথা থেকে এই সমাধানটি অনুলিপি করেছেন?
মাইন্ডরোস্টারমির

0

সবার আগে সিএসভি কী এবং এটি কীভাবে লিখতে হবে তা বুঝতে হবে।

  1. প্রতিটি পরবর্তী স্ট্রিং ( /r/n) পরবর্তী "সারণী" সারি হয় is
  2. "সারণী" কোষগুলি কিছু সীমানার চিহ্ন দ্বারা পৃথক করা হয়। বেশিরভাগ ক্ষেত্রে ব্যবহৃত প্রতীকগুলি হয় \tবা,
  3. প্রতিটি কক্ষে সম্ভবত এই ডিলিমিটার চিহ্ন থাকতে পারে (কোষের সাথে কোট চিহ্নটি শুরু করতে হবে এবং এই চিহ্নটির সাথে এই ক্ষেত্রে শেষ হবে)
  4. প্রতিটি ঘরে সম্ভবত /r/nসিবোল থাকতে পারে (কোষের সাথে কোট চিহ্নটি দিয়ে শুরু করতে হবে এবং এই চিহ্নটি দিয়ে এই ক্ষেত্রে শেষ হবে)

সিএসভি ফাইলগুলির সাথে কাজ করার জন্য সি # / ভিজ্যুয়াল বেসিকের সবচেয়ে সহজ উপায় হ'ল স্ট্যান্ডার্ড Microsoft.VisualBasicলাইব্রেরি ব্যবহার করা । আপনাকে কেবল আপনার ক্লাসে প্রয়োজনীয় রেফারেন্স এবং নিম্নলিখিত স্ট্রিং যুক্ত করতে হবে:

using Microsoft.VisualBasic.FileIO;

হ্যাঁ, আপনি এটি সি # তে ব্যবহার করতে পারেন, চিন্তা করবেন না। এই গ্রন্থাগারটি তুলনামূলকভাবে বড় ফাইলগুলি পড়তে পারে এবং প্রয়োজনীয় সমস্ত নিয়মকে সমর্থন করে, তাই আপনি সমস্ত সিএসভি ফাইলের সাথে কাজ করতে সক্ষম হবেন।

কিছু সময় আগে আমি এই লাইব্রেরির উপর ভিত্তি করে সিএসভি পড়ার / লেখার জন্য সহজ ক্লাস লিখেছিলাম। এই সাধারণ শ্রেণিটি ব্যবহার করে আপনি 2 টি মাত্রার অ্যারের মতো সিএসভিতে কাজ করতে সক্ষম হবেন। আপনি আমার লিখিত লিঙ্কটি দ্বারা ক্লাসটি সন্ধান করতে পারেন: https://github.com/ukushu/DataExporter

ব্যবহারের সহজ উদাহরণ:

Csv csv = new Csv("\t");//delimiter symbol

csv.FileOpen("c:\\file1.csv");

var row1Cell6Value = csv.Rows[0][5];

csv.AddRow("asdf","asdffffff","5")

csv.FileSave("c:\\file2.csv");

0

পূর্ববর্তী উত্তরগুলি সম্পূর্ণ করতে, তার নিজের সিএসভি ফাইল থেকে বস্তুর একটি সংগ্রহের প্রয়োজন হতে পারে হয় হয় TextFieldParserবা string.Splitপদ্ধতি দ্বারা পার্স করা হয় এবং তারপরে প্রতিটি লাইন প্রতিবিম্বের মাধ্যমে কোনও বস্তুতে রূপান্তরিত হয়। আপনার অবশ্যই প্রথমে এমন একটি শ্রেণির সংজ্ঞা দেওয়া দরকার যা সিএসভি ফাইলের লাইনের সাথে মেলে।

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

আমি নিম্নলিখিত পদ্ধতিটি দিয়ে আমার সিএসভি ফাইলটি ডিসেরায়াল করি:

public static IEnumerable<T> ReadCsvFileTextFieldParser<T>(string fileFullPath, string delimiter = ";") where T : new()
{
    if (!File.Exists(fileFullPath))
    {
        return null;
    }

    var list = new List<T>();
    var csvFields = GetAllFieldOfClass<T>();
    var fieldDict = new Dictionary<int, MemberInfo>();

    using (TextFieldParser parser = new TextFieldParser(fileFullPath))
    {
        parser.SetDelimiters(delimiter);

        bool headerParsed = false;

        while (!parser.EndOfData)
        {
            //Processing row
            string[] rowFields = parser.ReadFields();
            if (!headerParsed)
            {
                for (int i = 0; i < rowFields.Length; i++)
                {
                    // First row shall be the header!
                    var csvField = csvFields.Where(f => f.Name == rowFields[i]).FirstOrDefault();
                    if (csvField != null)
                    {
                        fieldDict.Add(i, csvField);
                    }
                }
                headerParsed = true;
            }
            else
            {
                T newObj = new T();
                for (int i = 0; i < rowFields.Length; i++)
                {
                    var csvFied = fieldDict[i];
                    var record = rowFields[i];

                    if (csvFied is FieldInfo)
                    {
                        ((FieldInfo)csvFied).SetValue(newObj, record);
                    }
                    else if (csvFied is PropertyInfo)
                    {
                        var pi = (PropertyInfo)csvFied;
                        pi.SetValue(newObj, Convert.ChangeType(record, pi.PropertyType), null);
                    }
                    else
                    {
                        throw new Exception("Unhandled case.");
                    }
                }
                if (newObj != null)
                {
                    list.Add(newObj);
                }
            }
        }
    }
    return list;
}

public static IEnumerable<MemberInfo> GetAllFieldOfClass<T>()
{
    return
        from mi in typeof(T).GetMembers(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static)
        where new[] { MemberTypes.Field, MemberTypes.Property }.Contains(mi.MemberType)
        let orderAttr = (ColumnOrderAttribute)Attribute.GetCustomAttribute(mi, typeof(ColumnOrderAttribute))
        orderby orderAttr == null ? int.MaxValue : orderAttr.Order, mi.Name
        select mi;            
}

0

আমি অত্যন্ত উচ্চারণে CSvHelper ব্যবহার করার পরামর্শ দিই।

এখানে একটি দ্রুত উদাহরণ:

public class csvExampleClass
{
    public string Id { get; set; }
    public string Firstname { get; set; }
    public string Lastname { get; set; }
}

var items = DeserializeCsvFile<List<csvExampleClass>>( csvText );

public static List<T> DeserializeCsvFile<T>(string text)
{
    CsvReader csv = new CsvReader( new StringReader( text ) );
    csv.Configuration.Delimiter = ",";
    csv.Configuration.HeaderValidated = null;
    csv.Configuration.MissingFieldFound = null;
    return (List<T>)csv.GetRecords<T>();
}

সম্পূর্ণ ডকুমেন্টেশনগুলি এখানে পাওয়া যাবে: https://joshclose.github.io/CsvHelper

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