C # का उपयोग करके CSV फ़ाइलों को पढ़ना


169

मैं एक साधारण आयात आवेदन लिख रहा हूँ और एक CSV फ़ाइल को पढ़ने की जरूरत है, एक DataGridऔर ग्रिड में CSV फ़ाइल के दूषित लाइनों को दिखाने और दिखाने के लिए। उदाहरण के लिए, उन पंक्तियों को दिखाएं जो किसी अन्य ग्रिड में 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])
    });
}

लेकिन इस मामले में सरणियों पर काम करना बहुत मुश्किल है। क्या मूल्यों को विभाजित करने का एक बेहतर तरीका है?


आपके समाधान के लिए धन्यवाद। इसे उत्तर पोस्ट के रूप में पोस्ट करने पर विचार करें - प्रश्न में इसे शामिल करने से इसकी पठनीयता में मदद नहीं मिलती है।
बार्टोज़कपी

जवाबों:


363

पहिया को सुदृढ़ मत करो। .NET BCL में जो पहले से है उसका लाभ उठाएं।

  • एक संदर्भ जोड़ें Microsoft.VisualBasic(हाँ, यह VisualBasic कहता है, लेकिन यह C # में भी काम करता है - याद रखें कि अंत में यह सिर्फ IL है)
  • 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 फाइल पर लुमेनवर्क्स फास्ट CSV रीडर को तोड़ा है। ऐसा लगता है कि 43,000 पंक्तियों के बाद लाइन कैशिंग विफल हो गई और बफर को खंगाल डाला। VB की कोशिश की TextFieldParserऔर इसने चाल चली। साभार
१२:५२

10
+1 महान जवाब, जैसा कि मैंने पाया कि बहुत से लोग नहीं जानते कि यह वर्ग मौजूद है। भविष्य के दर्शकों के लिए एक बात ध्यान रखें कि parser.TextFieldType = FieldType.Delimited;यदि आप कॉल करते हैं तो सेटिंग आवश्यक नहीं है parser.SetDelimiters(",");, क्योंकि विधि TextFieldTypeआपके लिए संपत्ति सेट करती है ।
ब्रायन

10
इसे भी देखें: dotnetperls.com/textfieldparser । TextFieldParser में String.Split और StreamReader की तुलना में खराब प्रदर्शन है। हालांकि, string.Split और TextFieldParser के बीच एक बड़ा अंतर है। TextFieldParser एक कॉलम में अल्पविराम रखने जैसे अजीब मामलों को संभालता है: आप एक स्तंभ को नाम दे सकते हैं "text with quote"", and comma", और आप text with quote", and commaगलत तरीके से अलग किए गए मानों के बजाय सही मान प्राप्त कर सकते हैं । यदि आप सीएसवी बहुत सरल है, तो आप स्ट्रिंग के लिए चयन कर सकते हैं।
योंगवेई वू

5
ध्यान दें कि इसका उपयोग करने के लिए आपको Microsoft.VisualBasic का संदर्भ जोड़ना होगा। Visual Studio में अपने प्रोजेक्ट पर राइट-क्लिक करें, फिर Add> Reference चुनें, और Microsoft.VisualBasic के बॉक्स को चेक करें।
डेरेक कुर्थ

37

मेरा अनुभव है कि कई अलग-अलग सीएसवी प्रारूप हैं। विशेष रूप से वे एक क्षेत्र के भीतर उद्धरण और सीमांकक से बचने के लिए कैसे संभालते हैं।

ये वे संस्करण हैं जिन्हें मैंने भाग लिया है:

  • उद्धरण उद्धृत और दोगुने (एक्सेल) हैं अर्थात 15 "-> फ़ील्ड 1," 15 "" ", फ़ील्ड 3
  • जब तक क्षेत्र किसी अन्य कारण से उद्धृत नहीं किया जाता है तब तक उद्धरण नहीं बदले जाते हैं। यानी 15 "-> फ़ील्ड 1,15", फ़ील्ड 3
  • उद्धरण \ _ से बच गए हैं। यानी 15 "-> फ़ील्ड 1," 15 \ "", फ़ील्ड 3
  • उद्धरण बिल्कुल नहीं बदले गए हैं (यह हमेशा सही ढंग से पार्स करने के लिए संभव नहीं है)
  • सीमांकक (एक्सेल) उद्धृत है। यानी ए, बी -> फील्ड 1, "ए, बी", फील्ड 3
  • सीमांकक के साथ बच जाता है। यानी ए, बी -> फील्ड 1, ए, बी, फील्ड 3

मैंने कई मौजूदा सीएसवी पार्सर्स की कोशिश की है, लेकिन एक भी ऐसा नहीं है जो मेरे द्वारा चलाए गए वेरिएंट को संभाल सकता है। यह भी प्रलेखन से पता लगाना मुश्किल है जो पार्सर्स के समर्थन से बचते हैं।

अपनी परियोजनाओं में मैं अब VB TextFieldParser या एक कस्टम स्प्लिटर का उपयोग करता हूं।


1
आपके द्वारा प्रदान किए गए परीक्षण मामलों के लिए इस उत्तर से प्यार करें!
मैथ्यू रोडेटस

2
मुख्य समस्या यह है कि अधिकांश कार्यान्वयन RFC 4180 के बारे में परवाह नहीं करते हैं जो CSV प्रारूप का वर्णन करता है और कैसे सीमांकक से बच जाना चाहिए।
जेनी ओ'रिली ने

RFC-4180 2005 का है, जो अब पुराना लगता है, लेकिन याद रखें: 2001 में .NET नेट फ्रेमवर्क पहली बार आउट हुआ था। इसके अलावा, RFC हमेशा आधिकारिक मानक नहीं होते हैं, और इस मामले में यह उतना वजन नहीं रखता है, जैसा कि कहें , ISO-8601 या RFC-761।
जोएल कोएहॉर्न

23

मैं Nuget से CsvHelper की सलाह देता हूं

(Microsoft.VisualBasic के संदर्भ को जोड़ने से यह सही नहीं लगता, यह केवल बदसूरत नहीं है, यह संभवतः क्रॉस-प्लेटफॉर्म के तहत नहीं है।)


2
यह बिल्कुल क्रॉस प्लेटफॉर्म है जैसा कि C # है।
PRMan

: गलत, लिनक्स में Microsoft.VisualBasic.dll मोनो स्रोतों, माइक्रोसॉफ्ट के तुलना में एक अलग कार्यान्वयन है जो और वहाँ कुछ चीजें हैं जो लागू नहीं किया जाता है, उदाहरण के लिए से आता है stackoverflow.com/questions/6644165/...
knocte

(प्लस, वीबी भाषा का कभी भी उन कंपनियों पर ध्यान नहीं दिया गया है जो मोनो परियोजना को बनाने / विकसित करने में शामिल रही हैं, इसलिए C # इकोसिस्टम / टूलिंग की तुलना में प्रयासों के मामले में यह पीछे है।)
knocte

1
दोनों के साथ खेलने के बाद, मैं जोड़ दूंगा कि CsvHelperएक निर्मित पंक्ति में वर्ग मैपर के साथ आता है; यह कॉलम हेडर (यदि मौजूद है) में भिन्नता के लिए अनुमति देता है, और यहां तक ​​कि कॉलम क्रम में स्पष्ट रूप से भिन्नताएं (हालांकि मैंने बाद में खुद का परीक्षण नहीं किया है)। सब सब में यह "की तुलना में अधिक" उच्च स्तर लगता है TextFieldParser
डेविड

1
हाँ, Microsoft.VisualBasic नाम स्थान .NET कोर 2.1 पर उपलब्ध नहीं है
N4ppeL

13

कभी-कभी पुस्तकालयों का उपयोग करना तब शांत होता है जब आप पहिये को सुदृढ़ नहीं करना चाहते हैं, लेकिन इस मामले में एक ही काम कोड की कम लाइनों के साथ कर सकते हैं और पुस्तकालयों का उपयोग करने की तुलना में पढ़ने में आसान है। यहाँ एक अलग दृष्टिकोण है जिसका उपयोग करना मुझे बहुत आसान लगता है।

  1. इस उदाहरण में, मैं फाइल को पढ़ने के लिए StreamReader का उपयोग करता हूं
  2. प्रत्येक लाइन (रेखाओं) से सीमांकक का पता लगाने के लिए रेगेक्स।
  3. सूचकांक 0 से n तक कॉलम एकत्र करने की एक सरणी

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
निश्चित रूप से उस डेटा के साथ समस्याएं हैं जिसमें खुद नई लाइनें शामिल हैं?
डोगल

अब CSV डेटाफ़ाइल्स को डेटा के बीच खाली लाइनों को शामिल करने के लिए नहीं जाना जाता है, लेकिन अगर आपके पास कोई ऐसा स्रोत है जो ऐसा करता है, तो उस स्थिति में मैं सिर्फ सरल रीजेक्स टेस्ट करूँगा ताकि रीडर चलाने से पहले व्हाट्सएप या लाइनों को हटा दिया जाए। विभिन्न उदाहरणों के लिए यहां देखें: stackoverflow.com/questions/7647716/…
मान

1
रेगेक्स की तुलना में निश्चित रूप से इस तरह की समस्या के लिए चार-आधारित दृष्टिकोण अधिक स्वाभाविक है। उद्धरण चिह्नों की उपस्थिति के आधार पर व्यवहार भिन्न माना जाता है।
केसी

6

CSV जटिल वास्तविक तेज़ प्राप्त कर सकता है ।

कुछ मजबूत और अच्छी तरह से जांच की गई का उपयोग करें:
FileHelpers: www.filehelpers.net

FileHelpers एक निशुल्क और आसान है। .NET लाइब्रेरी का उपयोग फाइलों, तारों या धाराओं में निश्चित लंबाई या सीमांकित रिकॉर्ड से डेटा आयात / निर्यात करने के लिए किया जाता है।


5
मुझे लगता है कि FileHelper एक बार में बहुत कुछ करने की कोशिश कर रहा है। पार्सिंग फाइलें एक 2 कदम प्रक्रिया है जहां आप पहली बार लाइनों को खेतों में विभाजित करते हैं और फिर डेटा में फ़ील्ड को पार्स करते हैं। कार्यों के संयोजन से मास्टर-डिटेल और लाइन फ़िल्टरिंग जैसी चीजों को संभालना मुश्किल हो जाता है।
एड्रियन


4

इस सूची में एक और, सेंचू ईटीएल - सीएसवी फाइलें पढ़ने और लिखने के लिए एक खुला स्रोत पुस्तकालय है

नीचे एक नमूना CSV फ़ाइल के लिए

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);
   }
}

