使用C#.Net 4.0 LINQ嵌入逗号的CSV
我试图找到一种优雅的方式通过4.0 linq读取cvs字符串,但由于引号之间嵌入了逗号,所以有点失败。以下是3列3行的示例: 日期、年份、备忘录文本 “2011-01-01”、“0.5”、“备忘录文本使用C#.Net 4.0 LINQ嵌入逗号的CSV,linq,c#-4.0,.net-4.0,csv,Linq,C# 4.0,.net 4.0,Csv,我试图找到一种优雅的方式通过4.0 linq读取cvs字符串,但由于引号之间嵌入了逗号,所以有点失败。以下是3列3行的示例: 日期、年份、备忘录文本 “2011-01-01”、“0.5”、“备忘录文本 备忘录文本继续 继续,然后是逗号,但备忘录中有引号“ “2010-01-01”、“0.5”、“备忘录文本、不带换行符的备忘录” “2009-01-01”、“1.0”、“纯备忘录文本” 到目前为止,我已经提出了以下错误代码,作为将其他堆栈交换位合并在一起的方法。这不起作用,因为回车符换行符将备注文本
备忘录文本继续
继续,然后是逗号,但备忘录中有引号“
“2010-01-01”、“0.5”、“备忘录文本、不带换行符的备忘录”
“2009-01-01”、“1.0”、“纯备忘录文本”
到目前为止,我已经提出了以下错误代码,作为将其他堆栈交换位合并在一起的方法。这不起作用,因为回车符换行符将备注文本拆分为多个字段,所以回车符换行符在备注文本中换行
using (var reader = new StreamReader(getReader))
{
var records = reader.ReadToEnd().Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
var enumRecords = records.Skip(1).Take(1);
using (var dc = new DataContext())
{
foreach (var record in enumRecords
.Select(x => x.Trim()
.Split(new char[] { ',' }))
.Select(fields => new Entity
{
Date = (!string.IsNullOrEmpty(record.ElementAt(0))) ? Convert.ToDateTime(record.ElementAt(0)) : default(DateTime),
DecimalYears = record.ElementAt(1),
MemoText = record.ElementAt(2)
}))
{
//Commit DataContext
}
}
}
仅在逗号上拆分时没有骰子,因为引号中的文本之间存在逗号:
using (var reader = new StreamReader(getReader))
{
var sdata = reader.ReadToEnd();
using (var dc = new DataContext())
{
var query = sdata
.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries)
.Replace(Environment.NewLine, string.Empty)
.Replace("\"\"", "\",\"")
.Select((i, n) => new { i, n })
.GroupBy(a => a.n / 3)
.Skip(1).Take(1);
foreach (var fields in query)
{
var newEntity = new Entity();
newEntity.Date = (!string.IsNullOrEmpty(fields.ElementAt(0).i)) ? Convert.ToDateTime(fields.ElementAt(0).i) : default(DateTime);
newEntity.DecimalYears = fields.ElementAt(1).i;
newEntity.MemoText = fields.ElementAt(2).i;
}
}
}
到目前为止,似乎一个简单的目标是接近冗长丑陋的代码,可能有人有一个干净和功能性的方法来使用LINQ来实现这一点 事实上,对于.Net的答案是建议不要自己做这件事——有大量的第三方库可以让这件事变得简单:
如果您可以更改文件的结构,我建议您找到一个在内容中其他地方不使用的唯一分隔符(即“;”) 然后,使用第三方库(如此库)显然可以简化任务 您可以使用这样一种干净的语法:
var memos = from p in myFile
select new { p.Date, p.DecimalYears, p.MemoText };
以下是我使用的代码,以防这对将来的某个人或其他人感到有必要对其进行微调
using (var reader = new StreamReader(Service.GetResult(batchInfo, results.result[0])))
{
using (var dc = new DataContext())
{
using (var parser = new TextFieldParser(reader))
{
parser.Delimiters = new string[] { "," };
parser.TrimWhiteSpace = true;
while (true)
{
string[] parts = parser.ReadFields();
if (parts == null) { break; }
try
{
var newEntity = new Entity();
newEntity.ID = Guid.NewGuid();
newEntity.Date = (!string.IsNullOrEmpty(parts[0])) ? Convert.ToDateTime(parts[0]) : default(DateTime);
newEntity.Year = parts[1];
newEntity.MemoText = parts[2];
dc.Entity.InsertOnSubmit(newEntity);
dc.SubmitChanges();
}
catch (MalformedLineException mle)
{
string message = mle.Message;
//TODO: log an error
}
}
}
}
}
以下是Eric White提出的一个很好的扩展方法,可以满足您的CSV需求:
- 只有逗号对分隔符有效
- 可以引用值。引号被删掉了
- 引用的值可以有内部逗号
- 引用的值也可以有内部转义序列:反斜杠 后跟任何字符,包括引号(\”)、反斜杠(\)或 任何其他字符(\a)
- CsvSplit将为格式不正确的字符串引发异常
我想我已经下定决心要在LINQ.net 4.0中实现这一点,在上面的代码片段中,我是否可能只差一行或多行?当然,可能还有更多的条件我还没有遇到,这正是我现在所知道的。文件解析是一个打破许多项目的主题。一切都很好,直到有人在某个字段中输入了错误的值,或者你雇用了奥康纳先生等等。我只想从那些在我之前经历过所有痛苦的人身上吸取教训:-)我最终使用了您附加的stackoverflow链接中的TextFieldParser。非常好,谢谢!哇,耶,如果我能控制csv的渲染,那就太好了。不幸的是,它来自salesforce bulkapi。我可以重新格式化输出,但可能会增加额外的开销。至少您不是唯一一个遇到此问题的人: