C# 如何分割周期性文本?

C# 如何分割周期性文本?,c#,.net,linq,C#,.net,Linq,我有一个文本文件,它在新行中包含人员信息,如下所示。这个数据包含大约500人 Person name: abc age: 40 . Person name: xyx age: 18 . Person name: uke age: 27 . 我想阅读此文本文件并将其解析为Personclass。我可以读取文件: string path = @"c:\temp\person.txt"; string readText = File.ReadAllText(path); 但我无法在“person”和

我有一个文本文件,它在新行中包含人员信息,如下所示。这个数据包含大约500人

Person name: abc age: 40 . Person name: xyx age: 18 . Person name: uke age: 27 . 我想阅读此文本文件并将其解析为
Person
class。我可以读取文件:

string path = @"c:\temp\person.txt";
string readText = File.ReadAllText(path);
但我无法在“person”和“.”字符之间解析每个人。 我不想使用
if-else
条件。使用正则表达式或
Split
或LINQ有实际的解决方案吗

阅读后,我想得到如下列表:

var person = List<Person> {
   new Person { Name = "abc", Age = 40 },
   new Person { Name = "xyx", Age = 18 },
   new Person { Name = "uke", Age = 27 }
   ....
   ...
   ..
}
var person=List{
新人{Name=“abc”,年龄=40},
新人{Name=“xyx”,年龄=18},
新人{Name=“uke”,年龄=27}
....
...
..
}

如果使用
File.ReadAllLines()
而不是
.ReadAllText()
,则可以使用装箱函数。我自己也写过其中一个,所以如果您想要开箱即用的东西(没有双关语),那么您可以像这样安装和使用它:

var readText = File.ReadAllLines(path);

var people = readText
    .BoxWhile((a, b) => b != ".")
    .Select(x => x
            .Where(t => t != "." && t != "Person")
            .Select(t => t.Split(':').Last().Trim())
        .ToList())
    .Where(x => x.Any())
    .Select(x => new Person
    {
        Name = x[0],
        Age = x[1]
    });

如果您想知道这在内部是如何工作的,那么可以查看BoxedEnumerable是如何实现的。它使用带有比较运算符的滑动窗口;每次当前元素和下一个元素之间的比较返回false时,它都会启动一个新的“框”。你的问题实际上是我最近看到的几个例子中的一个,在这些例子中,比较不需要看当前和下一个,只需要看下一个,因此我将很快添加一个额外的方法。

你可以将文本按点拆分,这将为每个人提供信息,然后按新行拆分每个人,
\r
\r\n
取决于文件结尾的行。然后,您可以从第二行(索引为1)获取姓名,从第三行(索引为2)获取年龄。最后,用冒号和空格分隔姓名和年龄:“并获得第二个字符串(索引为1)。这假设您的文件结构是固定的,永远不会更改,否则您需要根据您所说的要避免的条件查找名称和年龄:

var persons = new List<Person>();
var personInfo = readText.Split(new char[]{'.'}, StringSplitOptions.RemoveEmptyEntries);
foreach (var i in personInfo)
{
    var person = new Person();
    var lines = i.Split(new char[]{'\n'}, StringSplitOptions.RemoveEmptyEntries);
    person.Name = lines[1].Split(new string[]{": "}, StringSplitOptions.None)[1];
    person.Age = lines[2].Split(new string[]{": "}, StringSplitOptions.None)[1];
    persons.Add(person);
}

更简单的选择可能是按“人”或“姓名”拆分:

List people=File.ReadAllText(@“c:\temp\person.txt”)
.Split(新[]{“名称:”},StringSplitOptions.None)
.Skip(1)
.Select(p=>p.Split('\n',':'))
.选择(a=>新人员{
Name=a[0]。Trim(),
年龄=a[2]。修剪()
})
.ToList();

使用一元语法分析器的解决方案不简单但功能强大:

您可以组合解析器来解析非常复杂的结构

SplitTestSample.csproj

