.Net [बंद] में डेटा संरचना को दृढ़ता से टाइप करने के लिए CSV फ़ाइल आयात करें


106

एक सीएसवी फ़ाइल को दृढ़ता से टाइप किए गए डेटा संरचना में आयात करने का सबसे अच्छा तरीका क्या है?




7
यह देखते हुए कि 1103495 से एक साल पहले बनाया गया था, मुझे लगता है कि सवाल इस एक का एक डुप्लिकेट है।
मैथ

2
धन्यवाद, मैट। मैं बस उन्हें एक साथ जोड़ने की कोशिश कर रहा था, यह इंगित नहीं करता था कि कौन सा पहले आया था। आप देखेंगे कि मेरे पास इस प्रश्न पर इंगित करने वाले अन्य प्रश्न पर बिल्कुल वही पाठ है। क्या दो प्रश्नों को एक साथ बाँधने का एक बेहतर तरीका है?
मार्क मेउर

जवाबों:


74

Microsoft का TextFieldParser स्थिर है और CSV फ़ाइलों के लिए RFC 4180 का अनुसरण करता है। Microsoft.VisualBasicनामस्थान से मत हटाओ ; यह .NET फ्रेमवर्क में एक मानक घटक है, बस वैश्विक Microsoft.VisualBasicअसेंबली के लिए एक संदर्भ जोड़ें ।

यदि आप विंडोज के लिए संकलन कर रहे हैं (मोनो के विपरीत) और "टूटी हुई" (गैर-आरएफसी-अनुपालन) सीएसवी फाइलों को पार्स करने का अनुमान नहीं लगाते हैं, तो यह स्पष्ट विकल्प होगा, क्योंकि यह मुफ्त, अप्रतिबंधित, स्थिर है। और सक्रिय रूप से समर्थित, जिनमें से अधिकांश FileHelpers के लिए नहीं कहा जा सकता है।

इसे भी देखें: वीबी कोड उदाहरण के लिए विजुअल बेसिक में कोमा-डिलीटेड टेक्स्ट फाइल्स से कैसे पढ़ें:


2
वास्तव में इस वर्ग के बारे में वीबी-विशिष्ट कुछ भी नहीं है जो इसके दुर्भाग्य से नामित नामस्थान के अलावा है। अगर मुझे केवल "सरल" CSV पार्सर की आवश्यकता है, तो मैं निश्चित रूप से इस लाइब्रेरी को चुनूंगा, क्योंकि सामान्य रूप से डाउनलोड करने, वितरित करने या चिंता करने की कोई बात नहीं है। उस अंत तक मैंने इस उत्तर से VB- केंद्रित फंतासिंग को संपादित किया है।
हारून

@ मुझे लगता है कि आपके संपादन ज्यादातर सुधार हैं। हालाँकि RFC आवश्यक रूप से आधिकारिक नहीं है, क्योंकि कई CSV लेखक इसका अनुपालन नहीं करते हैं जैसे कि Excel हमेशा "CSV" फ़ाइलों में अल्पविराम का उपयोग नहीं करता है । इसके अलावा मेरे पहले वाले उत्तर ने यह नहीं कहा कि कक्षा का उपयोग C # से किया जा सकता है?
MarkJ

TextFieldParserभी टैब-सीमांकित और अन्य अजीब एक्सेल-उत्पन्न cruft के लिए इच्छा काम करते हैं। मुझे पता है कि आपका पिछला उत्तर यह दावा नहीं कर रहा था कि पुस्तकालय VB- विशिष्ट था, यह सिर्फ मेरे लिए यह कहकर आया था कि यह वास्तव में VB के लिए था , और C # से उपयोग करने का इरादा नहीं था , जो मुझे नहीं लगता। मामला - MSVB में कुछ वास्तव में उपयोगी कक्षाएं हैं।
हारून को

21

एक OleDB कनेक्शन का उपयोग करें।