यदि आपके पास CSV फ़ाइल से मेल खाता POCO वर्ग है

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

आप इसे नीचे दिए गए CSV फ़ाइल को लोड करने के लिए उपयोग कर सकते हैं

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

कृपया इसका उपयोग करने के बारे में CodeProject पर लेख देखें ।

अस्वीकरण: मैं इस पुस्तकालय का लेखक हूं


नमस्ते, क्या आप सीएसवी को एसक्यूएल टेबल पर लोड कर सकते हैं - मुझे हाथ से पहले सीएसवी तालिका में हेडर नहीं पता है। बस सीएसवी में एससीएल टेबल पर दर्पण दर्पण
एगी

हाँ तुम कर सकते हो। कृपया इस लिंक को देखें 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) अगली "टेबल" पंक्ति है।
  2. "टेबल" कोशिकाओं को कुछ सीमांकक प्रतीक द्वारा अलग किया जाता है। सबसे अधिक इस्तेमाल किया प्रतीक है \tया,
  3. हर सेल में संभवतः यह सीमांकक चिह्न हो सकता है (सेल को उद्धरण चिह्न के साथ शुरू करना चाहिए और इस मामले में इस प्रतीक के साथ समाप्त होना चाहिए)
  4. हर सेल में संभवतः /r/nचिन्ह हो सकते हैं (सेल को उद्धरण चिन्ह के साथ शुरू करना चाहिए और इस मामले में इस प्रतीक के साथ समाप्त होना चाहिए)

