根据以字符串开头的字符串列表创建子列表

时间:2017-02-27 04:00:59

标签: c# string linq

拳击时间发布,原谅我格式化... 我有一个文本文件,我已经使用File.ReadLines()读取并按顺序存储到List。 然后我想找到一个以" Student"开头的字符串的第一个实例。然后我想让列表中的所有字符串一直到以" Student"开头的字符串的下一个实例。就在它之前停下来。将这些字符串复制到子列表,然后冲洗并重复,直到到达文件末尾。

文本文件示例:

  

第1行的内容

     

第2行的内容

     

学生 ......:Joe Smith

     

Id ...:12345

     

少校......:数学

     

未知数量的更多行

     

学生 ...:简史密斯

     

Id ...:54321

     

少校......:护理

     

更多行

     

学生 ......:John Doe

     

Id ...:11223

     

专业:解剖学

     

甚至更多行。

我希望每个学生的行列表看起来像这样:

学生1

  

学生......:Joe Smith

     

Id ...:12345

     

少校......:数学

     

未知数量的更多行

学生2

  

学生......:简·史密斯

     

Id ...:54321

     

少校......:护理

     

更多行

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

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

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

3 个答案:

答案 0 :(得分: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();

根据您的示例数据,我得到了这个:

result

答案 1 :(得分:0)

像这样的东西

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"

答案 2 :(得分:-1)

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

现在你唯一需要做的就是改变fileName param。

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媒体资源中。