String sConnectionString = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=C:\\InputDirectory\\;Extended Properties='text;HDR=Yes;FMT=Delimited'";
OleDbConnection objConn = new OleDbConnection(sConnectionString);
objConn.Open();
DataTable dt = new DataTable();
OleDbCommand objCmdSelect = new OleDbCommand("SELECT * FROM file.csv", objConn);
OleDbDataAdapter objAdapter1 = new OleDbDataAdapter();
objAdapter1.SelectCommand = objCmdSelect;
objAdapter1.Fill(dt);
objConn.Close();

इसके लिए फ़ाइल सिस्टम एक्सेस की आवश्यकता होती है। जहाँ तक मुझे पता है कि इन-मेमोरी स्ट्रीम्स के साथ OLEDB काम करने का कोई तरीका नहीं है :(
UserControl

3
@UserControl, निश्चित रूप से इसके लिए फ़ाइल सिस्टम एक्सेस की आवश्यकता है। उन्होंने एक CSV फ़ाइल
केविन

1
मैं शिकायत नहीं कर रहा हूँ। वास्तव में मैं बाकी पर OLEDB समाधान पसंद करेंगे, लेकिन मैं कई बार निराश था जब ASP.NET अनुप्रयोगों में CSV को पार्स करने की आवश्यकता थी इसलिए इसे नोट करना चाहता था।
यूजरकंट्रोल

12

यदि आप CSV पार्सिंग के लिए काफी जटिल परिदृश्यों की अपेक्षा कर रहे हैं, तो हमारे स्वयं के पार्सर को रोल करने के बारे में भी न सोचें । वहाँ बहुत सारे उत्कृष्ट उपकरण हैं, जैसे कि FileHelpers , या यहां तक ​​कि CodeProject से भी ।

मुद्दा यह एक काफी सामान्य समस्या है और आप शर्त लगा सकते हैं कि बहुत सारे सॉफ़्टवेयर डेवलपर्स ने पहले से ही इस समस्या के बारे में सोचा और हल किया है।


हालांकि यह लिंक प्रश्न का उत्तर दे सकता है, लेकिन उत्तर के आवश्यक हिस्सों को यहां शामिल करना और संदर्भ के लिए लिंक प्रदान करना बेहतर है। लिंक-केवल उत्तर अमान्य हो सकते हैं यदि लिंक किए गए पृष्ठ बदल जाते हैं। - समीक्षा से
तकनीक

धन्यवाद @techspider मुझे आशा है कि आपने ध्यान दिया है कि यह पोस्ट StackOverflow के बीटा काल से थी: D कहा जा रहा है कि आजकल CSV टूल Nuget पैकेज से बेहतर बनाए गए हैं - इसलिए मुझे यकीन नहीं है कि लिंक उत्तर भी 8-वर्ष से प्रतिरक्षा कर रहे हैं
-ऑनलाइन डेवलपमेंट

9

ब्रायन इसे एक जोरदार टाइप संग्रह में परिवर्तित करने के लिए एक अच्छा समाधान देता है।

दिए गए अधिकांश CSV पार्सिंग विधियों में से बचने वाले खेतों या CSV फ़ाइलों की कुछ अन्य सूक्ष्मताओं (जैसे ट्रिमिंग फ़ील्ड) को ध्यान में नहीं रखा जाता है। यहां वह कोड है जिसका मैं व्यक्तिगत रूप से उपयोग करता हूं। यह किनारों के आसपास थोड़ा मोटा है और इसमें बहुत ज्यादा त्रुटि नहीं है।

public static IList<IList<string>> Parse(string content)
{
    IList<IList<string>> records = new List<IList<string>>();

    StringReader stringReader = new StringReader(content);

    bool inQoutedString = false;
    IList<string> record = new List<string>();
    StringBuilder fieldBuilder = new StringBuilder();
    while (stringReader.Peek() != -1)
    {
        char readChar = (char)stringReader.Read();

        if (readChar == '\n' || (readChar == '\r' && stringReader.Peek() == '\n'))
        {
            // If it's a \r\n combo consume the \n part and throw it away.
            if (readChar == '\r')
            {
                stringReader.Read();
            }

            if (inQoutedString)
            {
                if (readChar == '\r')
                {
                    fieldBuilder.Append('\r');
                }
                fieldBuilder.Append('\n');
            }
            else
            {
                record.Add(fieldBuilder.ToString().TrimEnd());
                fieldBuilder = new StringBuilder();

                records.Add(record);
                record = new List<string>();

                inQoutedString = false;
            }
        }
        else if (fieldBuilder.Length == 0 && !inQoutedString)
        {
            if (char.IsWhiteSpace(readChar))
            {
                // Ignore leading whitespace
            }
            else if (readChar == '"')
            {
                inQoutedString = true;
            }
            else if (readChar == ',')
            {
                record.Add(fieldBuilder.ToString().TrimEnd());
                fieldBuilder = new StringBuilder();
            }
            else
            {
                fieldBuilder.Append(readChar);
            }
        }
        else if (readChar == ',')
        {
            if (inQoutedString)
            {
                fieldBuilder.Append(',');
            }
            else
            {
                record.Add(fieldBuilder.ToString().TrimEnd());
                fieldBuilder = new StringBuilder();
            }
        }
        else if (readChar == '"')
        {
            if (inQoutedString)
            {
                if (stringReader.Peek() == '"')
                {
                    stringReader.Read();
                    fieldBuilder.Append('"');
                }
                else
                {
                    inQoutedString = false;
                }
            }
            else
            {
                fieldBuilder.Append(readChar);
            }
        }
        else
        {
            fieldBuilder.Append(readChar);
        }
    }
    record.Add(fieldBuilder.ToString().TrimEnd());
    records.Add(record);

    return records;
}

ध्यान दें कि यह खेतों के किनारे के मामले को दोहरे उद्धरणों से कम नहीं किया जा रहा है, लेकिन इसके अंदर एक उद्धृत स्ट्रिंग है। देखें इस पोस्ट के लिए एक बेहतर expanation का एक सा के लिए और साथ ही कुछ उचित पुस्तकालयों के लिए कुछ लिंक।


9

मैं @ NotMyself से सहमत हूं । FileHelpers का अच्छी तरह से परीक्षण किया जाता है और सभी प्रकार के किनारे के मामलों को संभालता है जो आपको अंततः खुद से करना होगा। FileHelpers क्या करता है, उस पर एक नज़र डालें और केवल अपना ही लिखें अगर आपको पूरा यकीन है कि या तो (1) आपको कभी भी किनारे के मामलों को संभालने की आवश्यकता नहीं होगी, FileHelpers करता है, या (2) आप इस तरह का सामान लिखना पसंद करते हैं और करने जा रहे हैं जब आपको इस तरह सामान को पार्स करना हो तो बहुत ज्यादा खुश हो:

1, "बिल", "स्मिथ", "पर्यवेक्षक", "नो कमेंट"

2, 'ड्रेक,', 'ओ'माली', '' जेनेटर,

उफ़, मैं उद्धृत नहीं हूँ और मैं एक नई लाइन पर हूँ!


6

मैं बोर हो गया था इसलिए मैंने कुछ सामान को संशोधित किया जो मैंने लिखा था। यह फ़ाइल के माध्यम से पुनरावृत्तियों की मात्रा में कटौती करते हुए एक OO तरीके से पार्सिंग को एनकैप्सुलेट करने की कोशिश करता है, यह केवल एक बार शीर्ष फ़ॉर्चेट पर पुनरावृत्ति करता है।

using System;

using System.Collections.Generic;

using System.Linq;

using System.Text;

using System.IO;

namespace ConsoleApplication1
{
    class Program
    {

        static void Main(string[] args)
        {

            // usage:

            // note this wont run as getting streams is not Implemented

            // but will get you started

            CSVFileParser fileParser = new CSVFileParser();

            // TO Do:  configure fileparser

            PersonParser personParser = new PersonParser(fileParser);

            List<Person> persons = new List<Person>();
            // if the file is large and there is a good way to limit
            // without having to reparse the whole file you can use a 
            // linq query if you desire
            foreach (Person person in personParser.GetPersons())
            {
                persons.Add(person);
            }

            // now we have a list of Person objects
        }
    }

    public abstract  class CSVParser 
    {

        protected String[] deliniators = { "," };

        protected internal IEnumerable<String[]> GetRecords()
        {

            Stream stream = GetStream();
            StreamReader reader = new StreamReader(stream);

            String[] aRecord;
            while (!reader.EndOfStream)
            {
                  aRecord = reader.ReadLine().Split(deliniators,
                   StringSplitOptions.None);

                yield return aRecord;
            }

        }

        protected abstract Stream GetStream(); 

    }

    public class CSVFileParser : CSVParser
    {
        // to do: add logic to get a stream from a file

        protected override Stream GetStream()
        {
            throw new NotImplementedException();
        } 
    }

    public class CSVWebParser : CSVParser
    {
        // to do: add logic to get a stream from a web request

        protected override Stream GetStream()
        {
            throw new NotImplementedException();
        }
    }

    public class Person
    {
        public String Name { get; set; }
        public String Address { get; set; }
        public DateTime DOB { get; set; }
    }

    public class PersonParser 
    {

        public PersonParser(CSVParser parser)
        {
            this.Parser = parser;
        }

        public CSVParser Parser { get; set; }

        public  IEnumerable<Person> GetPersons()
        {
            foreach (String[] record in this.Parser.GetRecords())
            {
                yield return new Person()
                {
                    Name = record[0],
                    Address = record[1],
                    DOB = DateTime.Parse(record[2]),
                };
            }
        }
    }
}


2

इसका एक अच्छा सरल तरीका यह है कि फ़ाइल को खोलें, और प्रत्येक पंक्ति को एक सरणी, लिंक की गई सूची, डेटा-संरचना-की-अपनी पसंद में पढ़ें। हालांकि पहली पंक्ति को संभालने के बारे में सावधान रहें।

यह आपके सिर पर हो सकता है, लेकिन कनेक्शन स्ट्रिंग का उपयोग करके उन्हें एक्सेस करने का एक सीधा तरीका प्रतीत होता है ।

C # या VB के बजाय पायथन का उपयोग करने की कोशिश क्यों नहीं की गई? यह आयात करने के लिए एक अच्छा CSV मॉड्यूल है जो आपके लिए सभी भारी उठाने का काम करता है।


1
CSV पार्सर की खातिर VB से अजगर पर न चढ़ें। VB में एक है। हालांकि अजीब तरह से इस सवाल के जवाब में इसे नजरअंदाज किया गया लगता है। msdn.microsoft.com/en-us/library/…
MarkJ

1

मुझे इस गर्मी में एक प्रोजेक्ट के लिए .NET में CSV पार्सर का उपयोग करना पड़ा और Microsoft Jet टेक्स्ट ड्राइवर पर बस गया। आप कनेक्शन स्ट्रिंग का उपयोग करके एक फ़ोल्डर निर्दिष्ट करते हैं, फिर SQL चयन कथन का उपयोग करके किसी फ़ाइल को क्वेरी करते हैं। आप स्कीमा.इन फ़ाइल का उपयोग करके मजबूत प्रकार निर्दिष्ट कर सकते हैं। मैंने पहले ऐसा नहीं किया था, लेकिन तब मुझे बुरे परिणाम मिल रहे थे, जहां डेटा का प्रकार तुरंत स्पष्ट नहीं था, जैसे कि आईपी नंबर या "XYQ 3.9 SP1" जैसी प्रविष्टि।

एक सीमा मैं भाग गया है कि यह 64 अक्षरों से ऊपर स्तंभ नामों को संभाल नहीं सकता है; यह छोटा है। यह एक समस्या नहीं होनी चाहिए, सिवाय इसके कि मैं बहुत खराब तरीके से तैयार किए गए इनपुट डेटा के साथ काम कर रहा था। यह ADO.NET डेटासेट लौटाता है।

यह सबसे अच्छा समाधान था जो मैंने पाया। मैं अपने स्वयं के CSV पार्सर को रोल करने से सावधान रहूंगा, क्योंकि मैं शायद कुछ अंत के मामलों को याद करूंगा, और मुझे .NET के लिए कोई अन्य मुफ्त सीएसवी पार्सिंग पैकेज नहीं मिला।

संपादित करें: इसके अलावा, प्रति निर्देशिका में केवल एक स्कीमा.इन फ़ाइल हो सकती है, इसलिए मैंने आवश्यक कॉलमों को दृढ़ता से टाइप करने के लिए इसे गतिशील रूप से जोड़ा है। यह केवल निर्दिष्ट कॉलम को दृढ़ता से टाइप करेगा, और किसी अनिर्दिष्ट क्षेत्र के लिए अनुमान लगाएगा। मैं वास्तव में इसकी सराहना करता था, क्योंकि मैं एक तरल पदार्थ 70+ कॉलम CSV के साथ काम कर रहा था और प्रत्येक स्तंभ को निर्दिष्ट नहीं करना चाहता था, केवल दुर्व्यवहार करने वाले।


CSB पार्सर में VB.NET क्यों नहीं बनाया गया? msdn.microsoft.com/en-us/library/…
MarkJ

1

मैंने कुछ कोड टाइप किए। डेटाग्रिडव्यूअर में परिणाम अच्छा लगा। यह वस्तुओं की एक सरणी सूची के लिए पाठ की एक पंक्ति को पार्स करता है।

    enum quotestatus
    {
        none,
        firstquote,
        secondquote
    }
    public static System.Collections.ArrayList Parse(string line,string delimiter)
    {        
        System.Collections.ArrayList ar = new System.Collections.ArrayList();
        StringBuilder field = new StringBuilder();
        quotestatus status = quotestatus.none;
        foreach (char ch in line.ToCharArray())
        {                                
            string chOmsch = "char";
            if (ch == Convert.ToChar(delimiter))
            {
                if (status== quotestatus.firstquote)
                {
                    chOmsch = "char";
                }                         
                else
                {
                    chOmsch = "delimiter";                    
                }                    
            }

            if (ch == Convert.ToChar(34))
            {
                chOmsch = "quotes";           
                if (status == quotestatus.firstquote)
                {
                    status = quotestatus.secondquote;
                }
                if (status == quotestatus.none )
                {
                    status = quotestatus.firstquote;
                }
            }

            switch (chOmsch)
            {
                case "char":
                    field.Append(ch);
                    break;
                case "delimiter":                        
                    ar.Add(field.ToString());
                    field.Clear();
                    break;
                case "quotes":
                    if (status==quotestatus.firstquote)
                    {
                        field.Clear();                            
                    }
                    if (status== quotestatus.secondquote)
                    {                                                                           
                            status =quotestatus.none;                                
                    }                    
                    break;
            }
        }
        if (field.Length != 0)            
        {
            ar.Add(field.ToString());                
        }           
        return ar;
    }

0

यदि आप यह गारंटी दे सकते हैं कि डेटा में कोई अल्पविराम नहीं है, तो सबसे सरल तरीका शायद String.split का उपयोग करना होगा ।

उदाहरण के लिए:

String[] values = myString.Split(',');
myObject.StringField = values[0];
myObject.IntField = Int32.Parse(values[1]);

आपकी सहायता के लिए उपयोग की जा सकने वाली लाइब्रेरी हो सकती हैं, लेकिन यह संभवतः उतना ही सरल है जितना आप प्राप्त कर सकते हैं। बस सुनिश्चित करें कि आपके पास डेटा में कॉमा नहीं हो सकता है, अन्यथा आपको इसे बेहतर पार्स करने की आवश्यकता होगी।


यह एक इष्टतम समाधान नहीं है
गोल चक्कर

मेमोरी के उपयोग और ओवरहेड के बहुत खराब होने पर। छोटे को कुछ किलोबाइट का कम धन्यवाद करना चाहिए। निश्चित रूप से 10mb सीएसवी के लिए अच्छा नहीं है!
पायोटर कुला

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