C# 如何分割周期性文本?
我有一个文本文件,它在新行中包含人员信息,如下所示。这个数据包含大约500人 Person name: abc age: 40 . Person name: xyx age: 18 . Person name: uke age: 27 . 我想阅读此文本文件并将其解析为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”和
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