강력한 형식의 데이터 구조로 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 사용하여 마이크로 소프트 텍스트 드라이버 .