4

如何在 C# 中解析文本文件?

4

10 回答 10

8

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.

于 2008-11-19T00:51:45.583 回答
6
using (TextReader rdr = new StreamReader(fullFilePath))
{
  string line;

  while ((line = rdr.ReadLine()) != null)
  {
    // use line here
  }
}

将变量“fullFilePath”设置为完整路径,例如。C:\temp\myTextFile.txt

于 2009-01-15T14:55:20.510 回答
3

该算法可能如下所示:

  1. 打开文本文件
  2. 对于文件中的每一行:
  3. 解析线

解析一行有几种方法。

从初学者的角度来看,最简单的方法是使用 String 方法。

MSDN 上的 System.String

如果您准备迎接更多挑战,那么您可以使用 System.Text.RegularExpression 库来解析您的文本。

MSDN 上的正则表达式

于 2008-11-19T00:42:43.870 回答
1

您可能想要使用一个帮助器类,例如http://www.blackbeltcoder.com/Articles/strings/a-text-parsing-helper-class中描述的那个。

于 2010-12-23T18:17:53.550 回答
1

从多年分析 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 上

于 2012-07-23T17:35:00.607 回答
0

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 将路径规范化,使其绝对正确(例如,在适当的情况下将 / 转换为 \),并允许您提取有关文件的额外数据——父目录、扩展名、仅名称、权限等。

于 2009-01-15T15:18:47.203 回答
0

如果您有不只是简单的语言,请使用解析器生成器。它让发疯,但我听说过关于ANTLR的好消息(注意:在开始之前获取手册并阅读它。如果您使用过解析器生成器以外的其他解析器生成器,那么您将无法立即正确处理它,至少我没有)

其他工具也存在。

于 2008-11-19T00:38:41.677 回答
0

在不真正知道您正在使用哪种文本文件的情况下,很难回答。但是,FileHelpers库有一套广泛的工具来帮助处理固定长度的文件格式、多记录、分隔等。

于 2008-11-19T00:40:21.550 回答
0

解析是什么意思?Parse 通常意味着将输入拆分为标记,如果您尝试实现编程语言,您可能会这样做。如果您只想读取文本文件的内容,请查看 System.IO.FileInfo。

于 2008-11-19T00:42:37.643 回答
0

首先,请确保您具有以下命名空间:

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。祝你编程好运。

于 2009-08-25T08:55:41.367 回答