解析制表符分隔的文本文件

8

我有一个制表符分隔的文件,其中包含一些列和行,例如:有些行可能对某些列没有值。 我们知道“顺序”不会改变,因此始终第三个制表符分隔的内容是Column3等等。

Column1  Column2  Column3 .... Column12 .... Column34 ... Column50
123  34 ABC
234     DEF                                   as@ddd.com    True
     45           NYC                         wwe@dsds.com  False      

现在我需要读取这个文件,但并不是所有的列对我的程序都很重要。例如,我只需要处理Column2、Column12、Column45中的某些值。

你建议采用什么方法?


4
逐行阅读,按制表符分割,提取所需列。您还需要什么? - Jonesopolis
2
你是在寻求意见还是在遇到问题的某个特定部分有困难? - D Stanley
@Jonesy,我不知道这部分的意思:“获取你需要的列”。 另外,有没有一种高效的读取文件的方法,可以避免在处理大文件时崩溃或超出内存限制? - user2740190
6个回答

12
尝试下面的方法:
static void Main(string[] args)
{
    DataTable datatable = new DataTable();
    StreamReader streamreader = new StreamReader(@"C:\Temp\txt.txt");
    char[] delimiter = new char[] { '\t' };
    string[] columnheaders = streamreader.ReadLine().Split(delimiter);
    foreach (string columnheader in columnheaders)
    {
        datatable.Columns.Add(columnheader); // I've added the column headers here.
    }

    while (streamreader.Peek() > 0)
    {
        DataRow datarow = datatable.NewRow();
        datarow.ItemArray = streamreader.ReadLine().Split(delimiter);
        datatable.Rows.Add(datarow);
    }

    foreach (DataRow row in datatable.Rows)
    {
        Console.WriteLine(""----Row No: " + datatable.Rows.IndexOf(row) + "----"");

        foreach (DataColumn column in datatable.Columns)
        {
            //check what columns you need
            if (column.ColumnName == "Column2" || 
                column.ColumnName == "Column12" ||
                column.ColumnName == "Column45") 
            {
                Console.Write(column.ColumnName);
                Console.Write(" ");
                Console.WriteLine(row[column]);
            }
        }
    }
    Console.ReadLine();
}

哇..太棒了 :) 你有什么最终想法或考虑吗?还是你认为它很好,我可以去使用它? - user2740190
我很好奇为什么你选择了这个答案而不是@Sudhakar的?在我看来,它更简单。 - Jonesopolis
@Jonesy 正确,我仍在评估它们。现在不确定为什么这个答案没有保持记录的顺序与输入文件中的相同.... - user2740190
@Jonesy 我也试过了,它会崩溃。 - user2740190
1
@DevWannaBe - 我不知道你所说的记录顺序未被维护是什么意思。我更新了代码以显示输入文件中的行索引。希望这可以帮助你看到每行的顺序在输入文件中是什么。 - user353gre3
感谢@BinayaRegmi。我的测试文件有12154行。所以可能并不是所有的行都显示在控制台中,而且控制台的滚动条只显示其中一部分?因为我在控制台输出中看到的第一行行号是12081。 - user2740190

8
不要自己造轮子。有一些微妙之处并不是立即显而易见的,包括但不限于:
- 引用字段? - 包含嵌入字段和/或记录分隔符的数据 - 错误长度记录 - 等等。
相反,使用类似Sebastien Lorion最优秀的快速CSV阅读器CodeProject编辑注意:尽管名称如此,但这是一个通用的分隔文本文件的读取器。可配置项包括
- 字段分隔符字符 - 记录分隔符字符 - 引号字符(用于引用文本) - 转义字符(用于嵌入引号) - 是否允许评论。如果启用,则注释字符(见下文)开始一个注释,到下一个记录分隔符结束。 - 注释字符(默认为“#”) - 第一行是否为标题,包含字段名称。

1
谢谢,但我的是制表符分隔的文本文件。你建议的那个是用于CSV文件的? - user2740190
2
唯一的区别在于您选择的字段分隔符和记录分隔符。只需将其配置为使用HT ('\t') 作为字段分隔符,而不是逗号(',')。 - Nicholas Carey

5
你可以使用File.ReadLines()方法(如果你正在使用.NET Framework 4.0或更高版本),而不会有任何性能损失,因为它不会将整个文件内容加载到内存中。
试一试:
using System.IO;

class FileData
{
public string Column2{ get; set; }
public string Column12{ get; set; }
public string Column45{ get; set; }
}


List<FileData> filedata =  new List<FileData>();

 FileData temp = new FileData();
 foreach(var line in File.ReadLines("filepath.txt").Skip(1))
 {     
   var tempLine = line.Split('\t');
   temp.Column2 = tempLine[1];
   temp.Column12 = tempLine[11];
   temp.Column45 = tempLine[44]; 
   filedata.Add(temp);
 }

一个定义好的对象结构列表可能比三个独立的值列表更好 :) - David
如何忽略文件的第一行?第一行是列名,下面的行是数据。列名包括Column1、Column2、Column3……Column12……Column34……Column50。 - user2740190
1
@DevWannaBe:要么将foreach转换为for并跳过第一行,要么引入计数器或标志变量。例如var skip = true,然后在循环中使用if (skip) { skip = false; continue; }。两种方法都可以,只是看你认为哪种更易读/可维护。 - David
3
为什么不直接使用 File.ReadLines("filepath.txt").Skip(1) - Jonesopolis
@SudhakarTillapudi:Crashes,templine 是一个只有一个成员且为空字符串""的数组。 - user2740190
显示剩余5条评论

5
var list = from row in System.IO.File.ReadLines("file.txt")
           let arr = row.Split('\t')
           select new Tuple<string, string, string>(arr[2], arr[12], arr[45]);

1
这应该是被接受的答案。非常简单,获取数据后易于操作。如果您希望拥有适当的数据模型,我也喜欢@Sudhakar Tillapudi的答案。逐行阅读似乎很令人生畏,只要给我所有的数据就可以了。问题在于,如果您想读取具有许多列的文件的每一列,则会出现问题。 - stepheaw

2

正如尼古拉斯所说,不要自己编写代码,因为其中有许多细节和特殊情况需要考虑。

一种选择是使用TextFieldParser

using (var parser = new TextFieldParser(filePath))
{
    parser.TextFieldType = FieldType.Delimited;
    parser.SetDelimiters("\t");

    while (!parser.EndOfData)
    {
        var cols = parser.ReadFields();
        // Can now access columns, eg cols[0]
    }
}

这需要 .net5.0。 - ErocM

2

只需读取文件的所有行,然后按制表符分割,即可访问每个列。

   var fileArray = File.ReadAllLines(myLocation);
    
        for(int i=0;i<fileArray.Length;i++)
        {
           var line=fileArray[i];

           if (i == 0)
           {  
              //handle column names
           }
           else
           {
             var columns = line.Split('\t');
             string value = columns[3];
           }
        }

文件可能很大,有没有更有效的方法,可以一次读取一行? - user2740190
请使用 File.ReadLines(myLocation) - Cam Bruce
1
修改后的答案也可以处理列名。 - Cam Bruce
1
在 for(int i=0;i< line<fileArray.Length;i++) 中,"line" 是什么?它甚至无法编译 :( - user2740190
1
抱歉,我之前的回答有错别字。 - Cam Bruce
2
不完整的示例 - 你需要定义什么是“line”。 - PhillyNJ

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接