问题描述
我有一个看起来像这样的文件:
-- 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
每个文件仅包含一个人的条目。我需要从该文件中提取名称,标题和级别,然后创建并填充以下类的对象:
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" };
然后逐行读取文件并尝试找到匹配项:
properties.Any(x => line.Contains(x))
如果找到匹配项,我将进行一些字符串拆分和解析以获取所需的值。但这将涉及大量的手工工作。有没有办法将字符串映射到类的变量并进行解析?
我的意思是这样的:
Person person = new Person();
Dictionary<string,Object> FieldToDataMember = new Dictionary<string,Object>()
{
{"Name",person.Name},{"Title",person.Title},{"Level",person.Level}
};
现在,我逐行读取文件,如果它与键之一匹配,我将进行解析,并直接更新相应变量的值。这样,我不需要先查找是否存在匹配项,然后再检查匹配的字符串以将其放入正确的变量中。这样可能吗?
感谢您的帮助。谢谢!
编辑:我也想退出循环(foreach(文件中的字符串行)),并在找到所需的所有属性后停止进一步读取文件。
解决方法
使用一组属性名称字符串完成此操作的一种方法是使用反射来获取属性并设置值。与直接设置属性相比,这需要额外的开销,但是所需的代码行却更少。
我们可以使用字典或元组列表(或自定义类)将文件中的字符串与实际的属性名称(在=INT(--SUBSTITUTE(MID(A1,FIND(" ",A1)+1,999),","",2))
和"Start Date"
等情况下进行映射。) >
在此示例中,我添加了一个StartDate
方法,该方法将采用文件路径并返回具有根据文件内容设置的属性的新public static Person FromFile(string filePath)
。
通过首先确定字符串数组中的任何属性名称是否包含在文件行中来工作。如果是,那么它将基于您的文件样本使用一些逻辑来获取该属性的值,然后使用反射来设置Person
对象的属性值:
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; }
}
}