2008-08-05 17 views
97

मुझे एहसास है कि यह एक नौसिखिया सवाल है, लेकिन मैं एक साधारण समाधान की तलाश में हूं - ऐसा लगता है कि ऐसा होना चाहिए।सीएसवी फ़ाइल आयात .Net

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

+0

यह http://stackoverflow.com/questions/1103495/is-there-a-proper-way-to-read-csv-files –

+7

का डुप्लिकेट होना माना जाता है इस 1,103,495 से बनाया गया था एक साल पहले , मुझे लगता है कि सवाल इस का एक डुप्लिकेट है। – MattH

+2

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

उत्तर

48

FileHelpers Open Source Library देखें।

+1

दुर्भाग्य से यह एलजीपीएल है, जो कॉर्पोरेट वातावरण में आदर्श से कम है ... –

+5

@ जॉन, आप ऐसा क्यों कहते हैं?एलजीपीएल को आपको तब तक कोई कोड जारी करने की आवश्यकता नहीं है जब तक आप लाइब्रेरी को संशोधित न करें। (इस मामले में वैसे भी एक पैच जमा करना समझ में आता है।) –

+0

+1 बस इसे कार्यान्वित किया गया ... भयानक –

2

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

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

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

+1

एक सीएसवी पार्सर के लिए वीबी से पायथन पर कूद न करें। वीबी में एक है। हालांकि अजीब बात यह है कि इस सवाल के जवाब में अनदेखा किया गया है। http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.fileio.textfieldparser.aspx – MarkJ

0

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

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

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

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

+0

यह इष्टतम समाधान – roundcrisis

+0

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

+0

यह आपकी स्मृति और फ़ाइल के आकार पर निर्भर करता है। – tonymiao

5

कोडप्रोजेक्ट पर दो लेख हैं जो समाधान के लिए कोड प्रदान करते हैं, जो StreamReader का उपयोग करता है और imports CSV dataMicrosoft Text Driver का उपयोग करता है।

6

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

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]), 
       }; 
      } 
     } 
    } 
} 
9

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

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

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

ध्यान दें कि यह नहीं दोहरे उद्धरण चिह्नों द्वारा deliminated किया जा रहा क्षेत्रों के किनारे मामले को संभाल नहीं है, लेकिन meerley इसके अंदर एक उद्धृत स्ट्रिंग हो रही है। एक बेहतर विस्तार के साथ-साथ कुछ उचित पुस्तकालयों के कुछ लिंक के लिए this post देखें।

1

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

एक सीमा जिसमें मैंने भाग लिया वह यह है कि यह 64 वर्णों के ऊपर कॉलम नामों को संभाल नहीं सकता है; यह truncates। यह एक समस्या नहीं होनी चाहिए, सिवाय इसके कि मैं बहुत खराब डिजाइन किए गए इनपुट डेटा से निपट रहा था। यह एक एडीओ.NET डेटासेट देता है।

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

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

+0

सीएसवी पार्सर में निर्मित वीबी.नेट क्यों नहीं? http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.fileio.textfieldparser.aspx – MarkJ

12

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

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

+0

हालांकि यह लिंक प्रश्न का उत्तर दे सकता है, लेकिन यहां उत्तर के आवश्यक हिस्सों को शामिल करना बेहतर है और लिंक प्रदान करना बेहतर है सन्दर्भ के लिए। लिंक किए गए पृष्ठ में परिवर्तन होने पर लिंक-केवल उत्तर अमान्य हो सकते हैं। - [समीक्षा से] (/ समीक्षा/निम्न गुणवत्ता वाले पदों/13623136) – techspider

+0

धन्यवाद @techspider मुझे आशा है कि आपने यह नोट किया है कि यह पोस्ट स्टैक ओवरफ्लो की बीटा अवधि से थी: डी कहा जा रहा है कि आजकल सीएसवी टूल्स नूजेट पैकेज से बेहतर हैं - इसलिए मुझे यकीन नहीं है कि यहां तक ​​कि लिंक उत्तर भी –

9

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

1, "बिल", "स्मिथ", "पर्यवेक्षक", "कोई टिप्पणी"

2, 'ड्रेक,', 'ओ' मैली ', "चौकीदार

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

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(); 
+0

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

+1

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

+1

आयात करने के बारे में पूछा, मैं शिकायत नहीं कर रहा हूं। असल में मैं ' डी बाकी हिस्सों पर ओएलडीडीबी समाधान पसंद करते हैं लेकिन एएसपी.NET अनुप्रयोगों में सीएसवी को पार्स करने के लिए आवश्यक होने पर मुझे निराशा होती है, इसलिए इसे नोट करना चाहता था। – UserControl

72

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

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

यह भी देखें: How to: Read From Comma-Delimited Text Files in Visual Basic एक वीबी कोड उदाहरण के लिए।

+2

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

+0

@Aaronaught I लगता है कि आपके संपादन ज्यादातर एक सुधार हैं।हालांकि आरएफसी अनिवार्य रूप से आधिकारिक नहीं है, क्योंकि कई सीएसवी लेखकों का पालन नहीं करते हैं उदा। एक्सेल [हमेशा अल्पविराम का उपयोग नहीं करता है] (http://office.microsoft.com/en-us/excel-help/import-or-export-text-txt-or-csv-files-HP010099725.aspx#BMchange_the_separator_in_all_। csv_text) "सीएसवी" फाइलों में। मेरे पिछले जवाब में पहले से ही यह नहीं कहा गया था कि कक्षा सी # से उपयोग की जा सकती है? – MarkJ

+0

'TextFieldParser' टैब-डिलीमिट और अन्य अजीब एक्सेल-जेनरेटेड क्रुफ़्ट के लिए भी काम करेगा। मुझे एहसास है कि आपका पिछला जवाब यह दावा नहीं कर रहा था कि लाइब्रेरी वीबी-विशिष्ट थी, यह सिर्फ मेरे पास आई थी कि यह वास्तव में * वीबी के लिए * मतलब था, और नहीं * इरादा * सी # से उपयोग किया जाना चाहिए, जिसे मैं नहीं करता ऐसा नहीं लगता है - एमएसवीबी में कुछ वास्तव में उपयोगी कक्षाएं हैं। – Aaronaught

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; 
    } 
संबंधित मुद्दे