如何拆分定期文字?

时间:2018-06-03 09:44:36

标签: c# .net linq

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

Person
name: abc
age: 40
.
Person
name: xyx
age: 18
.
Person
name: uke
age: 27
.

人员信息介于" Person"和"。"字符。我创建了一个Person类来获取信息: 本文

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

我想阅读此文本文件并将其解析为Person类。我可以阅读文件:

string path = @"c:\temp\person.txt";
string readText = File.ReadAllText(path);

但是我无法解析每个人之间的#34; Person"和"。"字符。 我不想使用if else条件。是否有使用Regex或Split或LINQ?

的实用解决方案

我希望在阅读后获得这样的列表:

var person = List<Person> {
   new Person { Name = "abc", Age = 40 },
   new Person { Name = "xyx", Age = 18 },
   new Person { Name = "uke", Age = 27 }
   ....
   ...
   ..
}

4 个答案:

答案 0 :(得分:1)

如果使用File.ReadAllLines()代替.ReadAllText(),则可以使用装箱功能。我自己写过其中一个,所以如果你想要一些开箱即用的东西(没有双关语)那么你可以安装Anaximander.Linq并像这样使用它:

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

如果您想知道内部如何工作,那么source code is available on GitHub以便您可以了解BoxedEnumerable的实现方式。它使用带有比较运算符的滑动窗口;每当当前元素和下一个元素之间的比较返回false时,它就会启动一个新的“框”。您的问题实际上是我最近看到的几个案例中的一个,其中比较不需要查看当前和下一个,只有下一个,所以我将很快为其添加一个额外的方法。

答案 1 :(得分:1)

您可以按点.分割文字,为您提供每个人的信息,然后按新行\n\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);
}

或者,您可以使用LINQ:

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

答案 2 :(得分:0)

更容易的替代方案可能是&#34; Person&#34;或&#34;名称:&#34;

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

答案 3 :(得分:0)

使用monadic解析器Sprache不是简单但非常强大的解决方案:

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

<强> SplitTestSample.csproj

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

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

<强> Program.cs的

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