c# - शीर्षलेख के साथ सी#में सीएसवी फाइलों को पार्स करना




csv file-io (11)

सी # में सीएसवी फाइलों का विश्लेषण करने के लिए कोई डिफ़ॉल्ट / आधिकारिक / अनुशंसित तरीका है? मैं अपना खुद का पार्सर रोल नहीं करना चाहता हूं।

साथ ही, मैंने टेक्स्ट ड्राइवर के माध्यम से सीएसवी पढ़ने के लिए ओडीबीसी / ओएलई डीबी का उपयोग करने वाले लोगों के उदाहरण देखे हैं, और बहुत से लोग इसकी "कमियां" के कारण इसे हतोत्साहित करते हैं। ये कमी क्या हैं?

आदर्श रूप से, मैं एक तरीका ढूंढ रहा हूं जिसके माध्यम से मैं शीर्षलेख / फ़ील्ड नामों के पहले रिकॉर्ड का उपयोग करके, स्तंभ नाम से सीएसवी पढ़ सकता हूं। दिए गए कुछ उत्तरों सही हैं लेकिन मूल रूप से कक्षाओं में फ़ाइल को deserialize करने के लिए काम करते हैं।


अगर आपको केवल सीएसवी फाइलों को पढ़ने की ज़रूरत है तो मैं इस पुस्तकालय की सिफारिश करता हूं: CSVReader
यदि आपको सीएसवी फाइलें भी उत्पन्न करने की आवश्यकता है तो इसे एक का उपयोग करें: FileHelpers

वे दोनों स्वतंत्र और opensource हैं।


इस सूची में एक और, सिंचू ईटीएल - एकाधिक फ़ाइल प्रारूपों को पढ़ने और लिखने के लिए एक ओपन सोर्स लाइब्रेरी (सीएसवी, फ्लैट फाइल, एक्सएमएल, जेएसओएन इत्यादि)

नीचे नमूना दिखाता है कि सीएसवी फ़ाइल को जल्दी से कैसे पढ़ा जाए (कोई पॉको ऑब्जेक्ट आवश्यक नहीं है)

static void ReadCSV()
{
    using (var stream = new MemoryStream())
    using (var reader = new StreamReader(stream))
    using (var writer = new StreamWriter(stream))
    using (var parser = new ChoCSVReader(reader))
    {
        writer.WriteLine("id,name");
        writer.WriteLine("1,Carl");
        writer.WriteLine("2,Mark");
        writer.WriteLine("3,Tom");

        writer.Flush();
        stream.Position = 0;

        foreach (dynamic dr in parser)
        {
            Console.WriteLine("Id: {0}, Name: {1}", dr.id, dr.name);
        }
    }
}

नीचे नमूना दिखाता है कि पीओसीओ ऑब्जेक्ट का उपयोग कर सीएसवी फ़ाइल को कैसे पढ़ा जाए

public partial class EmployeeRec
{
    public int Id { get; set; }
    public string Name { get; set; } 
}
static void ReadCSV()
{
    using (var stream = new MemoryStream())
    using (var reader = new StreamReader(stream))
    using (var writer = new StreamWriter(stream))
    using (var parser = new ChoCSVReader<EmployeeRec>(reader))
    {
        writer.WriteLine("id,name");
        writer.WriteLine("1,Carl");
        writer.WriteLine("2,Mark");
        writer.WriteLine("3,Tom");

        writer.Flush();
        stream.Position = 0;

        foreach (var dr in parser)
        {
            Console.WriteLine("Id: {0}, Name: {1}", dr.id, dr.name);
        }
    }
}

CodeProject पर इसका उपयोग कैसे करें इस पर लेख देखें।


एक सीएसवी पार्सर अब .NET Framework का हिस्सा है।

Microsoft.VisualBasic.dll में एक संदर्भ जोड़ें (सी # में ठीक काम करता है, नाम पर ध्यान न दें)

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

दस्तावेज़ यहां हैं - TextFieldParser क्लास


कुछ समय पहले मैंने Microsoft.VisualBasic आधार पर सीएसवी पढ़ने / लिखने के लिए सरल वर्ग लिखा था। विज़ुअल बेसिक लाइब्रेरी। इस सरल वर्ग का उपयोग करके आप 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");

हेडर पढ़ने के लिए केवल आपको csv.Rows[0] कोशिकाओं को पढ़ने की आवश्यकता है :)


मुझे पता नहीं है कि कोई आधिकारिक तरीका नहीं है, लेकिन आपको वास्तव में मौजूदा पुस्तकालयों का उपयोग करना चाहिए। यहां कोड कोड से वास्तव में उपयोगी पाया गया है:

CSVReader


मैंने .NET के लिए TinyCsvParser लिखा है, जो लगभग किसी भी CSV प्रारूप को पार्स करने के लिए सबसे तेज़ .NET पार्सर्स में से एक है और अत्यधिक कॉन्फ़िगर करने योग्य है।

इसे एमआईटी लाइसेंस के तहत जारी किया गया है:

