将潜在错误的分隔字符串解析为类的最有效方法是什么?

时间:2012-02-24 15:27:10

标签: c# parsing

我正在构建一个自定义解析器,它应该读取分隔的数据列表,并将结果存储在类中。我的问题是,生成数据的程序并不总是包含所有分隔符。

例如,如果最后3个属性没有值,它将跳过最后3个分隔符。

我一直在使用这样的东西,直到我注意到这个怪癖:

var data = message.Split(delimiter);

if (data.Length < 5)
    throw new Exception("Invalid message");

Id = data[0];
Property1 = data[1];
Property2 = data[2];
Property3 = data[3];
Property4 = data[4];

当然,如果分隔的字符串包含少于5个元素,则会产生问题。

将可能错误的分隔字符串解析为类的最佳方法是什么?

我不想对每个属性使用if语句,因为一些分隔的字符串包含超过50个属性。

我想过创建一个包含所有属性的数组,并在数据数组上运行for-each循环,但是我不确定它的性能影响,并且想先看看是否有更好的方法。 / p>

7 个答案:

答案 0 :(得分:3)

假设属性可以为空

Property1 = data.Length > 1 ? data[1] : null;
Property2 = data.Length > 2 ? data[2] : null;
Property3 = data.Length > 3 ? data[3] : null;
Property4 = data.Length > 4 ? data[4] : null;

您可以使用对属性有意义的任何默认值,而不是null


编辑:

var dataEx = new string[expectedLength];
data.CopyTo(dataEx, 0);

Property1 = dataEx[1];
Property2 = dataEx[2];
Property3 = dataEx[3];
Property4 = dataEx[4];

答案 1 :(得分:2)

扩展方法怎么样?

public static T GetByIndexOrDefault<T>(this Array array, int index)
{
    if (array == null)
    {
        return default(T);
    }

    if (index <= array.Length)
    {
        return (T)array.GetValue(index - 1);
    }

    return default(T);
}

然后:

string data = "foo1;foo2;foo3;foo4";

string[] splittedData = data.Split(';');

string e1 = splittedData.GetByIndexOrDefault<string>(1);    // foo1
string e2 = splittedData.GetByIndexOrDefault<string>(2);    // foo2
string e3 = splittedData.GetByIndexOrDefault<string>(3);    // foo3
string e4 = splittedData.GetByIndexOrDefault<string>(4);    // foo4
string e5 = splittedData.GetByIndexOrDefault<string>(5);    // null

答案 2 :(得分:1)

假设您的属性命名方案实际上类似于您的示例,您可以使用反射执行此操作:

var data = message.Split(delimiter);
if (data.Length < 1) throw new Exception("Invalid message");
Id = data[0];
for (var i = 1; i < data.Length; i++)
{
    var property = GetType().GetProperty("Property" + i);
    property.SetValue(this, data[i], null);
}

确保所有属性都具有可接受的默认状态,以防它们未被message设置。

答案 3 :(得分:1)

创建属性数组会起作用,即。 Xander的答案,但它仍然不是解决数据不良问题的方法。如果文件中间有一个分隔很少的字段,那么数组中间的属性也会出错。

我认为遇到问题时失败并不是什么问题。如果消息格式错误,那么数据就会很糟糕。如果您不需要创建缺少的字段,则可以随时手动解析消息并修复分隔严重的部分。

如果需要缺少字段,某些应用程序会使用算法来尝试修复错误数据。如果您认为可以修复数据(通过创建新数据或按摩旧数据),您可以创建一个算法来“猜测”丢失的字段。

答案 4 :(得分:0)

我会考虑制作一个属性名称的查找表,将属性的预期索引映射到其属性名称。然后通过反射设置属性。

        string[] propertyLookup = { "Property1", "Property2", "Property3", "Property4", "Property5" };  \\ etc etc
        string[] parsedValues = message.Split(delimiter);
        Foo newFoo = new Foo();
        Type fooType = newFoo.GetType();
        for (int i = 0; i < parsedValues.Count(); i++)
        {
            PropertyInfo prop = fooType.GetProperty(propertyLookup[i]);
            prop.SetValue(newFoo, parsedValues[i], null);
        }

答案 5 :(得分:0)

using System;
using System.Windows.Forms;
using System.Reflection;
namespace DynamicProp
{
    public partial class Form1 : Form
    {
        class Messagage 
        {
            public string ID { get; set; }
            public string Property1 { get; set; }
            public string Property2 { get; set; }
            public string Property3 { get; set; }
            public string Property4 { get; set; }
        }

        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            string[] data = { "hasan", "osman", "ali", "veli", "deli" };

            Messagage message = new Messagage();
            PropertyInfo[] ozellikler = message.GetType().GetProperties();
            int I=0;
            foreach (PropertyInfo ozellik in ozellikler)
            {
                ozellik.SetValue(message, data[I], null);
                listBox1.Items.Add("özellik :" + ozellik.Name + "  tipi :"+ozellik.GetValue(message,null).ToString());
                I++;
            }
        }
    }
}

答案 6 :(得分:0)

提供另一种方式......

如果您有一个默认值,例如空字符串,则可以创建一个List并使用AddRange添加数据字符串中的值。然后,如果尚未使用该特定数据的最大字段数,请使用AddRange和Enumerable.Repeat以默认值填充其余值。

        List<string> Results = new List<string>();
        int MaxFields = 5;
        Results.AddRange(message.Split(delimiter));
        if(Results.Count < MaxFields)
            Results.AddRange(Enumerable.Repeat(String.Empty,MaxFields - Results.Count)); 
        Id = Results[0]; 
        Property1 = Results[1];  
        Property2 = Results[2]; 
        Property3 = Results[3]; 
        Property4 = Results[4];