Импорт данных CSV в классы C # - PullRequest
3 голосов
/ 09 октября 2011

Я знаю, как читать и отображать строку файла .csv. Теперь я хотел бы проанализировать этот файл, сохранить его содержимое в массивах и использовать эти массивы в качестве значений для некоторых классов, которые я создал.

Хотелось бы узнать как.

Вот пример:

basketball,2011/01/28,Rockets,Blazers,98,99
baseball,2011/08/22,Yankees,Redsox,4,3

Как видите, каждое поле разделено запятыми. Я создал классы Basketball.cs и Baseball, которые являются расширением класса Sport.cs с полями:

private string sport;
private string date;
private string team1;
private string team2;
private string score;

Я понимаю, что это упрощенно, и что есть лучшие способы хранения этой информации, то есть создание классов для каждой команды, превращение даты в тип данных DateType и многое другое, но я хотел бы знать, как ввести это информация в классах.

Я предполагаю, что это как-то связано с геттерами и сеттерами ... Я также читал словари и коллекции, но я хотел бы начать с простого, храня их все в массивах ... (Если это делает чувство ... не стесняйтесь поправлять меня).

Вот что у меня есть. Все, что он делает, это читает CSV-файл и выводит его содержимое на консоли:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;

namespace Assign01
{
    class Program
    {
        static void Main(string[] args)
        {
            string line;
            FileStream aFile = new FileStream("../../sportsResults.csv", FileMode.Open);
            StreamReader sr = new StreamReader(aFile);

            // read data in line by line
            while ((line = sr.ReadLine()) != null)
            {
                Console.WriteLine(line);
                line = sr.ReadLine();
            }
            sr.Close();
        }
    }
}

Помощь будет высоко ценится.

Ответы [ 5 ]

6 голосов
/ 09 октября 2011

разбиение кода на массивы для получения данных может быть подвержено ошибкам и медленным.Попробуйте использовать поставщик данных OLE для чтения CSV, как если бы это была таблица в базе данных SQL, таким образом, вы можете использовать предложение WHERE для фильтрации результатов.

App.Config
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <connectionStrings>
    <add name="csv" providerName="System.Data.OleDb" connectionString="Provider=Microsoft.Jet.OLEDB.4.0;Data Source='C:\CsvFolder\';Extended Properties='text;HDR=Yes;FMT=Delimited';" />
  </connectionStrings>
</configuration>

program.cs
<code>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.OleDb;
using System.Configuration;
using System.Data;
using System.Data.Common;

namespace CsvImport
{
    class Stat
    {
        public string Sport { get; set; }
        public DateTime Date { get; set; }
        public string TeamOne { get; set; }
        public string TeamTwo { get; set; }
        public int Score { get; set; }
    }

    class Program
    {
        static void Main(string[] args)
        {
            ConnectionStringSettings csv = ConfigurationManager.ConnectionStrings["csv"];
            List stats = new List();

            using (OleDbConnection cn = new OleDbConnection(csv.ConnectionString))
            {
                cn.Open();
                using (OleDbCommand cmd = cn.CreateCommand())
                {
                    cmd.CommandText = "SELECT * FROM [Stats.csv]";
                    cmd.CommandType = CommandType.Text;
                    using (OleDbDataReader reader = cmd.ExecuteReader(CommandBehavior.CloseConnection))
                    {
                        int fieldSport = reader.GetOrdinal("sport");
                        int fieldDate = reader.GetOrdinal("date");
                        int fieldTeamOne = reader.GetOrdinal("teamone");
                        int fieldTeamTwo = reader.GetOrdinal("teamtwo");
                        int fieldScore = reader.GetOrdinal("score");

                        foreach (DbDataRecord record in reader)
                        {
                            stats.Add(new Stat
                            {
                                Sport = record.GetString(fieldSport),
                                Date = record.GetDateTime(fieldDate),
                                TeamOne = record.GetString(fieldTeamOne),
                                TeamTwo = record.GetString(fieldTeamTwo),
                                Score = record.GetInt32(fieldScore)
                            });
                        }
                    }
                }
            }

            foreach (Stat stat in stats)
            {
                Console.WriteLine("Sport: {0}", stat.Sport);
            }
        }
    }
}

</code>

edit

Я забыл показать, как должен выглядеть CSV:)

stats.csv

sport,date,teamone,teamtwo,score
basketball,28/01/2011,Rockets,Blazers,98
baseball,22/08/2011,Yankees,Redsox,4
6 голосов
/ 09 октября 2011

