读取文本文件中特定字符串后的行,然后将数据存储在列表中

Reading lines after specific string in a text file then storing data in lists

我有一个读取文本文件的程序,我希望它在文本文件中的某个标题之后收集数据,在本例中为 [HRData]。一旦 streamreader 达到 [HRData],我希望它读取之后的每一行并将每一行存储在列表中,但允许我访问单独的数字。

文本文件是这样的:

[HRZones]
190
175
162
152
143
133
0
0
0
0
0

[SwapTimes]

[Trip]
250
0
3978
309
313
229
504
651
//n header 
[HRData]
91  154 70  309 83  6451
91  154 70  309 83  6451
92  160 75  309 87  5687
94  173 80  309 87  5687
96  187 87  309 95  4662
100 190 93  309 123 4407
101 192 97  309 141 4915
103 191 98  309 145 5429

所以参考文本文件,我希望它在 [HRData] 之后存储第一行并允许我访问每个变量,例如 91[0]

如果单词与正则表达式匹配,我的代码已经存储到列表中,但我不知道如何编写代码以在 [HRData] 等特定字符串之后读取。

if (squareBrackets.Match(line).Success) {
 titles.Add(line);
 if (textAfterTitles.Match(line).Success) {
  textaftertitles.Add(line);

 }
}

这是我目前的尝试:

if (line.Contains("[HRData]")) {
 inttimes = true;
 MessageBox.Show("HRDATA Found");
 if (inttimes == true) {
  while (null != (line = streamReader.ReadLine())) {
   //ADD LINE AND BREAK UP INTO PARTS S
  }
 }
}

你可以调用一个 LINQ 友好的方法 File.ReadLines ,然后你可以使用 LINQ 得到你想要的部分:

List<string> numbers = File.ReadLines("data.txt")
                           .SkipWhile(line => line != "[HRData]") 
                           .Skip(1)
                           .SelectMany(line => line.Split())
                           .ToList();

Console.WriteLine(numbers[0]); // 91

编辑 - 这将为您提供一个 List<string> 中的所有数字,如果您想保持行序,请使用 Select 而不是 SelectMany:

List<List<string>> listsOfNums = File.ReadLines("data.txt")
                                     .SkipWhile(line => line != "[HRData]") 
                                     .Skip(1)
                                     .Select(line => line.Split().ToList())
                                     .ToList();

请注意,这需要额外的索引才能获得单个数字:

Console.WriteLine(listsOfNums[0][0]); // 91

假设您当前的代码尝试有效,但我尚未验证...

您可以简单地执行以下操作:

List<int> elements = new List<int>();
while (null != (line = streamReader.ReadLine())) 
{
    if(line.Contains("["))
    {
        //Prevent reading in the next section
        break;
    }
    string[] split = line.Split(Convert.ToChar(" "));
    //Each element in split will be each number on each line.
    for(int i=0;i<split.Length;i++)
    {
        elements.Add(Convert.ToInt32(split[i]));
    }

}

或者,如果您想要一个二维列表,以便您可以按行引用数字,您可以使用嵌套列表。对于外循环的每个 运行,创建一个新列表并将其添加到元素(元素将是 List<List<int>>)。

编辑

请注意,请注意 Convert.ToInt32() 函数。它真的应该在 try catch 语句中,以防万一读取了一些不是数字的文本。

编辑

好的..使例程更健壮(根据我在下面的评论):

首先确保例程不会超出您的号码范围。我不确定你列出的块之外还有什么,所以这将取决于你,但它应该采用以下形式:

If(line.Contains("[") || line.Contains("]") || etc etc etc)
{
    break;
}

下一步是预先格式化您的拆分值。在 for 语句中:

for(int i=0;i<split.Length;i++)
{
    string val = split[i].Trim(); //Get rid of white space
    val = val.Replace("\r\n","");  //Use one of these to trim every character.
    val = val.Replace("\n","");
    try
    {
        elements.Add(Convert.ToInt32());
    }
    catch (Exception ex)
    {
        string err = ex.Message;
        //You might try formatting the split value even more here and retry convert
    }

}

要访问单个数字(假设您使用的是单一维度列表),有几种方法可以做到这一点。如果要按索引值访问:

elements.ElementAt(index)

如果您想遍历值列表:

foreach(int val in elements)
{
}

如果您需要确切知道值来自哪一行,我建议使用二维列表。它将按如下方式实现(我正在从原始代码片段中复制我的代码,因此假设添加了所有错误检查!)

List<List<int>> elements = new List<List<int>>();
while (null != (line = streamReader.ReadLine())) 
{
    if(line.Contains("["))
    {
        //Prevent reading in the next section
        break;
    }
    List<int> newLine = new List<int>();
    string[] split = line.Split(Convert.ToChar(" "));
    //Each element in split will be each number on each line.
    for(int i=0;i<split.Length;i++)
    {
        newLine.Add(Convert.ToInt32(split[i]));
    }
    elements.Add(newLine);
}

现在按行访问每个元素:

foreach(var line in elements)
{
    //line is a List<int>
    int value = line.ElementAt(index); //grab element at index for the given line.
}

或者,如果您需要直接通过行索引和列索引进行引用

int value = elements.ElementAt(lineIndex).ElementAt(columnIndex);

小心所有这些直接索引引用。您可以很容易地得到索引越界问题。

另一件事.. 你应该在你的 Convert.ToInt 语句上放置一个断点并找到它中断的字符串。如果您可以假设数据输入是一致的,那么准确地找到破坏转换的字符串将帮助您创建一个例程来处理正在过滤的特定字符。我猜想该方法在尝试时中断了将最后一个拆分值转换为整数,我们没有删除行尾。

您可以使用变量来跟踪当前部分:

var list = new List<int[]>();
using (StreamReader streamReader = ...)
{
    string line;
    string sectionName = null;
    while (null != (line = streamReader.ReadLine()))
    {
        var sectionMatch = Regex.Match(line, @"\s*\[\s*(?<NAME>[^\]]+)\s*\]\s*");
        if (sectionMatch.Success)
        {
            sectionName = sectionMatch.Groups["NAME"].Value;
        }
        else if (sectionName == "HRData")
        {
            // You can process lines inside the `HRData` section here.

            // Getting the numbers in the line, and adding to the list, one array for each line.
            var nums = Regex.Matches(line, @"\d+")
                .Cast<Match>()
                .Select(m => m.Value)
                .Select(int.Parse)
                .ToArray();

            list.Add(nums);
        }
    }
}