【问题标题】:Create sub lists from list of strings based on string starts with根据字符串开头从字符串列表创建子列表
【发布时间】:2017-02-27 13:14:36
【问题描述】:

第一次发帖,请原谅我的格式... 我有一个使用 File.ReadLines() 读取并按顺序存储到列表中的文本文件。 然后我想找到以“Student”开头的字符串的第一个实例。然后我想获取列表中的所有字符串,直到下一个以“Student”开头的字符串实例并在它之前停止。将这些字符串复制到子列表中,然后冲洗并重复,直到到达文件末尾。

文本文件示例:

第 1 行的东西

第 2 行的东西

学生...:乔·史密斯

ID...:12345

主修...:数学

不知道还有多少行

学生...:简·史密斯

ID...:54321

专业...:护理

更多行

学生...:John Doe

ID...:11223

专业:解剖学

更多行。

我希望每个学生的行列表如下所示:

学生 1

学生...:乔·史密斯

ID...:12345

主修...:数学

不知道还有多少行

学生 2

学生...:简·史密斯

ID...:54321

专业...:护理

更多行

我使用了一个 foreach 来迭代这些行。每行都添加到一个新列表中。当我找到一个以“Student”开头的字符串时,我会创建一个新的学生对象并将这些行存储在子列表中。然后我清除子列表,然后继续 foreach,创建新的学生对象。

当前问题 我想念最后一个学生。我知道我可以使用 if 语句来检查当前行是否以“Student”开头,包括检查当前行是否是列表中的最后一行,但我觉得必须有更好/更快的方法来做到这一点。
我必须添加 && lines.Count > 3,因为在“Student”的第一个实例之前有几行我想跳过。

Linq 示例将不胜感激。

List<Student> students = new List<Student>();
List<string> lines = File.ReadLines(args[0]).ToList();  
List<string> student_lines = new List<string>();
foreach(string line in lines) 
{ 
    if(line.StartsWith("Student...", StringComparison.OrdinalIgnoreCase) && lines.Count > 3) 
    {
        students.Add(new Student(student_lines)); 
        student_lines.Clear(); 
    } 
    lines.Add(line)
}

【问题讨论】:

  • 更容易File.ReadAllText,在“学生”上拆分,并按\r\n拆分部分
  • 你能详细说明一下“学生”的分裂吗?

标签: c# string linq


【解决方案1】:

试试这个:

var results =
    lines
        .Aggregate(new[] { new List<string>() }.ToList(), (a, x) =>
        {
            if (x.StartsWith("Student"))
            {
                a.Add(new List<string>());
            }
            a.Last().Add(x);
            return a;
        })
        .Skip(1)
        .Select(x => new Student(x))
        .ToList();

从您的示例数据中我得到:

【讨论】:

  • 这就是我要找的。我已经有一段时间没有使用 C# 了,所以我将看看这里发生了什么。感谢您的意见!
  • 出色的工作!这正是我想要的。效果很好!
【解决方案2】:

像这样的

if (args.Length < 1) return;                // optional check if any args

string text = File.ReadAllText(args[0]);

string[] parts = text.Split(new[] { "Student" }, 0);

string[][] lines = Array.ConvertAll(parts, part => part.Split(new[] { '\r', '\n' }, 1));

Debug.Print(lines[1][1]);        // "Id...: 12345"

【讨论】:

    【解决方案3】:

    你想要做什么真的很模糊,但这是我认为你在做什么的一个非常基本的例子。

    现在您唯一需要做的就是更改 fileName 参数。

    static void Main(string[] args)
    {
        string line = null;
        Student student = null;
        IList<Student> students = new List<Student>();
        using (var fileReader = new StreamReader(fileName))
        {
            while ((line = fileReader.ReadLine()) != null)
            {
                if (string.IsNullOrWhiteSpace(line))
                    continue; //continue execution on extra lines
                if(line.StartsWith("Student...", StringComparison.CurrentCultureIgnoreCase))
                {
                    student = new Student();
                    students.Add(student);
                }
    
                if (student != null)
                    student.Lines.Add(line);
    
    
            }
    
            fileReader.Close();
        }
    }
    
    class Student
    {
        public IList<string> Lines { get; } = new List<string>();
    }
    

    简而言之,这个小程序所做的就是开始逐行读取文件(注意while ((line = fileReader.ReadLine()) != null) 会将line 的值设置为文件中的下一行或null 当文件已被完全读取时.

    接下来它检查空行(我们不关心空行,所以我们继续)

    然后我们检查 line 是否开始和 Student... 现在我们正在使用 StringComparison.CurrentCultureIgnoreCase 以便我们可以比较不区分大小写。

    如果这是一个学生行,我们将创建一个新学生并将Add() 它添加到我们的students 集合中。

    最后只要student 不为空,我们就可以将line 的内容添加到学生.Lines 属性中。

    【讨论】:

    • 不知道为什么投反对票。谢谢(你的)信息。我从这种方法开始,但决定输入文本文件不是那么大(可能有 100k 行),而我想使用 ReadLines() 函数。
    猜你喜欢
    • 2016-02-13
    • 1970-01-01
    • 2011-04-20
    • 2020-04-21
    • 1970-01-01
    • 1970-01-01
    • 2019-11-08
    • 2019-07-07
    • 2016-12-02
    相关资源
    最近更新 更多