.Net中的CSV文件导入

我意识到这是一个新手问题,但我正在寻找一个简单的解决scheme – 它似乎应该有一个。

将CSV文件导入强types数据结构的最佳方法是什么? 再简单=更好。

查看FileHelpers开源库 。

微软的TextFieldParser是稳定的,并遵循RFC 4180的CSV文件。 不要被Microsoft.VisualBasic命名空间拖延; 它是.NET Framework中的标准组件,只需添加对全局Microsoft.VisualBasic程序集的引用即可。

如果您正在编译Windows(而不是Mono),并且不希望parsing“破坏的”(非RFC兼容的)CSV文件,那么这将是明显的select,因为它是免费的,不受限制的,稳定的,并积极支持,其中大部分不能说FileHelpers。

另请参阅: 如何:从Visual Basic中的逗号分隔文本文件读取 VB代码示例。

使用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(); 

如果你预计CSVparsing相当复杂的场景, 甚至不要想起滚动我们自己的parsing器 。 那里有很多优秀的工具,比如FileHelpers ,甚至CodeProject的工具 。

关键是这是一个相当普遍的问题,你可以打赌很多软件开发人员已经想到并解决了这个问题。

布赖恩提供了一个很好的解决scheme,将其转换为强types集合。

大多数CSVparsing方法没有考虑到转义字段或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在其中引用了一个string。 看到这个post有一个更好的扩展,以及一些正确的库的一些链接。

我同意@ NotMyself 。 FileHelpers经过了充分的testing,可以处理各种边缘情况,如果你自己做,你最终必须处理。 看一下FileHelpers所做的事情,只有在你确信(1)你永远不需要处理FileHelpers所做的边缘情况,或者(2)你喜欢写这种东西,当你必须parsing像这样的东西时会大喜过望:

1,“比尔”,“史密斯”,“主pipe”,“没有评论”

2,“德雷克”,“奥马利”,“看门人”

哎呀,我没有被引用,我正在换一个新的行!

我很无聊,所以我修改了一些我写的东西。 它尝试以OO方式封装parsing,并减less遍历整个文件的迭代次数,它只在顶端的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上有两篇文章提供了一个解决scheme的代码,一个使用StreamReader ,一个使用Microsoft Text Driver 导入CSV数据 。

一个很好的简单的方法是打开文件,并将每行读入一个数组,链表,数据结构的select。 不过要小心处理第一行。

这可能是你的头,但似乎有一个直接的方式来访问它们以及使用连接string 。

为什么不尝试使用Python而不是C#或VB? 它有一个很好的CSV模块导入,为您做所有繁重的工作。

今年夏天,我不得不在.NET中使用CSVparsing器进行项目,并在Microsoft Jet文本驱动程序中解决。 您使用连接string指定文件夹,然后使用SQL Select语句查询文件。 您可以使用schema.ini文件指定强types。 我一开始并没有这样做,但是在数据types不是很明显的情况下,例如IP号码或“XYQ 3.9 SP1”这样的条目,我的结果就变得很糟糕。

我碰到的一个限制是,它不能处理超过64个字符的列名; 它截断。 这不应该是一个问题,除非我处理devise非常差的input数据。 它返回一个ADO.NET数据集。

这是我find的最好的解决scheme。 我会警惕的滚动我自己的CSVparsing器,因为我可能会错过一些最终的情况下,我没有find任何其他的免费CSVparsing包在那里。

编辑:此外,每个目录只能有一个schema.ini文件,所以我dynamic地追加到它强烈键入所需的列。 它只会强制键入指定的列,并推断任何未指定的字段。 我真的很感激这一点,因为我正在处理导入一个stream体70 +列CSV,不想指定每一列,只是行为不端。

我input了一些代码。 datagridviewer中的结果看起来不错。 它将一行文本parsing为一个对象的数组列表。

  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]); 

可能有些图书馆可以帮助你,但是这可能很简单。 只要确保在数据中不能有逗号,否则您将需要更好地parsing它。