好吧,一种方法(虽然它有点难看)是使用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;
}
这是非常脆弱的代码——很多东西都会破坏它。如果其中一个事件缺少一行怎么办?如果里面有多个空行怎么办?如果其中一个 Convert.Toxxx 方法抛出错误怎么办?
如果您可以选择更改文件的格式,我强烈建议您至少将其分隔。如果您无法更改格式,则需要使上面的代码示例更加健壮,以便它可以处理空行、转换失败、缺少行等。
使用分隔文件要容易得多。更容易使用 XML 或 JSON 文件。
分隔文件 (CSV)
假设您有相同的示例输入,但这次是 CSV 文件,如下所示:
1,8:00 PM,25.00,“贝多芬第九交响曲”,“聆听路德维希·范·贝多芬的第九首也是最后一首杰作。”
2,6:00 PM,15.00,“棒球比赛”,“来看看冠军球队打他们的劲敌——保证不会停工”
我在最后两项上加上引号,以防其中有逗号,它不会破坏解析。
对于 CSV 文件,我喜欢使用Microsoft.VisualBasic.FileIO.TextFieldParser类,尽管它的名称可以在 C# 中使用。不要忘记添加对 Microsoft.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
方法而不是 Convert.Toxxx,但它比非定界示例更容易(我认为)。
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 PM|25.00|贝多芬第九交响曲|聆听路德维希·凡·贝多芬的第九部也是最后一部杰作。
2|6:00 PM|15.00,|棒球比赛|来观看冠军球队打他们的劲敌--保证不会停工
如果您愿意,您仍然可以使用TextFieldParser
上面的示例(只需将分隔符从 更改,
为|
,或者如果您愿意,您可以使用原始代码。
最后的一些想法
我还想解决原始代码并说明它为什么不起作用。主要原因是您一次读取一行,然后在“ ”上拆分。如果所有字段都在同一行上,这将是一个好的开始(尽管由于 Time、StrEvent 和 Description 字段中的空格仍然会出现问题),但事实并非如此。
因此,当您阅读第一行(即1
)并在“ ”上拆分时,您会得到一个值(1
)。当您尝试访问拆分数组的下一个元素时,您得到了索引超出范围错误,因为该行没有列 [1]。
本质上,您试图将每一行视为其中包含所有字段,而实际上每行一个字段。