programing

CSV 파일을 에서 강력하게 입력된 데이터 구조로 가져옵니다.그물

jooyons 2023. 6. 9. 22:02
반응형

CSV 파일을 에서 강력하게 입력된 데이터 구조로 가져옵니다.그물

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

Microsoft의 TextFieldParser는 안정적이며 CSV 파일의 경우 RFC 4180을 따릅니다.그것 때문에 미루지 마.Microsoft.VisualBasic네임스페이스Framework의 입니다. .NET Framework의 .NET Framework에 . 글로벌에 대한 참조를 추가하기만 하면 됩니다.Microsoft.VisualBasic집회의

Windows용 컴파일(Mono와 대조적)을 수행하는 경우 "파손된"(RFC 호환이 아닌) CSV 파일을 구문 분석할 필요가 없을 것으로 예상되는 경우, 이는 자유롭고, 제한되지 않으며, 안정적이며, 적극적으로 지원되므로 FileHelper의 경우 대부분을 지원할 수 없습니다.

참고 항목:방법: 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 구문 분석에 상당히 복잡한 시나리오가 필요한 경우 자체 구문 분석기를 롤업할 생각은 하지 마십시오.파일 헬퍼나 코드 프로젝트와 같은 훌륭한 도구들이 많이 있습니다.

요점은 이것이 상당히 흔한 문제이며 많은 소프트웨어 개발자들이 이미 이 문제에 대해 생각하고 해결했다는 입니다.

는 @NotMyself에 동의합니다.FileHelper는 테스트를 잘 거쳤으며 사용자가 직접 처리할 경우 처리해야 하는 모든 종류의 에지 사례를 처리합니다.FileHelper가 수행하는 작업을 살펴보고 (1) FileHelper가 수행하는 에지 사례를 처리할 필요가 전혀 없거나 (2) 이러한 종류의 글을 쓰는 것을 좋아하고 다음과 같은 내용을 구문 분석해야 할 때 매우 기뻐할 경우에만 자신의 글을 쓰십시오.

1,"빌","스미스","감독","노 코멘트"

2, '드레이크', '오말리', '제니터,

이런, 저는 인용되지 않았고 저는 새로운 전화를 받고 있습니다!

브라이언은 강력한 유형의 컬렉션으로 변환하기 위한 좋은 솔루션을 제공합니다.

제공된 대부분의 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;
}

이것은 큰따옴표로 구분되지 않고 따옴표로 묶인 문자열이 있는 필드의 가장자리 대소문자를 처리하지 않습니다.일부 올바른 라이브러리에 대한 링크뿐만 아니라 좀 더 나은 확장을 위해 이 게시물을 참조하십시오.

저는 심심해서 제가 쓴 것들을 수정했습니다.파일을 통해 반복되는 양을 줄이면서 OOO 방식으로 구문 분석을 캡슐화하려고 합니다. 각 구문 분석은 맨 위에서 한 번만 반복됩니다.

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를 사용하는 기사이고 다른 하나는 Microsoft Text Driver를 사용하여 CSV 데이터를 가져오는 기사입니다.

파일을 열고 각 행을 배열, 링크된 목록, 원하는 데이터 구조로 읽어 들이는 것이 간단한 방법입니다.하지만 첫 번째 줄을 다룰 때는 조심해야 합니다.

이것은 당신의 머리 위에 있을 수 있지만, 연결 문자열을 사용하여 직접 액세스할 수 있는 방법도 있는 것 같습니다.

C#이나 VB 대신 Python을 사용해 보는 것은 어떻습니까?그것은 당신을 위해 모든 무거운 물건을 들어주는 좋은 CSV 모듈을 가지고 있습니다.

저는 이번 여름 프로젝트를 위해 .NET의 CSV 파서를 사용해야 했고 마이크로소프트 제트 텍스트 드라이버에 정착했습니다.연결 문자열을 사용하여 폴더를 지정한 다음 SQL Select 문을 사용하여 파일을 쿼리합니다.schema.ini 파일을 사용하여 강력한 유형을 지정할 수 있습니다.처음에는 이 작업을 수행하지 않았지만 IP 번호나 "XYQ 3.9 SP1"과 같은 항목과 같이 데이터 유형이 즉시 나타나지 않는 좋지 않은 결과를 얻었습니다.

한 가지 제한 사항은 64자를 초과하는 열 이름을 처리할 수 없다는 것입니다. 잘립니다.이것은 문제가 되지 않을 것입니다. 다만 제가 매우 부실하게 설계된 입력 데이터를 다루고 있었다는 점을 제외하고는요.ADO.NET 데이터 세트를 반환합니다.

이것이 제가 찾은 최고의 해결책이었습니다.제 CSV 파서를 굴리는 것은 조심스러울 것입니다. 왜냐하면 저는 아마도 몇몇 최종 사례를 놓쳤을 것이고 .NET에 대한 다른 무료 CSV 파싱 패키지를 찾을 수 없었기 때문입니다.

편집: 또한 디렉토리당 schema.ini 파일이 하나만 있을 수 있으므로 필요한 열을 강력하게 입력하기 위해 동적으로 추가했습니다.지정된 열만 강력하게 입력하고 지정되지 않은 필드에 대해 추론합니다.Fluid 70+ 컬럼 CSV를 가져오는 작업을 처리하면서 각 컬럼을 지정하지 않고 잘못된 컬럼만 지정할 수 있어 정말 감사했습니다.

코드를 입력했습니다.데이터 그리드 뷰어의 결과는 좋아 보였습니다.텍스트 한 줄을 개체의 배열 목록으로 구문 분석합니다.

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

데이터에 쉼표가 없다는 것을 보장할 수 있다면 String.split을 사용하는 것이 가장 간단한 방법일 것입니다.

예:

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

여러분이 도움을 줄 수 있는 도서관이 있을 수도 있지만, 그것은 아마도 여러분이 얻을 수 있는 것만큼 간단할 것입니다.데이터에 쉼표를 사용할 수 없는지 확인하십시오. 그렇지 않으면 더 잘 구문 분석해야 합니다.

언급URL : https://stackoverflow.com/questions/1898/import-csv-file-to-strongly-typed-data-structure-in-net

반응형