如何获取没有分隔符的文本文件行到数组中

本文关键字:文件 文本 数组 分隔符 何获取 获取 | 更新日期: 2023-09-27 18:10:11

我有一个文本文件,我试图输入到一个数组称为列。文本文件中的每一行都属于我创建的子类中的不同属性。

例如,我的文本文件中的第2行是我想忽略的日期…我不想使用Split,因为我没有分隔符,但我不知道还有其他选择。如果有人能帮助我,我不完全理解下面的内容。当我尝试运行它时,它说列[1]超出了它的范围…谢谢你。

StreamReader textIn = 
    new StreamReader(
    new FileStream(path, FileMode.OpenOrCreate, FileAccess.Read));
//create the list
List<Event> events = new List<Event>();
while (textIn.Peek() != -1)
{
    string row = textIn.ReadLine();
    string[] columns = row.Split(' ');
    Event special = new Event();
    special.Day = Convert.ToInt32(columns[0]);
    special.Time = Convert.ToDateTime(columns[1]);
    special.Price = Convert.ToDouble(columns[2]);
    special.StrEvent = columns[3];
    special.Description = columns[4];
    events.Add(special);
}

输入文件示例:

<>之前1下午8点25.00贝多芬第九交响曲聆听路德维希·凡·贝多芬的第九部也是最后一部杰作。2下午6点15.00棒球比赛来看冠军球队和他们的劲敌的比赛吧——保证不停工。

如何获取没有分隔符的文本文件行到数组中

嗯,一种方法(虽然有点难看)是使用File.ReadAllLines,然后循环遍历数组,像这样:

string[] lines = File.ReadAllLines(path);
int index = 0;
while (index < lines.Length)
{
    Event special = new Event();
    special.Day = Convert.ToInt32(lines[index]);
    special.Time = Convert.ToDateTime(lines[index + 1]);
    special.Price = Convert.ToDouble(lines[index + 2]);
    special.StrEvent = lines[index + 3];
    special.Description = lines[index + 4];
    events.Add(special);
    lines = lines + 5;
}

这是非常脆弱的代码——很多人都可以破坏它。如果其中一个事件丢失了一行呢?如果其中有多个空行怎么办?如果其中一个皈依者。方法抛出错误?

如果您可以更改文件的格式,我强烈建议您至少将其分隔开。如果不能更改格式,则需要使上面的代码示例更加健壮,以便它可以处理空行、转换失败、缺失行等。

使用带分隔符的文件要容易得多。使用XML或JSON文件更容易。

带分隔符文件(CSV)

假设您有相同的示例输入,但这次是CSV文件,如下所示:

<>之前1、下午8:00,25.00,"贝多芬第九交响曲","聆听贝多芬的第九部也是最后一部杰作。"2、下午6:00,15.00,"棒球比赛","来观看冠军球队与劲敌的比赛——保证不停工"之前

我在最后两项上加了引号,以防里面有逗号,它不会中断解析。

对于CSV文件,我喜欢使用Microsoft.VisualBasic.FileIO.TextFieldParser类,尽管它的名字可以在c#中使用。别忘了加上微软的参考资料。VisualBasic和using指令(using Microsoft.VisualBasic.FileIO;).

下面的代码将允许您解析上面的CSV示例:
using (TextFieldParser parser = new TextFieldParser(path))
{
    parser.Delimiters = new string[] {","};
    parser.TextFieldType = Delimited;
    parser.HasFieldsEnclosedInQuotes = true;
    string[] parsedLine;
    while (!parser.EndOfData)
    {
        parsedLine = parser.ReadFields();
        Event special = new Event();
        special.Day = Convert.ToInt32(parsedLine[0]);
        special.Time = Convert.ToDateTime(parsedLine[1]);
        special.Price = Convert.ToDouble(parsedLine[2]);
        special.StrEvent = parsedLine[3];
        special.Description = parsedLine[4];
        events.Add(special);    
    }
}

这仍然有一些问题-你需要处理有缺失字段的情况,我建议使用TryParse方法而不是转换。但是(我认为)它比没有分隔的样本要简单一些。

XML文件(使用LINQ to XML)

现在让我们对XML文件进行尝试,并使用LINQ to XML来获取数据:

<Events>
  <Event>
    <Day>1</Day>
    <Time>8:00 PM</Time>
    <Price>25.00</Price>
    <Title><![CDATA[Beethoven's 9th Symphone]]></Title>
    <Description><![CDATA[Listen to the ninth and final masterpiece by Ludwig van Beethoven.]]></Description>
  </Event>
  <Event>
    <Day>2</Day>
    <Time>6:00 PM</Time>
    <Price>15.00</Price>
    <Title><![CDATA[Baseball Game]]></Title>
    <Description><![CDATA[Come watch the championship team play their archrival--No work stoppages, guaranteed]]></Description>
  </Event>
</Events>

我使用CDATA作为标题和描述,以便特殊字符不会破坏XML解析。

这很容易通过以下代码解析为您的事件:

XDocument doc = XDocument.Load(path);
List<Event> events = (from x in doc.Descendants("Event")
                     select new Event {
                                Day = Convert.ToInt32(x.Element("Day").Value),
                                Time = Convert.ToDateTime(x.Element("Time").Value),
                                Price = Convert.ToDouble(x.Element("Price").Value),
                                StrEvent = x.Element("Title").Value,
                                Description = x.Element("Description").Value
                     }).ToList();

当然,这仍然不是完美的,因为您仍然有转换失败或缺少元素的可能性。

以管道分隔的文件示例

根据我们在评论中的讨论,如果您想使用管道(|),则需要将每个事件(完整地)放在一行中,如下所示:

<>之前1|晚上8:00 |25.00|贝多芬第九交响曲|聆听贝多芬的第九部也是最后一部杰作。2、棒球赛:来观看冠军球队与劲敌的比赛——保证不停工之前

如果你喜欢,你仍然可以使用上面的TextFieldParser示例(只是将分隔符从,更改为|,或者如果你愿意,你可以使用原始代码。

一些最后的想法

我还想指出原始代码,并说明为什么它不能工作。主要原因是你每次读一行,然后在' '上分裂。如果所有的字段都在同一行,这将是一个很好的开始(尽管它仍然会有问题,因为时间,StrEvent和描述字段中的空格),但它们不是。

因此,当您读取第一行(这是1)并分割' '时,您得到一个值(1)。当您试图访问拆分数组的下一个元素时,您得到了索引超出范围错误,因为该行没有列[1]。

从本质上讲,您试图将每行视为包含所有字段,而实际上每行只有一个字段。

对于给定的示例文件,如

string[] lines = File.ReadAllLines(path);
for (int index = 4; index < lines.Length; index += 5)
{
    Event special = new Event();
    special.Day = Convert.ToInt32(lines[index - 4]);
    special.Time = Convert.ToDateTime(lines[index - 3]);
    special.Price = Convert.ToDouble(lines[index - 2]);
    special.StrEvent = lines[index - 1];
    special.Description = lines[index];
    events.Add(special);
}

可以,但是就像Tim已经提到的,你应该考虑改变你的文件格式。

分隔符可以删除,如果您的侧列值没有相交字符或有固定的大小。在这种情况下,你可以读取文件和分割字段。
如果你想从文件中读取并自动加载数据到变量,我建议序列化和反序列化变量到文件,但该文件不是文本文件!