【CSV 로딩】TextFieldParser 대신 CsvParser 사용

2020/02/19 추가



CsvParser를 사용하지 않고 CsvReader에서 가능했습니다.
CSV 해설과 각 프로그래밍 언어의 구현 예

CSV 퍼스시 우려점



CSV를 퍼스 할 때, 단순히 1행씩 읽어들여, 콤마 「,」로 Split 하는 것만으로 부족한 데이터이면 좋지만, 실제로는 그렇게 할 수 없기 때문에, 이하와 같은 고려가 필요하게 된다 라고 생각합니다.

※전제 구분자:쉼표「,」
    둘러싸는 문자:더블 쿼테이션 「"」
    각 행의 열 수는 동일
  • 데이터에 쉼표가 있습니다 (구분 기호에 지정된 문자가 있음)
  • "a","b","c,d,e"
    
  • 데이터에 더블 인용이 존재합니다 (더블 인용을 이중화하고 이스케이프 처리).
    "a","""b","c"
    
  • 데이터에 줄 바꿈이 있습니다
  • "a","b
    ccc","d"
    
  • 둘러싸인 패턴과 둘러싸지 않는 패턴이 혼합
  • "a",b,"c,d,e"
    

    그 외에는, 데이터중에 탭 「\t」가 존재하는 등・・・

    먼저 TextFieldParser를 사용해 보았습니다 ...



    이런 데이터를 고려하면서 자전으로 퍼스 해 나가는 것은 꽤 힘들기 때문에, 라고 할까 나는 생각하는 것조차 그만뒀기 때문에···
    (언젠가 도전하고 싶다)
    유용한 클래스가 없거나 옛날 넷에서 찾았는데, TextFieldParser가 좋은 것 같았습니다.
    TextFiledParser

    .Net의 클래스이므로, 다른 오픈 소스의 파서와 비교해, 동작에 신뢰가 갖고, 컴플라이언스적으로 봐도 좋다고 생각했습니다.

    실제로 상기 데이터를 읽어 보면 깨끗하게 퍼스 할 수 있습니다.

    사용할 CSV
    "ヘッダー1","ヘッダー2","ヘッダー3"
    "a","b","c,d,e"
    "a","""b","c"
    "a","b
    ccc","d"
    "a",b,"c,d,e"
    

    코드
    using Microsoft.VisualBasic.FileIO;
    using System;
    using System.Linq;
    using System.Text;
    
    namespace TestProject.CSVParseTest
    {
        public class CSVParseTest
        {
            public void ReadCsv()
            {
                using (var parser = new TextFieldParser(@".\test.csv", Encoding.UTF8))
                {
                    // 区切り文字
                    parser.Delimiters = new string[] { "," };
    
                    // 囲み文字あり
                    parser.HasFieldsEnclosedInQuotes = true;
    
                    while (!parser.EndOfData)
                    {
                        var values = parser.ReadFields();
                        values.ToList().ForEach(v =>
                        {
                            // わかりやすいようにパイプを入れる
                            Console.Write(v + "|");
                        });
    
                        // わかりやすいように改行
                        Console.WriteLine();
                    }
                }
            }
        }
    }
    
    using System;
    
    namespace TestProject.CSVParseTest
    {
        public class Program
        {
            public static void Main(string[] args)
            {
                try
                {
                    var pTest = new CSVParseTest();
                    pTest.ReadCsv();
                }
                catch(Exception err)
                {
                    Console.WriteLine(err.Message);
                }
                finally
                {
                    Console.Read();
                }
            }
        }
    }
    



    상기의 결과에 만족하고 있었습니다만, 다음과 같은 데이터의 경우에, 의도하지 않은 움직임이 되어 버렸습니다.

    사용하는 CSV ※데이터중에 하늘의 개행이 들어간다
    "ヘッダー1","ヘッダー2","ヘッダー3"
    "a","b","c,d,e"
    "a","""b","c"
    "a","b
    
    gggg
    
    ffff
    
    ccc","d"
    "a",b,"c,d,e"
    

    결과

    데이터 중에도 빈 줄이 삭제되는 것 같습니다.
    게시판 등의 비고란 등의 텍스트에는, 자주(잘) 공행을 넣어 본 눈을 정돈하는 일이 있을까라고 생각합니다.
    그러한 데이터를 취급할 수 없게 되는 것은 곤란하기 때문에 불채용으로 했습니다.

    CsvHelper의 CsvParser 클래스 사용



    그 외를 맞이하게 되어, 인기의 CsvHelper를 검토하고 있었습니다.
    CsvHelper

    다만, 이것을 이용하는 경우, CSV의 포맷에 따른 엔티티의 클래스를 준비할 필요가,
    CSV의 열수가 나중에 변경이 되거나 하면, 매번 프로그램 소스도 수정해야 한다고 생각했기 때문에 주저하고 있었습니다.

    TextFiledParser와 같이 1행분의 문자를 단락 문자로 split해 배열로 돌려주면...
    또한, 공행도 재현해 주면 ...
    그렇다고 생각하면 CsvHelper.CsvParser의 Read 메서드가 그것이었습니다.
    using CsvHelper;
    using System;
    using System.Globalization;
    using System.IO;
    using System.Linq;
    using System.Text;
    
    namespace TestProject.CSVParseTest
    {
        public class CSVParseTest2
        {
            public void ReadCsv()
            {
                using(var stream = new StreamReader(@".\test.csv", Encoding.UTF8))
                using (var parser = new CsvParser(stream, CultureInfo.InvariantCulture))
                {
                    string[] line;
                    while ((line = parser.Read()) != null)
                    {
                        line.ToList().ForEach(v =>
                        {
                            // わかりやすいようにパイプを入れる
                            Console.Write(v + "|");
                        });
    
                        // わかりやすいように改行
                        Console.WriteLine();
                    }
                }
            }
        }
    }
    



    "TextFieldParser + 빈 줄이 삭제되지 않음"이 실현되었습니다.

    결론



    CsvHelper안은 아마 1문자씩 처리해, 퍼스하고 있다고 생각합니다.
    언젠가 내용을 보고 공부하고 싶습니다.

    오인이나, 다른 방법으로 해결할 수 있는 등, 있으면 지적해 주시면 고맙습니다.

    좋은 웹페이지 즐겨찾기