<PropertyGroup>
  <OutputType>Exe</OutputType>
  <TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
  <PackageReference Include="Sprache" Version="2.1.2" />
</ItemGroup>


File.ReadAllLines“我不想使用if-else条件。”。。。为什么?格式有什么不同吗?这意味着,即使是这500人中的一人,在他们的名字之后,在他们的年龄之前,还会有额外的财产吗?这只是一个例子,你有更多的字段吗?它们可以变化吗?请记住,如果我们不知道您所陈述的问题的一般性,那么很难获得特定问题的一般性答案。解析特定的文件格式相当容易,但如果它开始变化,那么就不一样了。为什么年龄属性是
字符串
?它应该是数字类型,根据您的示例,可能是
int
。然后,您可以将接受的答案与
int.Parse
结合使用,这实际上还不起作用-您还需要在空格或冒号上拆分,否则此人的名字将是“name:abc”。@MineR Oops!我用它进行测试,但忘了将其移除。更新了我的答案。谢谢你的来信。顺便说一下,最好用冒号和空格分开,因为有些名称可能有空格。如果单独按冒号分割,则需要修剪多余的空间。
var persons = readText.Split(new char[]{'.'}, StringSplitOptions.RemoveEmptyEntries)
                      .Select(i => i.Split(new char[]{'\n'}, StringSplitOptions.RemoveEmptyEntries))
                      .Select(l => new Person{
                          Name = l[1].Split(new string[]{": "}, StringSplitOptions.None)[1],
                          Age = l[2].Split(new string[]{": "}, StringSplitOptions.None)[1]
                      });
List<Person> people = File.ReadAllText(@"c:\temp\person.txt")
    .Split(new[] { "name:" }, StringSplitOptions.None)
    .Skip(1)
    .Select(p => p.Split('\n', ':'))
    .Select(a => new Person { 
         Name = a[0].Trim(), 
         Age = a[2].Trim() 
    })
    .ToList();
<PropertyGroup>
  <OutputType>Exe</OutputType>
  <TargetFramework>netcoreapp2.1</TargetFramework>
</PropertyGroup>

<ItemGroup>
  <PackageReference Include="Sprache" Version="2.1.2" />
</ItemGroup>
using System;
using System.Collections.Generic;
using Sprache;

namespace SplitTextSample
{
    class Program
    {
        public static string Text =
@"Person
name: abc
age: 40
.
Person
name: xyx
age: 18
.
Person
name: uke
age: 27
.";

        public class Person
        {
            public string Name { get; set; }
            public string Age { get; set; }
        }

        static void Main(string[] args)
        {
            // Parses: Person\r\n
            var personTagParser = Parse.String("Person").Then(_=>Parse.LineEnd);

            // Parses: name: {name}\r\n
            Parser<string> nameParser = from tag in Parse.String("name:").Token()
                from name in Parse.AnyChar.Except(Parse.LineEnd).AtLeastOnce().Text()
                from lineEnd in Parse.LineEnd
                select name;

            // Parses: age: {age}\r\n.[\r\n]
            Parser<string> ageParser = from tag in Parse.String("age:").Token()
                from age in Parse.AnyChar.Except(Parse.LineEnd).AtLeastOnce().Text()
                from delimeter in Parse.LineEnd.Then(_ => Parse.Char('.')).Then(_=> Parse.LineEnd.Optional())
                select age;

            // Parses: Person\r\nname: {name}\r\nage: {age}\r\n.[\r\n]
            Parser<Person> personParser =
                from personTag in personTagParser
                from name in nameParser
                from age in ageParser
                select new Person{Name = name, Age = age};

            // Parses: Many persons
            var personsParser = personParser.Many();

            // Final parse returns IEnumerable<Person>
            var persons = personsParser.Parse(Text);

            foreach (var person in persons)
            {
                Console.WriteLine($"Name: {person.Name}, Age: {person.Age}");
            }

            Console.ReadLine();
        }

}
Name: abc, Age: 40
Name: xyx, Age: 18
Name: uke, Age: 27