【问题标题】:Reading lines after specific string in a text file then storing data in lists在文本文件中的特定字符串之后读取行,然后将数据存储在列表中
【发布时间】:2016-03-07 18:13:57
【问题描述】:

我有一个读取文本文件的程序,我希望它在文本文件中的某个标题之后收集数据,在本例中为 [HRData]。一旦流读取器到达[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
  }
 }
}

【问题讨论】:

  • 您可以读取 HRDATA 之后的行,将每一行拆分为数据。当你看到是什么让你停下来时停下来。
  • 是的,这就是我想要达到的目标,但这就是我问这个问题的原因,我不知道如何告诉我的代码在 [HRData] 之后读取
  • 因为我当前的 if 语句 if (line.Contains("[HRData]")) 只读取包含它的行,而我希望它在那之后读取。
  • while (null != (line = streamReader.ReadLine())) { arr = line.Split(" "); }

标签: c# regex streamreader


【解决方案1】:

您可以调用 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&lt;string&gt; 中为您提供所有数字,如果您想保持线路顺序,请使用 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

【讨论】:

  • 这看起来非常漂亮和简单,我将如何访问 154,会不会是 [1]?那么下一行呢?谢谢。
【解决方案2】:

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

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);
        }
    }
}

【讨论】:

    【解决方案3】:

    假设您当前的代码尝试有效,我还没有通过验证...

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

    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&lt;List&lt;int&gt;&gt;)。

    编辑

    请注意,请小心使用 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)
    {
    }
    

    如果您需要确切知道值来自哪一行,我建议您使用 2d 列表。它将实现如下(我从原始代码 sn-p 复制我的代码,因此假设添加了所有错误检查!)

    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 语句上放置一个断点并找出它正在中断的字符串。如果您可以假设数据输入将是一致的,那么准确找到破坏转换的字符串将帮助您创建一个处理正在过滤的特定字符的例程。我猜该方法在尝试将最后一个拆分值转换为整数,我们没有删除行尾。

    【讨论】:

    • 非常感谢。它似乎运行良好。然后我怎么能调用存储的号码?例如,如果我想获得第一个数字?
    • 另外,我收到错误输入字符串格式不正确。就行 elements.Add(Convert.ToInt32(split[i]));
    • split 的元素之一是非数字,导致您的错误。我可能会加强'break'语句,以确保它不会首先读取非数字行。然后,将转换放入 try-catch 中,以在值非数字的情况下捕获错误。我将编辑我的答案以提供更多信息。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-03-30
    • 1970-01-01
    • 2020-01-13
    • 1970-01-01
    • 2019-06-21
    相关资源
    最近更新 更多