CSV फ़ाइलों के साथ काम करने के लिए C # / Visual Basic का सबसे आसान तरीका मानक Microsoft.VisualBasicपुस्तकालय का उपयोग करना है। आपको केवल आवश्यक संदर्भ और अपनी कक्षा में निम्नलिखित स्ट्रिंग जोड़ने की आवश्यकता है:

using Microsoft.VisualBasic.FileIO;

हां, आप इसे C # में उपयोग कर सकते हैं, चिंता न करें। यह लाइब्रेरी अपेक्षाकृत बड़ी फ़ाइलों को पढ़ सकती है और सभी आवश्यक नियमों का समर्थन करती है, इसलिए आप सभी CSV फ़ाइलों के साथ काम करने में सक्षम होंगे।

कुछ समय पहले मैंने इस पुस्तकालय पर आधारित सीएसवी पढ़ने / लिखने के लिए सरल वर्ग लिखा था। इस सरल वर्ग का उपयोग करके आप 2 आयाम सरणी के साथ CSV के साथ काम कर पाएंगे। आप निम्न लिंक द्वारा मेरी कक्षा पा सकते हैं: 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

पिछले उत्तरों को पूरा करने के लिए, किसी को अपनी CSV फ़ाइल से वस्तुओं के संग्रह की आवश्यकता हो सकती है, या तो विधि TextFieldParserया string.Splitविधि द्वारा पार्स किया गया है , और फिर प्रत्येक रेखा परावर्तन के माध्यम से किसी वस्तु में परिवर्तित हो जाती है। आपको स्पष्ट रूप से पहले एक वर्ग को परिभाषित करने की आवश्यकता है जो सीएसवी फ़ाइल की रेखाओं से मेल खाता है।

मैंने माइकल क्रॉपट से सरल CSV सीरियल का उपयोग किया, जो यहां पाया गया: CSV को जेनेरिक वर्ग (सभी गुण) और कामना वर्ग के क्षेत्र और गुण प्राप्त करने के लिए अपने तरीकों का पुन: उपयोग किया।

मैं अपनी CSV फ़ाइल को निम्न विधि से डिस्क्राइबलाइज़ करता हूँ:

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.