[c#] .Net에서 강력한 형식의 데이터 구조로 CSV 파일 가져 오기 [닫기]

강력한 형식의 데이터 구조로 CSV 파일을 가져 오는 가장 좋은 방법은 무엇입니까?



답변

Microsoft의 TextFieldParser 는 안정적이며 CSV 파일의 경우 RFC 4180 을 따릅니다 . Microsoft.VisualBasic이름 공간 때문에 미루지 마십시오 . .NET Framework의 표준 구성 요소이므로 전역 Microsoft.VisualBasic어셈블리에 대한 참조를 추가하기 만하면 됩니다.

Windows (Mono와 반대) 용으로 컴파일하고 “파손 된”(RFC 비준수) CSV 파일을 구문 분석 할 필요가없는 경우, 이것이 무료이고 제한되지 않고 안정적이기 때문에 이것이 확실한 선택이 될 것입니다. 그리고 적극적으로 지원되며 대부분은 FileHelpers에 대해 말할 수 없습니다.

참고 항목 : 방법 : VB 코드 예제 는 Visual Basic의 쉼표로 구분 된 텍스트 파일에서 읽기


답변

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


답변

CSV 파싱에 대해 상당히 복잡한 시나리오를 예상하는 경우 자체 파서 롤링을 생각하지 마십시오 . FileHelpers 또는 CodeProject의 도구와 같은 훌륭한 도구가 많이 있습니다 .

요점은 이것은 매우 일반적인 문제이며 많은 소프트웨어 개발자가 이미이 문제에 대해 생각하고 해결했다고 확신 할 수 있습니다.


답변

Brian은 강력한 형식의 컬렉션으로 변환 할 수있는 좋은 솔루션을 제공합니다.

제공된 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;
}

이것은 큰 따옴표로 구분되지 않는 필드의 가장자리 케이스를 처리하지 않지만 그 안에 따옴표로 묶인 문자열이있는 meerley를 처리합니다. 더 나은 확장과 적절한 라이브러리에 대한 링크는 이 게시물 을 참조하십시오 .


답변

@NotMyself에 동의합니다 . FileHelpers 는 잘 테스트되었으며 사용자가 직접 처리 할 경우 결국 처리해야하는 모든 종류의 엣지 케이스를 처리합니다. FileHelpers가 수행하는 작업을 살펴보고 (1) FileHelpers가 수행하는 엣지 케이스를 처리 할 필요가 전혀 없다고 확신하는 경우에만 직접 작성하거나 (2) 이런 종류의 작성을 좋아하고 다음과 같이 구문 분석해야 할 때 기뻐하십시오.

1, “Bill”, “Smith”, “Supervisor”, “No Comment”

2, ‘Drake,’, ‘O’Malley’, “Janitor,

죄송합니다. 저는 인용되지 않았고 새 줄에 있습니다!


답변

지루해서 내가 쓴 내용을 수정했습니다. 파일을 통한 반복의 양을 줄이면서 OO 방식으로 파싱을 캡슐화하려고 시도하며, 상위 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]),
                };
            }
        }
    }
}


답변

솔루션 사용 하나에 대한 코드를 제공 CodeProject의 두 기사가 있습니다 에서는 StreamReader를 하고 있다는 한 수입은 데이터 CSV 사용하여 마이크로 소프트 텍스트 드라이버 .