Создание массива для хранения информации не очень хорошая идея, так как вы не знаете, сколько строк будет во входном файле. Какой будет начальный размер вашего массива ?? Я бы посоветовал вам использовать, например, общий список для хранения информации (например, список <>).

Вы также можете добавить конструктор в свой спортивный класс, который принимает массив (результат действия разделения, как описано в ответе выше.

Дополнительно вы можете предоставить некоторые преобразования в сеттеры

public class Sport
{
    private string sport;
    private DateTime date;
    private string team1;
    private string team2;
    private string score;

    public Sport(string[] csvArray)
    {
        this.sport = csvArray[0];
        this.team1 = csvArray[2];
        this.team2 = csvArray[3];
        this.date = Convert.ToDateTime(csvArray[1]);
        this.score = String.Format("{0}-{1}", csvArray[4], csvArray[5]);
    }

Просто для простоты я написал метод преобразования, но имейте в виду, что это также не очень безопасный способ, если только вы не уверены, что DateField всегда содержит действительные даты, а Score всегда содержит числовые значения. Вы можете попробовать другие более безопасные методы, такие как tryParse или некоторая обработка исключений.

Я, честно говоря, должен добавить, что вышеприведенное решение является простым (в соответствии с просьбой), на концептуальном уровне я бы посоветовал против него. Помещение логики сопоставления между атрибутами и csv-файлом в классе сделает спортивный класс слишком зависимым от самого файла и, следовательно, менее пригодным для повторного использования. Любые последующие изменения в файловой структуре должны быть отражены в вашем классе и часто могут игнорироваться. Поэтому было бы разумнее поместить вашу логику «отображения и преобразования» в основную программу и сохранить ваш класс в чистоте, насколько это возможно

(изменил проблему «Score», отформатировав ее как 2 строки в сочетании с дефисом)

3 голосов
/ 09 октября 2011

Хотя существует множество библиотек, которые облегчат чтение csv (см .: здесь ), все, что вам нужно сделать прямо сейчас, когда у вас есть строка, это разделить ее.

String[] csvFields = line.Split(",");

Теперь присвойте каждое поле соответствующему члену

sport = csvFields[0];
date = csvFields[1];
//and so on

Однако это будет перезаписывать значения каждый раз, когда вы читаете новую строку, поэтому вам нужно упаковать значения в класс и сохранить экземплярыэтого класса в список.

1 голос
/ 09 октября 2011
// use "Microsoft.VisualBasic.dll"

using System;
using Microsoft.VisualBasic.FileIO;

class Program {
    static void Main(string[] args){
        using(var csvReader = new TextFieldParser(@"sportsResults.csv")){
            csvReader.SetDelimiters(new string[] {","});
            string [] fields;
            while(!csvReader.EndOfData){
                fields = csvReader.ReadFields();
                Console.WriteLine(String.Join(",",fields));//replace make instance
            }
        }
    }
}
0 голосов
/ 09 октября 2011

Ниже для новичков и привлекательных решений, которые большинство новичков любит пробовать и ошибаться, пожалуйста, не забудьте добавить System.Core.dll в ссылках. Импортируйте пространство имен в ваш файл .cs: используя System.Linq;

Возможно, лучше добавить итератор, код

private static IEnumerable<String> GetDataPerLines()
{
    FileStream aFile = new FileStream("sportsResults.csv",FileMode.Open);             
    StreamReader sr = new StreamReader(aFile); 
    while ((line = sr.ReadLine()) != null)             
    { 
        yield return line;
    }             
    sr.Close(); 
}

static void Main(string[] args)
{
    var query = from data in GetDataPerLines()
          let splitChr = data.Split(",".ToCharArray())
                select new Sport
    {
       sport = splitChr[0],
       date = splitChr[1],.. and so on
    }

    foreach (var item in query)
    {
        Console.Writeline(" Sport = {0}, in date when {1}",item.sport,item.date);
    }
}

Может быть, вот так, в приведенном выше примере создается ваша собственная итерация с использованием yield (для этого обратитесь к документации MSDN) и создайте коллекцию на основе вашей строки.

Дайте мне знать, если я напишу код неправильно, поскольку у меня нет Visual Studio, когда я пишу ответ.Насколько вам известно, массив одного измерения, такой как "Sport []", будет переводиться в CLR IEnumerable

...