如何将没有分隔符的文本文件行放入数组中

时间:2013-04-22 04:30:29

标签: c#

我有一个文本文件,我正在尝试输入一个名为columns的数组。 文本文件中的每一行都属于我创建的子类中的不同属性。

例如,我的文本文件中的第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:00 PM 
25.00 
Beethoven's 9th Symphony 
Listen to the ninth and final masterpiece by Ludwig van Beethoven. 
2 
6:00 PM 
15.00 
Baseball Game 
Come watch the championship team play their archrival--No work stoppages, guaranteed.

3 个答案:

答案 0 :(得分:2)

嗯,一种方法(虽然它有点难看)是使用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,"Beethoven's 9th Symphony","Listen to the ninth and final masterpiece by Ludwig van Beethoven."
2,6:00 PM,15.00,"Baseball Game","Come watch the championship team play their archrival--No work stoppages, guaranteed"

我在最后两个项目上加上引号,以防那里有逗号,它不会破坏解析。

对于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,但它比(非)更容易(我认为) -deimited sampe。

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|Beethoven's 9th Symphony|Listen to the ninth and final masterpiece by Ludwig van Beethoven.
2|6:00 PM|15.00,|Baseball Game|Come watch the championship team play their archrival--No work stoppages, guaranteed

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

最后的一些想法

我还想解决原始代码并说明它无法正常工作的原因。主要原因是你一次读一行,然后拆分''。如果所有字段都在同一行上,这将是一个良好的开端(尽管由于Time,StrEvent和Description字段中的空格而仍然存在问题),但它们不是。

因此,当您阅读第一行(1)并拆分''时,您会得到一个值(1)。当您尝试访问拆分数组的下一个元素时,索引超出了范围错误,因为该行没有列[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已经提到的那样,你应该考虑改变你的文件格式。

答案 2 :(得分:0)

如果您的侧列值没有相交char或具有固定大小,则可以删除

分隔符。在这种情况下,您可以读取文件和拆分字段。
如果你想从文件中读取并自动将数据加载到变量,我建议将serialize和deSeialize变量写入文件,但该文件不是文本文件!