【问题标题】:How to split periodic text?如何拆分周期性文本?
【发布时间】:2018-06-03 09:44:35
【问题描述】:

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

人 名称:abc 年龄:40 . 人 名称:xyx 年龄:18 . 人 姓名:尤克 年龄: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);

但我无法在“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 }
   ....
   ...
   ..
}

【问题讨论】:

  • "我不想使用 if else 条件。" ...为什么?
  • 格式是否完全不同?意思是,即使对于这 500 人中的一个人来说,在名字之后,在年龄之前,是否有额外的财产?这只是一个例子,你有更多的领域吗?它们可以变化吗?请记住,如果我们不知道您所陈述的问题的一般性,那么很难获得特定问题的一般性答案。解析那个SPECIFIC文件格式相当容易,但如果它开始变化,那就不是了。
  • 为什么您的 Age 属性是 string?它应该是数字类型,可能是 int 根据您的样本。然后,您可以将接受的答案与int.Parse 结合使用

标签: c# .net linq


【解决方案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]
                      });

【讨论】:

  • 这实际上还不起作用 - 你还需要在空格或冒号上分割,否则这个人的名字将是“name:abc”。
  • @MineR 哎呀!我用它来测试并忘记删除它。更新了我的答案。感谢您的注意。顺便说一句,最好用冒号和空格分开,因为有些名字可能有空格。如果你只用冒号分割,你需要修剪多余的空间。
【解决方案2】:

如果您使用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 时,它​​都会启动一个新的“框”。您的问题实际上是我最近看到的几种情况之一,其中比较不需要查看当前和下一个,只需查看下一个,所以我很快就会为此添加一个额外的方法。

【讨论】:

    【解决方案3】:

    更简单的选择可能是按“Person”或“name:”分割

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

    【讨论】:

      【解决方案4】:

      使用单子解析器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
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-06-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多