आप इसे स्थापित करने के लिए NuGet का उपयोग कर सकते हैं। पैकेज प्रबंधक कंसोल में निम्न आदेश चलाएं।

PM> Install-Package TinyCsvParser

प्रयोग

कल्पना कीजिए कि हमारे पास उनके पहले नाम, अंतिम नाम और जन्मतिथि के साथ एक CSV फ़ाइल persons.csv में व्यक्तियों की सूची है।

FirstName;LastName;BirthDate
Philipp;Wagner;1986/05/12
Max;Musterman;2014/01/02

हमारे सिस्टम में संबंधित डोमेन मॉडल इस तरह दिख सकता है।

private class Person
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime BirthDate { get; set; }
}

TinyCsvParser का उपयोग करते समय आपको CSV डेटा में कॉलम और डोमेन मॉडल में संपत्ति के बीच मैपिंग को परिभाषित करना होगा।

private class CsvPersonMapping : CsvMapping<Person>
{

    public CsvPersonMapping()
        : base()
    {
        MapProperty(0, x => x.FirstName);
        MapProperty(1, x => x.LastName);
        MapProperty(2, x => x.BirthDate);
    }
}

और फिर हम सीएसवी डेटा को CsvParser साथ पार्स करने के लिए मैपिंग का उपयोग कर सकते हैं।

namespace TinyCsvParser.Test
{
    [TestFixture]
    public class TinyCsvParserTest
    {
        [Test]
        public void TinyCsvTest()
        {
            CsvParserOptions csvParserOptions = new CsvParserOptions(true, new[] { ';' });
            CsvPersonMapping csvMapper = new CsvPersonMapping();
            CsvParser<Person> csvParser = new CsvParser<Person>(csvParserOptions, csvMapper);

            var result = csvParser
                .ReadFromFile(@"persons.csv", Encoding.ASCII)
                .ToList();

            Assert.AreEqual(2, result.Count);

            Assert.IsTrue(result.All(x => x.IsValid));

            Assert.AreEqual("Philipp", result[0].Result.FirstName);
            Assert.AreEqual("Wagner", result[0].Result.LastName);

            Assert.AreEqual(1986, result[0].Result.BirthDate.Year);
            Assert.AreEqual(5, result[0].Result.BirthDate.Month);
            Assert.AreEqual(12, result[0].Result.BirthDate.Day);

            Assert.AreEqual("Max", result[1].Result.FirstName);
            Assert.AreEqual("Mustermann", result[1].Result.LastName);

            Assert.AreEqual(2014, result[1].Result.BirthDate.Year);
            Assert.AreEqual(1, result[1].Result.BirthDate.Month);
            Assert.AreEqual(1, result[1].Result.BirthDate.Day);
        }
    }
}

उपयोगकर्ता गाइड

एक पूर्ण उपयोगकर्ता मार्गदर्शिका यहां उपलब्ध है:


यह समाधान आधिकारिक माइक्रोसॉफ्ट का उपयोग कर रहा है। सीएसवी का विश्लेषण करने के लिए विज़ुअल बेसिक असेंबली।

लाभ:

  • delimiter भागने
  • हैडर अनदेखा करता है
  • ट्रिम रिक्त स्थान
  • टिप्पणियों को अनदेखा करें

कोड:

    using Microsoft.VisualBasic.FileIO;

    public static List<List<string>> ParseCSV (string csv)
    {
        List<List<string>> result = new List<List<string>>();


        // To use the TextFieldParser a reference to the Microsoft.VisualBasic assembly has to be added to the project. 
        using (TextFieldParser parser = new TextFieldParser(new StringReader(csv))) 
        {
            parser.CommentTokens = new string[] { "#" };
            parser.SetDelimiters(new string[] { ";" });
            parser.HasFieldsEnclosedInQuotes = true;

            // Skip over header line.
            //parser.ReadLine();

            while (!parser.EndOfData)
            {
                var values = new List<string>();

                var readFields = parser.ReadFields();
                if (readFields != null)
                    values.AddRange(readFields);
                result.Add(values);
            }
        }

        return result;
    }

यहां एक सहायक वर्ग है जिसका उपयोग मैं अक्सर करता हूं, अगर कोई भी इस धागे पर वापस आ जाता है (मैं इसे साझा करना चाहता था)।

मैं इसे उपयोग करने के लिए तैयार परियोजनाओं में पोर्टिंग की सादगी के लिए इसका उपयोग करता हूं:

public class CSVHelper : List<string[]>
{
  protected string csv = string.Empty;
  protected string separator = ",";

  public CSVHelper(string csv, string separator = "\",\"")
  {
    this.csv = csv;
    this.separator = separator;

    foreach (string line in Regex.Split(csv, System.Environment.NewLine).ToList().Where(s => !string.IsNullOrEmpty(s)))
    {
      string[] values = Regex.Split(line, separator);

      for (int i = 0; i < values.Length; i++)
      {
        //Trim values
        values[i] = values[i].Trim('\"');
      }

      this.Add(values);
    }
  }
}

और इसका उपयोग करें:

public List<Person> GetPeople(string csvContent)
{
  List<Person> people = new List<Person>();
  CSVHelper csv = new CSVHelper(csvContent);
  foreach(string[] line in csv)
  {
    Person person = new Person();
    person.Name = line[0];
    person.TelephoneNo = line[1];
    people.Add(person);
  }
  return people;
}

[अद्यतन सीएसवी सहायक: बग तय किया गया जहां अंतिम नई लाइन चरित्र ने एक नई लाइन बनाई]


लाइब्रेरी को आपके लिए सभी नट-किरकिरा विवरणों को संभालने दें! :-)

FileHelpers और DRY रहें - अपने आप को दोहराएं - पहिया को फिर से आविष्कार करने की कोई ज़रूरत नहीं है ....

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


सरल पार्सिंग आवश्यकताओं के लिए एकल स्रोत फ़ाइल समाधान, उपयोगी। सभी बुरा किनारे के मामलों के साथ सौदा करता है। जैसे कि नई लाइन सामान्यीकरण और उद्धृत स्ट्रिंग अक्षर में नई लाइनों को संभालना। आपका स्वागत है!

यदि आप सीएसवी फ़ाइल में हेडर है तो आप पहली पंक्ति से कॉलम नाम (और कॉलम इंडेक्स की गणना) पढ़ते हैं। इतना ही आसान।

ध्यान दें कि Dump एक LINQPad विधि है, यदि आप LINQPad का उपयोग नहीं कर रहे हैं तो आप उसे हटाना चाहेंगे।

void Main()
{
    var file1 = "a,b,c\r\nx,y,z";
    CSV.ParseText(file1).Dump();

    var file2 = "a,\"b\",c\r\nx,\"y,z\"";
    CSV.ParseText(file2).Dump();

    var file3 = "a,\"b\",c\r\nx,\"y\r\nz\"";
    CSV.ParseText(file3).Dump();

    var file4 = "\"\"\"\"";
    CSV.ParseText(file4).Dump();
}

static class CSV
{
    public struct Record
    {
        public readonly string[] Row;

        public string this[int index] => Row[index];

        public Record(string[] row)
        {
            Row = row;
        }
    }

    public static List<Record> ParseText(string text)
    {
        return Parse(new StringReader(text));
    }

    public static List<Record> ParseFile(string fn)
    {
        using (var reader = File.OpenText(fn))
        {
            return Parse(reader);
        }
    }

    public static List<Record> Parse(TextReader reader)
    {
        var data = new List<Record>();

        var col = new StringBuilder();
        var row = new List<string>();
        for (; ; )
        {
            var ln = reader.ReadLine();
            if (ln == null) break;
            if (Tokenize(ln, col, row))
            {
                data.Add(new Record(row.ToArray()));
                row.Clear();
            }
        }

        return data;
    }

    public static bool Tokenize(string s, StringBuilder col, List<string> row)
    {
        int i = 0;

        if (col.Length > 0)
        {
            col.AppendLine(); // continuation

            if (!TokenizeQuote(s, ref i, col, row))
            {
                return false;
            }
        }

        while (i < s.Length)
        {
            var ch = s[i];
            if (ch == ',')
            {
                row.Add(col.ToString().Trim());
                col.Length = 0;
                i++;
            }
            else if (ch == '"')
            {
                i++;
                if (!TokenizeQuote(s, ref i, col, row))
                {
                    return false;
                }
            }
            else
            {
                col.Append(ch);
                i++;
            }
        }

        if (col.Length > 0)
        {
            row.Add(col.ToString().Trim());
            col.Length = 0;
        }

        return true;
    }

    public static bool TokenizeQuote(string s, ref int i, StringBuilder col, List<string> row)
    {
        while (i < s.Length)
        {
            var ch = s[i];
            if (ch == '"')
            {
                // escape sequence
                if (i + 1 < s.Length && s[i + 1] == '"')
                {
                    col.Append('"');
                    i++;
                    i++;
                    continue;
                }
                i++;
                return true;
            }
            else
            {
                col.Append(ch);
                i++;
            }
        }
        return false;
    }
}

CsvHelper (एक पुस्तकालय मैं बनाए रखता हूं) कस्टम ऑब्जेक्ट्स में एक CSV फ़ाइल पढ़ेगा।

var csv = new CsvReader( File.OpenText( "file.csv" ) );
var myCustomObjects = csv.GetRecords<MyCustomObject>();

कभी-कभी आपके पास उन ऑब्जेक्ट्स का स्वामित्व नहीं है जिन्हें आप पढ़ने की कोशिश कर रहे हैं। इस मामले में, आप धाराप्रवाह मैपिंग का उपयोग कर सकते हैं क्योंकि आप कक्षा पर विशेषताओं को नहीं डाल सकते हैं।

public sealed class MyCustomObjectMap : CsvClassMap<MyCustomObject>
{
    public MyCustomObjectMap()
    {
        Map( m => m.Property1 ).Name( "Column Name" );
        Map( m => m.Property2 ).Index( 4 );
        Map( m => m.Property3 ).Ignore();
        Map( m => m.Property4 ).TypeConverter<MySpecialTypeConverter>();
    }
}




header