如何在 C# 中解析文本文件?
10 回答
Check this interesting approach, Linq To Text Files, very nice, you only need a IEnumerable<string>
method, that yields every file.ReadLine()
, and you do the query.
Here is another article that better explains the same technique.
using (TextReader rdr = new StreamReader(fullFilePath))
{
string line;
while ((line = rdr.ReadLine()) != null)
{
// use line here
}
}
将变量“fullFilePath”设置为完整路径,例如。C:\temp\myTextFile.txt
该算法可能如下所示:
- 打开文本文件
- 对于文件中的每一行:
- 解析线
解析一行有几种方法。
从初学者的角度来看,最简单的方法是使用 String 方法。
如果您准备迎接更多挑战,那么您可以使用 System.Text.RegularExpression 库来解析您的文本。
您可能想要使用一个帮助器类,例如http://www.blackbeltcoder.com/Articles/strings/a-text-parsing-helper-class中描述的那个。
从多年分析 CSV 文件(包括损坏或有边缘情况的文件)来看,这是我通过几乎所有单元测试的代码:
/// <summary>
/// Read in a line of text, and use the Add() function to add these items to the current CSV structure
/// </summary>
/// <param name="s"></param>
public static bool TryParseCSVLine(string s, char delimiter, char text_qualifier, out string[] array)
{
bool success = true;
List<string> list = new List<string>();
StringBuilder work = new StringBuilder();
for (int i = 0; i < s.Length; i++) {
char c = s[i];
// If we are starting a new field, is this field text qualified?
if ((c == text_qualifier) && (work.Length == 0)) {
int p2;
while (true) {
p2 = s.IndexOf(text_qualifier, i + 1);
// for some reason, this text qualifier is broken
if (p2 < 0) {
work.Append(s.Substring(i + 1));
i = s.Length;
success = false;
break;
}
// Append this qualified string
work.Append(s.Substring(i + 1, p2 - i - 1));
i = p2;
// If this is a double quote, keep going!
if (((p2 + 1) < s.Length) && (s[p2 + 1] == text_qualifier)) {
work.Append(text_qualifier);
i++;
// otherwise, this is a single qualifier, we're done
} else {
break;
}
}
// Does this start a new field?
} else if (c == delimiter) {
list.Add(work.ToString());
work.Length = 0;
// Test for special case: when the user has written a casual comma, space, and text qualifier, skip the space
// Checks if the second parameter of the if statement will pass through successfully
// e.g. "bob", "mary", "bill"
if (i + 2 <= s.Length - 1) {
if (s[i + 1].Equals(' ') && s[i + 2].Equals(text_qualifier)) {
i++;
}
}
} else {
work.Append(c);
}
}
list.Add(work.ToString());
// If we have nothing in the list, and it's possible that this might be a tab delimited list, try that before giving up
if (list.Count == 1 && delimiter != DEFAULT_TAB_DELIMITER) {
string[] tab_delimited_array = ParseLine(s, DEFAULT_TAB_DELIMITER, DEFAULT_QUALIFIER);
if (tab_delimited_array.Length > list.Count) {
array = tab_delimited_array;
return success;
}
}
// Return the array we parsed
array = list.ToArray();
return success;
}
然而,这个函数实际上并没有解析所有有效的 CSV 文件!一些文件中嵌入了换行符,您需要启用流阅读器来同时解析多行以返回一个数组。这是一个可以做到这一点的工具:
/// <summary>
/// Parse a line whose values may include newline symbols or CR/LF
/// </summary>
/// <param name="sr"></param>
/// <returns></returns>
public static string[] ParseMultiLine(StreamReader sr, char delimiter, char text_qualifier)
{
StringBuilder sb = new StringBuilder();
string[] array = null;
while (!sr.EndOfStream) {
// Read in a line
sb.Append(sr.ReadLine());
// Does it parse?
string s = sb.ToString();
if (TryParseCSVLine(s, delimiter, text_qualifier, out array)) {
return array;
}
}
// Fails to parse - return the best array we were able to get
return array;
}
作为参考,我将我的开源 CSV 代码放在 code.google.com 上。
Pero 回答的一个小改进:
FileInfo txtFile = new FileInfo("c:\myfile.txt");
if(!txtFile.Exists) { // error handling }
using (TextReader rdr = txtFile.OpenText())
{
// use the text file as Pero suggested
}
FileInfo 类使您有机会在实际开始读取文件之前对文件进行“处理”。您还可以在函数之间传递它作为文件位置的更好抽象(而不是使用完整路径字符串)。FileInfo 将路径规范化,使其绝对正确(例如,在适当的情况下将 / 转换为 \),并允许您提取有关文件的额外数据——父目录、扩展名、仅名称、权限等。
如果您有不只是简单的语言,请使用解析器生成器。它让我发疯,但我听说过关于ANTLR的好消息(注意:在开始之前获取手册并阅读它。如果您使用过解析器生成器以外的其他解析器生成器,那么您将无法立即正确处理它,至少我没有)
其他工具也存在。
在不真正知道您正在使用哪种文本文件的情况下,很难回答。但是,FileHelpers库有一套广泛的工具来帮助处理固定长度的文件格式、多记录、分隔等。
解析是什么意思?Parse 通常意味着将输入拆分为标记,如果您尝试实现编程语言,您可能会这样做。如果您只想读取文本文件的内容,请查看 System.IO.FileInfo。
首先,请确保您具有以下命名空间:
using System.Data;
using System.IO;
using System.Text.RegularExpressions;
接下来,我们构建一个函数,将任何 CSV 输入字符串解析为 DataTable:
public DataTable ParseCSV(string inputString) {
DataTable dt=new DataTable();
// declare the Regular Expression that will match versus the input string
Regex re=new Regex("((?<field>[^\",\\r\\n]+)|\"(?<field>([^\"]|\"\")+)\")(,|(?<rowbreak>\\r\\n|\\n|$))");
ArrayList colArray=new ArrayList();
ArrayList rowArray=new ArrayList();
int colCount=0;
int maxColCount=0;
string rowbreak="";
string field="";
MatchCollection mc=re.Matches(inputString);
foreach(Match m in mc) {
// retrieve the field and replace two double-quotes with a single double-quote
field=m.Result("${field}").Replace("\"\"","\"");
rowbreak=m.Result("${rowbreak}");
if (field.Length > 0) {
colArray.Add(field);
colCount++;
}
if (rowbreak.Length > 0) {
// add the column array to the row Array List
rowArray.Add(colArray.ToArray());
// create a new Array List to hold the field values
colArray=new ArrayList();
if (colCount > maxColCount)
maxColCount=colCount;
colCount=0;
}
}
if (rowbreak.Length == 0) {
// this is executed when the last line doesn't
// end with a line break
rowArray.Add(colArray.ToArray());
if (colCount > maxColCount)
maxColCount=colCount;
}
// create the columns for the table
for(int i=0; i < maxColCount; i++)
dt.Columns.Add(String.Format("col{0:000}",i));
// convert the row Array List into an Array object for easier access
Array ra=rowArray.ToArray();
for(int i=0; i < ra.Length; i++) {
// create a new DataRow
DataRow dr=dt.NewRow();
// convert the column Array List into an Array object for easier access
Array ca=(Array)(ra.GetValue(i));
// add each field into the new DataRow
for(int j=0; j < ca.Length; j++)
dr[j]=ca.GetValue(j);
// add the new DataRow to the DataTable
dt.Rows.Add(dr);
}
// in case no data was parsed, create a single column
if (dt.Columns.Count == 0)
dt.Columns.Add("NoData");
return dt;
}
现在我们有了一个用于将字符串转换为 DataTable 的解析器,我们现在只需要一个函数,该函数将从 CSV 文件中读取内容并将其传递给 ParseCSV 函数:
public DataTable ParseCSVFile(string path) {
string inputString="";
// check that the file exists before opening it
if (File.Exists(path)) {
StreamReader sr = new StreamReader(path);
inputString = sr.ReadToEnd();
sr.Close();
}
return ParseCSV(inputString);
}
现在您可以轻松地使用来自 CSV 文件的数据填充 DataGrid:
protected System.Web.UI.WebControls.DataGrid DataGrid1;
private void Page_Load(object sender, System.EventArgs e) {
// call the parser
DataTable dt=ParseCSVFile(Server.MapPath("./demo.csv"));
// bind the resulting DataTable to a DataGrid Web Control
DataGrid1.DataSource=dt;
DataGrid1.DataBind();
}
恭喜!您现在可以将 CSV 解析为 DataTable。祝你编程好运。