답변:
도서관이 당신을 위해 모든 세부 사항을 처리하게하십시오! :-)
FileHelpers를 확인하고 DRY를 유지하십시오-스스로 반복하지 마십시오-바퀴를 다시 발명 할 필요가 없습니다 ....
기본적으로 공개 클래스 (따라서 기본값, NULL 값 대체 등과 같은 잘 알려진 속성), 포인트를 사용하여 데이터의 형태 (CSV의 개별 줄에있는 필드)를 정의하면됩니다. 파일의 FileHelpers 엔진 및 bingo-해당 파일에서 모든 항목을 가져옵니다. 하나의 간단한 조작-뛰어난 성능!
CSV 파서는 이제 .NET Framework의 일부입니다.
Microsoft.VisualBasic.dll에 대한 참조 추가 (C #에서는 잘 작동하지만 이름은 신경 쓰지 않음)
using (TextFieldParser parser = new TextFieldParser(@"c:\temp\test.csv"))
{
parser.TextFieldType = FieldType.Delimited;
parser.SetDelimiters(",");
while (!parser.EndOfData)
{
//Process row
string[] fields = parser.ReadFields();
foreach (string field in fields)
{
//TODO: Process field
}
}
}
문서는 여기에 있습니다 -TextFieldParser 클래스
PS CSV 내보내기 가 필요한 경우 CsvExport를 사용해보십시오 (Discl : I 'm atributors).
TextFieldParser.ReadLine()
. 참조 TextFieldParser 워드 프로세서
CsvHelper (내가 유지 관리하는 라이브러리)는 CSV 파일을 사용자 지정 개체로 읽습니다.
var csv = new CsvReader( File.OpenText( "file.csv" ) );
var myCustomObjects = csv.GetRecords<MyCustomObject>();
때로는 읽으려는 객체를 소유하지 않은 경우가 있습니다. 이 경우 클래스에 속성을 넣을 수 없으므로 매끄럽게 매핑 할 수 있습니다.
public sealed class MyCustomObjectMap : CsvClassMap<MyCustomObject>
{
public MyCustomObjectMap()
{
Map( m => m.Property1 ).Name( "Column Name" );
Map( m => m.Property2 ).Index( 4 );
Map( m => m.Property3 ).Ignore();
Map( m => m.Property4 ).TypeConverter<MySpecialTypeConverter>();
}
}
편집하다:
CsvReader는 이제 CultureInfo가 컨스트럭터 ( https://github.com/JoshClose/CsvHelper/issues/1441 ) 로 전달되어야 합니다.
예:
var csv = new CsvReader(File.OpenText("file.csv"), System.Globalization.CultureInfo.CurrentCulture);
비즈니스 응용 프로그램에서 codeproject.com, CSVReader 의 오픈 소스 프로젝트를 사용합니다 .
잘 작동하고 성능이 좋습니다. 내가 제공 한 링크에 대한 벤치마킹이 있습니다.
프로젝트 페이지에서 복사 한 간단한 예 :
using (CsvReader csv = new CsvReader(new StreamReader("data.csv"), true))
{
int fieldCount = csv.FieldCount;
string[] headers = csv.GetFieldHeaders();
while (csv.ReadNextRecord())
{
for (int i = 0; i < fieldCount; i++)
Console.Write(string.Format("{0} = {1};", headers[i], csv[i]));
Console.WriteLine();
}
}
보시다시피, 작업하기가 매우 쉽습니다.
나는 조금 늦었지만 CSS 파일을 처리 Microsoft.VisualBasic.FileIO
하는 TextFieldParser
클래스 가 있는 라이브러리 를 찾았습니다 .
csv 파일 만 읽어야하는 경우이 라이브러리를 권장합니다. 빠른 CSV 리더
csv 파일도 생성해야하는 경우이 파일을 사용하십시오. FileHelpers
둘 다 무료이며 오픈 소스입니다.
이 스레드로 돌아 오는 경우를 위해 내가 자주 사용하는 도우미 클래스가 있습니다 (공유하고 싶었습니다).
사용할 준비가 된 프로젝트로 간단하게 이식하기 위해 이것을 사용합니다.
public class CSVHelper : List<string[]>
{
protected string csv = string.Empty;
protected string separator = ",";
public CSVHelper(string csv, string separator = "\",\"")
{
this.csv = csv;
this.separator = separator;
foreach (string line in Regex.Split(csv, System.Environment.NewLine).ToList().Where(s => !string.IsNullOrEmpty(s)))
{
string[] values = Regex.Split(line, separator);
for (int i = 0; i < values.Length; i++)
{
//Trim values
values[i] = values[i].Trim('\"');
}
this.Add(values);
}
}
}
그리고 그것을 다음과 같이 사용하십시오 :
public List<Person> GetPeople(string csvContent)
{
List<Person> people = new List<Person>();
CSVHelper csv = new CSVHelper(csvContent);
foreach(string[] line in csv)
{
Person person = new Person();
person.Name = line[0];
person.TelephoneNo = line[1];
people.Add(person);
}
return people;
}
[CSV 도우미 업데이트 : 마지막 줄 바꾸기 문자가 줄 바꾸기를 만들 때 발생하는 버그 수정]
이 솔루션은 공식 Microsoft.VisualBasic을 사용하고 있습니다. 어셈블리를 CSV를 구문 분석합니다.
장점 :
암호:
using Microsoft.VisualBasic.FileIO;
public static List<List<string>> ParseCSV (string csv)
{
List<List<string>> result = new List<List<string>>();
// To use the TextFieldParser a reference to the Microsoft.VisualBasic assembly has to be added to the project.
using (TextFieldParser parser = new TextFieldParser(new StringReader(csv)))
{
parser.CommentTokens = new string[] { "#" };
parser.SetDelimiters(new string[] { ";" });
parser.HasFieldsEnclosedInQuotes = true;
// Skip over header line.
//parser.ReadLine();
while (!parser.EndOfData)
{
var values = new List<string>();
var readFields = parser.ReadFields();
if (readFields != null)
values.AddRange(readFields);
result.Add(values);
}
}
return result;
}
TinyCsvParser를 작성 했습니다 for .NET . 이는 가장 빠른 .NET 파서 중 하나이며 거의 모든 CSV 형식을 구문 분석하도록 구성 할 수 있습니다.
MIT 라이센스에 따라 배포됩니다 :
NuGet 을 사용 하여 설치할 수 있습니다. 패키지 관리자 콘솔 에서 다음 명령을 실행하십시오 .
PM> Install-Package TinyCsvParser
persons.csv
이름, 성 및 생년월일이 있는 CSV 파일에 사람 목록이 있다고 가정 합니다.
FirstName;LastName;BirthDate
Philipp;Wagner;1986/05/12
Max;Musterman;2014/01/02
시스템의 해당 도메인 모델은 다음과 같습니다.
private class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
}
TinyCsvParser를 사용하는 경우 CSV 데이터의 열과 도메인 모델의 속성 간의 매핑을 정의해야합니다.
private class CsvPersonMapping : CsvMapping<Person>
{
public CsvPersonMapping()
: base()
{
MapProperty(0, x => x.FirstName);
MapProperty(1, x => x.LastName);
MapProperty(2, x => x.BirthDate);
}
}
그런 다음 매핑을 사용하여 CSV 데이터를로 구문 분석 할 수 있습니다 CsvParser
.
namespace TinyCsvParser.Test
{
[TestFixture]
public class TinyCsvParserTest
{
[Test]
public void TinyCsvTest()
{
CsvParserOptions csvParserOptions = new CsvParserOptions(true, new[] { ';' });
CsvPersonMapping csvMapper = new CsvPersonMapping();
CsvParser<Person> csvParser = new CsvParser<Person>(csvParserOptions, csvMapper);
var result = csvParser
.ReadFromFile(@"persons.csv", Encoding.ASCII)
.ToList();
Assert.AreEqual(2, result.Count);
Assert.IsTrue(result.All(x => x.IsValid));
Assert.AreEqual("Philipp", result[0].Result.FirstName);
Assert.AreEqual("Wagner", result[0].Result.LastName);
Assert.AreEqual(1986, result[0].Result.BirthDate.Year);
Assert.AreEqual(5, result[0].Result.BirthDate.Month);
Assert.AreEqual(12, result[0].Result.BirthDate.Day);
Assert.AreEqual("Max", result[1].Result.FirstName);
Assert.AreEqual("Mustermann", result[1].Result.LastName);
Assert.AreEqual(2014, result[1].Result.BirthDate.Year);
Assert.AreEqual(1, result[1].Result.BirthDate.Month);
Assert.AreEqual(1, result[1].Result.BirthDate.Day);
}
}
}
전체 사용 설명서는 다음에서 구할 수 있습니다.
내 KISS 구현은 다음과 같습니다.
using System;
using System.Collections.Generic;
using System.Text;
class CsvParser
{
public static List<string> Parse(string line)
{
const char escapeChar = '"';
const char splitChar = ',';
bool inEscape = false;
bool priorEscape = false;
List<string> result = new List<string>();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < line.Length; i++)
{
char c = line[i];
switch (c)
{
case escapeChar:
if (!inEscape)
inEscape = true;
else
{
if (!priorEscape)
{
if (i + 1 < line.Length && line[i + 1] == escapeChar)
priorEscape = true;
else
inEscape = false;
}
else
{
sb.Append(c);
priorEscape = false;
}
}
break;
case splitChar:
if (inEscape) //if in escape
sb.Append(c);
else
{
result.Add(sb.ToString());
sb.Length = 0;
}
break;
default:
sb.Append(c);
break;
}
}
if (sb.Length > 0)
result.Add(sb.ToString());
return result;
}
}
얼마 전에 Microsoft.VisualBasic
라이브러리를 기반으로 CSV 읽기 / 쓰기를위한 간단한 클래스를 작성했습니다 . 이 간단한 클래스를 사용하면 2 차원 배열과 같은 CSV로 작업 할 수 있습니다. 다음 링크를 통해 내 수업을 찾을 수 있습니다 : https://github.com/ukushu/DataExporter
간단한 사용법 예 :
Csv csv = new Csv("\t");//delimiter symbol
csv.FileOpen("c:\\file1.csv");
var row1Cell6Value = csv.Rows[0][5];
csv.AddRow("asdf","asdffffff","5")
csv.FileSave("c:\\file2.csv");
헤더를 읽으려면 csv.Rows[0]
셀 을 읽는 것만 필요 합니다. :)
간단한 구문 분석 요구를위한 단일 소스 파일 솔루션. 불쾌한 모든 경우를 처리합니다. 줄 바꿈 정규화 및 따옴표 붙은 문자열 리터럴에서 줄 바꾸기 처리와 같은. 천만에요!
CSV 파일에 헤더가 있으면 첫 번째 행에서 열 이름을 읽고 열 인덱스를 계산하십시오. 그렇게 간단합니다.
참고 Dump
LINQPad 방법입니다, 당신은 LINQPad을 사용하지 않는 경우 있음을 제거 할 수 있습니다.
void Main()
{
var file1 = "a,b,c\r\nx,y,z";
CSV.ParseText(file1).Dump();
var file2 = "a,\"b\",c\r\nx,\"y,z\"";
CSV.ParseText(file2).Dump();
var file3 = "a,\"b\",c\r\nx,\"y\r\nz\"";
CSV.ParseText(file3).Dump();
var file4 = "\"\"\"\"";
CSV.ParseText(file4).Dump();
}
static class CSV
{
public struct Record
{
public readonly string[] Row;
public string this[int index] => Row[index];
public Record(string[] row)
{
Row = row;
}
}
public static List<Record> ParseText(string text)
{
return Parse(new StringReader(text));
}
public static List<Record> ParseFile(string fn)
{
using (var reader = File.OpenText(fn))
{
return Parse(reader);
}
}
public static List<Record> Parse(TextReader reader)
{
var data = new List<Record>();
var col = new StringBuilder();
var row = new List<string>();
for (; ; )
{
var ln = reader.ReadLine();
if (ln == null) break;
if (Tokenize(ln, col, row))
{
data.Add(new Record(row.ToArray()));
row.Clear();
}
}
return data;
}
public static bool Tokenize(string s, StringBuilder col, List<string> row)
{
int i = 0;
if (col.Length > 0)
{
col.AppendLine(); // continuation
if (!TokenizeQuote(s, ref i, col, row))
{
return false;
}
}
while (i < s.Length)
{
var ch = s[i];
if (ch == ',')
{
row.Add(col.ToString().Trim());
col.Length = 0;
i++;
}
else if (ch == '"')
{
i++;
if (!TokenizeQuote(s, ref i, col, row))
{
return false;
}
}
else
{
col.Append(ch);
i++;
}
}
if (col.Length > 0)
{
row.Add(col.ToString().Trim());
col.Length = 0;
}
return true;
}
public static bool TokenizeQuote(string s, ref int i, StringBuilder col, List<string> row)
{
while (i < s.Length)
{
var ch = s[i];
if (ch == '"')
{
// escape sequence
if (i + 1 < s.Length && s[i + 1] == '"')
{
col.Append('"');
i++;
i++;
continue;
}
i++;
return true;
}
else
{
col.Append(ch);
i++;
}
}
return false;
}
}
이 목록의 또 다른 하나 인 Cinchoo ETL 여러 파일 형식 (CSV, 플랫 파일, Xml, JSON 등)을 읽고 쓰는 오픈 소스 라이브러리
아래 샘플은 CSV 파일을 빠르게 읽는 방법을 보여줍니다 (POCO 개체 필요 없음).
string csv = @"Id, Name
1, Carl
2, Tom
3, Mark";
using (var p = ChoCSVReader.LoadText(csv)
.WithFirstLineHeader()
)
{
foreach (var rec in p)
{
Console.WriteLine($"Id: {rec.Id}");
Console.WriteLine($"Name: {rec.Name}");
}
}
아래 샘플은 POCO 객체를 사용하여 CSV 파일을 읽는 방법을 보여줍니다.
public partial class EmployeeRec
{
public int Id { get; set; }
public string Name { get; set; }
}
static void CSVTest()
{
string csv = @"Id, Name
1, Carl
2, Tom
3, Mark";
using (var p = ChoCSVReader<EmployeeRec>.LoadText(csv)
.WithFirstLineHeader()
)
{
foreach (var rec in p)
{
Console.WriteLine($"Id: {rec.Id}");
Console.WriteLine($"Name: {rec.Name}");
}
}
}
CodeProject 에서 사용 방법에 대한 기사를 확인 하십시오.
C # split () 함수를 사용하여 CSV를 올바르게 분할하는 방법 에 대한 무제한의 게시물을 기반으로 합니까? :
string[] tokens = System.Text.RegularExpressions.Regex.Split(paramString, ",");
참고 : 이스케이프 / 중첩 쉼표 등을 처리하지 않으므로 특정 간단한 CSV 목록에만 적합합니다.
이 코드는 csv를 DataTable로 읽습니다.
public static DataTable ReadCsv(string path)
{
DataTable result = new DataTable("SomeData");
using (TextFieldParser parser = new TextFieldParser(path))
{
parser.TextFieldType = FieldType.Delimited;
parser.SetDelimiters(",");
bool isFirstRow = true;
//IList<string> headers = new List<string>();
while (!parser.EndOfData)
{
string[] fields = parser.ReadFields();
if (isFirstRow)
{
foreach (string field in fields)
{
result.Columns.Add(new DataColumn(field, typeof(string)));
}
isFirstRow = false;
}
else
{
int i = 0;
DataRow row = result.NewRow();
foreach (string field in fields)
{
row[i++] = field;
}
result.Rows.Add(row);
}
}
}
return result;
}
스 니펫을 원하는 사람은 라이브러리를 바인딩하거나 패키지를 다운로드하지 않고도 코드를 사용할 수 있습니다. 내가 쓴 버전은 다음과 같습니다.
public static string FormatCSV(List<string> parts)
{
string result = "";
foreach (string s in parts)
{
if (result.Length > 0)
{
result += ",";
if (s.Length == 0)
continue;
}
if (s.Length > 0)
{
result += "\"" + s.Replace("\"", "\"\"") + "\"";
}
else
{
// cannot output double quotes since its considered an escape for a quote
result += ",";
}
}
return result;
}
enum CSVMode
{
CLOSED = 0,
OPENED_RAW = 1,
OPENED_QUOTE = 2
}
public static List<string> ParseCSV(string input)
{
List<string> results;
CSVMode mode;
char[] letters;
string content;
mode = CSVMode.CLOSED;
content = "";
results = new List<string>();
letters = input.ToCharArray();
for (int i = 0; i < letters.Length; i++)
{
char letter = letters[i];
char nextLetter = '\0';
if (i < letters.Length - 1)
nextLetter = letters[i + 1];
// If its a quote character
if (letter == '"')
{
// If that next letter is a quote
if (nextLetter == '"' && mode == CSVMode.OPENED_QUOTE)
{
// Then this quote is escaped and should be added to the content
content += letter;
// Skip the escape character
i++;
continue;
}
else
{
// otherwise its not an escaped quote and is an opening or closing one
// Character is skipped
// If it was open, then close it
if (mode == CSVMode.OPENED_QUOTE)
{
results.Add(content);
// reset the content
content = "";
mode = CSVMode.CLOSED;
// If there is a next letter available
if (nextLetter != '\0')
{
// If it is a comma
if (nextLetter == ',')
{
i++;
continue;
}
else
{
throw new Exception("Expected comma. Found: " + nextLetter);
}
}
}
else if (mode == CSVMode.OPENED_RAW)
{
// If it was opened raw, then just add the quote
content += letter;
}
else if (mode == CSVMode.CLOSED)
{
// Otherwise open it as a quote
mode = CSVMode.OPENED_QUOTE;
}
}
}
// If its a comma seperator
else if (letter == ',')
{
// If in quote mode
if (mode == CSVMode.OPENED_QUOTE)
{
// Just read it
content += letter;
}
// If raw, then close the content
else if (mode == CSVMode.OPENED_RAW)
{
results.Add(content);
content = "";
mode = CSVMode.CLOSED;
}
// If it was closed, then open it raw
else if (mode == CSVMode.CLOSED)
{
mode = CSVMode.OPENED_RAW;
results.Add(content);
content = "";
}
}
else
{
// If opened quote, just read it
if (mode == CSVMode.OPENED_QUOTE)
{
content += letter;
}
// If opened raw, then read it
else if (mode == CSVMode.OPENED_RAW)
{
content += letter;
}
// It closed, then open raw
else if (mode == CSVMode.CLOSED)
{
mode = CSVMode.OPENED_RAW;
content += letter;
}
}
}
// If it was still reading when the buffer finished
if (mode != CSVMode.CLOSED)
{
results.Add(content);
}
return results;
}
짧고 간단한 해결책이 있습니다.
using (TextFieldParser parser = new TextFieldParser(outputLocation))
{
parser.TextFieldType = FieldType.Delimited;
parser.SetDelimiters(",");
string[] headers = parser.ReadLine().Split(',');
foreach (string header in headers)
{
dataTable.Columns.Add(header);
}
while (!parser.EndOfData)
{
string[] fields = parser.ReadFields();
dataTable.Rows.Add(fields);
}
}