文件解析以获取特定信息

Posted

技术标签:

【中文标题】文件解析以获取特定信息【英文标题】:File parsing to get specific information 【发布时间】:2020-12-28 04:20:57 【问题描述】:

我有如下文件:

-- 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

每个文件都包含一个仅适用于 1 个人的条目。我需要从该文件中提取名称、标题和级别,然后创建并填充以下类的对象:

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(文件中的字符串行))并在找到我要查找的所有属性后停止进一步读取文件。

【问题讨论】:

您可以使用反射从字符串数组中找到字符串和属性名称,但它的性能不会很好。您对性能更感兴趣还是对代码行更少感兴趣? 行数更少,代码更简洁,但对性能没有太大影响。我不想以大量 if-else 循环结束。 您将这些字段定义为私有成员。您的意思是让它们改为公共属性吗?还是它们应该是私有字段? 对不起,他们应该是公开的。将更新问题。 【参考方案1】:

使用属性名称字符串集合的一种方法是使用反射来获取属性并设置值。与直接设置属性相比,这需要额外的开销,但它需要的代码行数更少。

我们可以使用字典或元组列表(或自定义类)将文件中的字符串映射到实际的属性名称(例如"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;
    

在使用中,这看起来像:

Person myPerson = Person.FromFile("@c:\Public\PeopleFiles\JohnSmith.txt");

【讨论】:

太棒了!这应该有效。但可能有一个小问题。如果将来我想添加其他字段,比如开始日期,反射将不起作用,因为字段名称不匹配。有什么办法可以适应吗? 如果您想在文件中添加具有不同字符串表示的类的属性,例如文件中的StartDate 属性和"Start Date",那么您可以使用你最初建议的字典。 我添加了一个使用自定义类将文件行值映射到属性名称的示例 还添加了一个“IsSet”属性,可以为我们设置的每个属性值设置,这样我们就可以在它们都设置好后停止读取文件。【参考方案2】:

尝试以下:

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; 
    

【讨论】:

以上是关于文件解析以获取特定信息的主要内容,如果未能解决你的问题,请参考以下文章

解析主播放列表文件后获取视频和音频的单独 url

ffmpeg 可以从文件中的特定位置解析吗?

C# 解析字符串/innerhtml 以获取特定数据

如何按特定 ID 解析 html 表(来自文件)

解析配置文件、环境和命令行参数,以获取单个选项集合

解析 Apache VHosts 以获取域和文件夹路径列表