【问题标题】:How to create an object after parsing a flat text file解析平面文本文件后如何创建对象
【发布时间】:2014-12-11 18:47:10
【问题描述】:

我正在使用 C# 编写代码,过去我曾成功使用 http://www.filehelpers.net/ 解析文本文件。我的文件格式已从更标准的 .csv 格式更改,现在我想解析一个如下所示的文件:

客户 ID:1732
姓名:胡安·佩雷斯
余额:435.00
日期:11-05-2002

客户 ID:554
姓名:佩德罗·戈麦斯
余额:12342.30
日期:06-02-2004

如何解析这样的文件我找不到这样的示例,而不是分隔符,我需要找到关键字,然后读取在“:”之前给出的值

【问题讨论】:

  • 记得为您使用的编程语言添加标签。还请告诉我们您到目前为止所做的尝试。
  • 您可以使用string.Split 将每一行分成键/值对。

标签: c# file text-parsing filehelpers flat


【解决方案1】:

这是一个示例(参见.NetFiddle),需要根据实际的文件进行调整。可以使用基本的正则表达式来解析文件,然后使用 Linq 输出解析成类实例的内容。以下是使用正则表达式解析数据的模式,您需要根据自己的情况对其进行修改。


我正在创建一个目标类(而不是将字符串转换为本示例所需的最终格式),例如 Customer 对象:

public class Customer
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Balance { get; set; }
    public string Date { get; set; }
}

这是与客户对象匹配的示例数据,它模拟此时数据已从文件中读取到字符串中:

string data = @"
custID: 1732 
name: Juan Perez
balance: 435.00
date: 11-05-2002

custID: 554
name: Pedro Gomez
balance: 12342.30
date: 06-02-2004";

有了数据和目标实体,我们将使用正则表达式映射出我们想要如何解析文件的模式。我们将在模式中使用named captures(?<NameHere> ) 结构将数据从标头中分离出来,以便于提取(而不是可用的索引)。

string pattern = @"custID:(?<ID>[^\r\n]+)\s+name:(?<Name>[^\r\n]+)\s+balance:(?<Balance>[^\r\n]+)\s+date:(?<Date>[^\r\n]+)";

var KVPs = Regex.Matches(data, pattern)
                .OfType<Match>()
                .Select (mt => new Customer()
                {
                    Id = mt.Groups["ID"].Value,
                    Name = mt.Groups["Name"].Value,
                    Balance = mt.Groups["Balance"].Value,
                    Date    = mt.Groups["Date"].Value,
                })
                .ToList();

当它运行时,我们会在一个列表中获得两个类实例,就像在 LinqPad 中运行的那样:


您正在解析的数据似乎是一个 INI 文件。我讨论了如何使用正则表达式(高级)将该信息解析为字典以访问INI Files Meet Regex and Linq in C# to Avoid the WayBack Machine of Kernal32.Dll

【讨论】:

    【解决方案2】:

    将 OmegaMan 的解决方案应用于 FileHelpers 并不容易,但以下内容可能会帮助您入门。

    暂时假设您只有一条记录。然后以下工作:

    [DelimitedRecord(":")]
    public class ImportRecord
    {
        [FieldTrim(TrimMode.Both)]
        public string Key;
        [FieldTrim(TrimMode.Both)]
        public string Value;
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            var engine = new FileHelperEngine<ImportRecord>();
    
            string fileAsString = @"custID: 1732" + Environment.NewLine +
                                  @"name: Juan Perez" + Environment.NewLine +
                                  @"balance: 435.00" + Environment.NewLine +
                                  @"date: 11-05-2002" + Environment.NewLine;
    
            ImportRecord[] validRecords = engine.ReadString(fileAsString);
    
            var dictionary = validRecords.ToDictionary(r => r.Key, r => r.Value);
    
            Assert.AreEqual(dictionary["custID"], "1732");
            Assert.AreEqual(dictionary["name"], "Juan Perez");
            Assert.AreEqual(dictionary["balance"], "435.00");
            Assert.AreEqual(dictionary["date"], "11-05-2002");
    
            Console.ReadKey();
        }
    }
    

    但是,一旦您有多个记录,您就会得到重复的字典条目,而上述方法将不起作用。但是有一些方法可以解决这个问题。例如,如果每条记录的行数相同(在您的示例中为 4,您可以这样做)

    [DelimitedRecord(":")]
    [IgnoreEmptyLines()]
    public class ImportRecord
    {
        [FieldTrim(TrimMode.Both)]
        public string Key;
        [FieldTrim(TrimMode.Both)]
        public string Value;
    }
    
    public class Customer
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public string Balance { get; set; }
        public string Date { get; set; }
    }
    
    class Program
    {
        static void Main(string[] args)
        {
            var engine = new FileHelperEngine<ImportRecord>();
    
            string fileAsString =
    @"custID: 1732
    name: Juan Perez
    balance: 435.00
    date: 11-05-2002
    
    custID: 554
    name: Pedro Gomez
    balance: 12342.30
    date: 06-02-2004";
    
            ImportRecord[] validRecords = engine.ReadString(fileAsString);
    
            var customers = validRecords
                .Batch(4, x => x.ToDictionary(r => r.Key, r => r.Value))
                .Select(dictionary => new Customer()
                    {
                        Id = dictionary["custID"],
                        Name = dictionary["name"],
                        Balance = dictionary["balance"],
                        Date = dictionary["date"]
                    }).ToList();
    
            Customer customer1 = customers[0];
            Assert.AreEqual(customer1.Id, "1732");
            Assert.AreEqual(customer1.Name, "Juan Perez");
            Assert.AreEqual(customer1.Balance, "435.00");
            Assert.AreEqual(customer1.Date, "11-05-2002");
    
            Customer customer2 = customers[1];
            Assert.AreEqual(customer2.Id, "554");
            Assert.AreEqual(customer2.Name, "Pedro Gomez");
            Assert.AreEqual(customer2.Balance, "12342.30");
            Assert.AreEqual(customer2.Date, "06-02-2004");
    
            Console.WriteLine("All OK");
            Console.ReadKey();
        }
    }
    

    }

    另一种选择是预先解析内容,以便将其转换为更传统的 CSV 文件。也就是说,使用File.ReadAllText() 得到string,然后用字段分隔符替换换行符,用换行符替换空行。然后用FileHelpersEngine.ReadAsString()读取转换后的字符串。

    【讨论】:

    • 顺便说一句,LINQ 例程Batch() 来自MoreLinq
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-04-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多