C# 文件解析以获取特定信息
我有这样一个文件:C# 文件解析以获取特定信息,c#,regex,linq,split,pattern-matching,C#,Regex,Linq,Split,Pattern Matching,我有这样一个文件: -- Name John Smith, PhD [20,00] Title : Software Engineer [20,00] Employee Id : 20307 [20,00] Level : 41 [20,00] Start Date : 04/21/2014 [20,00] Org : Security Person person = new Person(); Dictionary<string, Object> FieldToDataMem
-- Name John Smith, PhD
[20,00] Title : Software Engineer
[20,00] Employee Id : 20307
[20,00] Level : 41
[20,00] Start Date : 04/21/2014
[20,00] Org : Security
Person person = new Person();
Dictionary<string, Object> FieldToDataMember = new Dictionary<string, Object>()
{
{"Name", person.Name},
{"Title", person.Title},
{"Level", person.Level}
};
每个文件只包含一个人的条目。我需要从该文件中提取名称、标题和级别,然后创建并填充以下类的对象:
public class Person
{
public string Name { get; set; }
public string Title { get; set; }
public string Level { get; set; }
}
一种方法是创建需要匹配的字符串列表:
List<string> properties = new List<string> { "Name", "Title", "Level" };
如果找到匹配项,我会进行一些字符串拆分和解析,以获得所需的值。但这将涉及大量的手工工作。是否有一种方法可以将字符串映射到类的变量并进行解析
我的意思是这样的:
-- Name John Smith, PhD
[20,00] Title : Software Engineer
[20,00] Employee Id : 20307
[20,00] Level : 41
[20,00] Start Date : 04/21/2014
[20,00] Org : Security
Person person = new Person();
Dictionary<string, Object> FieldToDataMember = new Dictionary<string, Object>()
{
{"Name", person.Name},
{"Title", person.Title},
{"Level", person.Level}
};
Person-Person=新的Person();
Dictionary FieldToDataMember=新字典()
{
{“Name”,person.Name},
{“Title”,person.Title},
{“级别”,person.Level}
};
现在我逐行读取文件,如果它与其中一个键匹配,我进行解析,它直接更新相应变量的值。这样,我不需要先找到是否存在匹配,然后再检查匹配的字符串,以便将其放入正确的变量中。这样的事情可能吗
谢谢你的帮助。谢谢
编辑:我还想退出循环(foreach(文件中的字符串行))并在找到我要查找的所有属性后停止进一步读取文件。使用属性名称字符串集合执行此操作的一种方法是使用反射来获取属性并设置值。与直接设置属性相比,这需要额外的开销,但正如您所要求的那样,代码行更少 我们可以使用字典或元组列表(或自定义类)将文件中的字符串映射为实际属性名(在
“开始日期”
和StartDate
等情况下)
下面是一个示例,我添加了一个publicstaticpersonfromfile(stringfilepath)
方法,该方法将接受一个文件路径,并返回一个新的Person
,其属性是根据文件内容设置的
它首先确定字符串数组中的任何属性名是否包含在文件行中。如果是,则它使用基于您的文件示例的一些逻辑来获取该属性的值,然后使用反射来设置人
对象的属性值:
public class Person
{
public string Name { get; set; }
public string Title { get; set; }
public string Level { get; set; }
public string StartDate { get; set; }
private class FileToPropertyMap
{
public string FileValue { get; }
public string PropertyName { get; }
public bool IsSet { get; set; }
public FileToPropertyMap(string fileValue, string propertyName)
{
FileValue = fileValue;
PropertyName = propertyName;
}
}
public static Person FromFile(string filePath)
{
if (!File.Exists(filePath)) throw new FileNotFoundException(nameof(filePath));
var person = new Person();
var propertyMap = new List<FileToPropertyMap>
{
new FileToPropertyMap("Name", "Name"),
new FileToPropertyMap("Title", "Title"),
new FileToPropertyMap("Level", "Level"),
new FileToPropertyMap("Start Date", "StartDate"),
};
foreach (var line in File.ReadLines(filePath))
{
// Find a match for one of the properties
var match = propertyMap.FirstOrDefault(p => line.Contains(p.FileValue));
if (match == null) continue;
// Get the value of the property from the file line
var value = line.Substring(line.IndexOf(match.FileValue) +
match.FileValue.Length).Trim();
if (value.Contains(':')) value = value.Split(':')[1].Trim();
// Set the property value using reflection
person.GetType().GetProperty(match.PropertyName).SetValue(person, value);
// Mark this property as "IsSet"
match.IsSet = true;
// If we've set all the properties, exit the loop
if (propertyMap.All(p => p.IsSet)) break;
}
return person;
}
}
请尝试以下操作:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;
using System.Text.RegularExpressions;
using System.IO;
namespace ConsoleApplication167
{
class Program
{
const string FILENAME = @"c:\temp\test.txt";
static void Main(string[] args)
{
List<Person> people = new List<Person>();
StreamReader reader = new StreamReader(FILENAME);
string line = "";
Person person = null;
while ((line = reader.ReadLine()) != null)
{
line = line.Trim();
if (line.Length > 0)
{
if (line.StartsWith("-- Name"))
{
person = new Person();
people.Add(person);
person.Name = line.Replace("-- Name", "").Trim();
}
else
{
string pattern = "](?'key'[^:]+):(?'value'.*)";
Match match = Regex.Match(line, pattern);
string key = match.Groups["key"].Value.Trim();
string value = match.Groups["value"].Value.Trim();
switch (key)
{
case "Title" :
person.Title = value;
break;
case "Level":
person.Level = value;
break;
}
}
}
}
}
}
public class Person
{
public string Name { get; set; }
public string Title { get; set; }
public string Level { get; set; }
}
}
使用系统;
使用System.Collections.Generic;
使用System.Linq;
使用系统文本;
使用System.Xml;
使用System.Xml.Linq;
使用System.Text.RegularExpressions;
使用System.IO;
命名空间控制台应用程序167
{
班级计划
{
常量字符串文件名=@“c:\temp\test.txt”;
静态void Main(字符串[]参数)
{
列表人员=新列表();
StreamReader=新的StreamReader(文件名);
字符串行=”;
Person=null;
而((line=reader.ReadLine())!=null)
{
line=line.Trim();
如果(直线长度>0)
{
if(line.StartsWith(“--Name”))
{
person=新的person();
人。添加(人);
person.Name=line.Replace(“--Name”,”).Trim();
}
其他的
{
字符串模式=“](?'key'[^::]+):(?'value.*);
Match=Regex.Match(线条、图案);
string key=match.Groups[“key”].Value.Trim();
字符串值=match.Groups[“value”].value.Trim();
开关(钥匙)
{
案例“标题”:
职位=价值;
打破
案例“级别”:
个人水平=价值;
打破
}
}
}
}
}
}
公共阶层人士
{
公共字符串名称{get;set;}
公共字符串标题{get;set;}
公共字符串级别{get;set;}
}
}
您可以使用反射从字符串数组中查找字符串和属性名,但它的性能不太好。您对性能更感兴趣还是更少的代码行?更少的代码行和更干净的代码,但不会对性能造成太大影响。我不想以很多if-else循环结束。您将这些字段定义为私有成员。你是不是想把它们变成公共财产?或者它们应该是私人领域?对不起,它们应该是公共领域。将更新问题。非常好!这应该行得通。但可能有一个小问题。如果将来,我想添加其他字段,比如开始日期,反射将无法工作,因为字段名称不匹配。有什么方法可以适应这种情况吗?如果您想向类中添加文件中具有不同字符串表示形式的属性,如文件中的StartDate
属性和开始日期,然后,您可以按照您最初的建议,使用字典在属性之间创建映射。我添加了一个示例,使用自定义类将文件行值映射到属性名称。还添加了一个“IsSet”属性,可以为我们设置的每个属性值设置该属性,以便在所有属性都设置好后停止读取文件。