代码之家  ›  专栏  ›  技术社区  ›  barteloma

如何拆分定期文本?

  •  2
  • barteloma  · 技术社区  · 7 年前

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

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

    个人信息介于“个人”和“.”字符之间。我创造了一个 Person 类获取信息: 此文本

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

    我想读取这个文本文件并将其解析为 上课。我可以读取文件:

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

    但我无法在“person”和“.”字符之间解析每个人。 我不想用 if else 条件。是否有使用regex或 Split 还是林肯?

    我想在阅读后得到这样一个列表:

    var person = List<Person> {
       new Person { Name = "abc", Age = 40 },
       new Person { Name = "xyx", Age = 18 },
       new Person { Name = "uke", Age = 27 }
       ....
       ...
       ..
    }
    
    4 回复  |  直到 7 年前
        1
  •  1
  •   Racil Hilan    7 年前

    你可以用点把文字分开 . 它提供每个人的信息,然后用新行分割每个人 \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
  •  1
  •   anaximander Chetan Gaikwad    7 年前

    如果你使用 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时,它都会启动一个新的“box”。你的问题实际上是我最近看到的几个例子中的一个,比较不需要看当前和下一个,只需要看下一个,所以我很快会为这个添加一个额外的方法。

        3
  •  0
  •   Slai    7 年前

    更简单的选择可能是按“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
  •  0
  •   Alexey.Petriashev    7 年前

    不简单,但非常强大的解决方案使用一元解析器 Sprache :

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

    SplitTestSample.csproj文件

    <PropertyGroup>
      <OutputType>Exe</OutputType>
      <TargetFramework>netcoreapp2.1</TargetFramework>
    </PropertyGroup>
    
    <ItemGroup>
      <PackageReference Include="Sprache" Version="2.1.2" />
    </ItemGroup>
    

    程序.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