将非分隔字符串中的值读取到类对象中

时间:2014-01-21 03:46:09

标签: c# .net string substring stringreader

我有一个具有以下结构的字符串:

Student Name________AgeAddress_______________________Bithday___Lvl

示例:

Jonh Smith         016Some place in NY, USA               01/01/2014L01

如您所见,没有像|,

这样的分隔字符

此外,字段之间没有空格(如果您检查,则年龄/地址和生日/级别之间没有空格。

每个字段的大小是静态的,因此如果数据的长度小于那么它将包含空格。

我有一个课程需要填写这些信息:

public class StudentData
{
    public char[] _name = new char[20];
    public string name;
    public char[] _age = new char[3];
    public string age;
    public char[] _address = new char[30];
    public string address;
    public char[] _bday = new char[10];
    public string bday;
    public char[] _level = new char[3];
    public string level;
}

有没有办法自动动态地执行此操作?

我的意思是我真的不想这样编码:

myClass.name = stringLine.substring(0,19);
myClass.age = stringLine.substring(20,22);

那是因为我有更多的字段,在这个例子中添加的字段和&更多字符串与其他不同数据的方式。

更新:“Smith”和“016”之间应该有很多空格,但我不知道如何编辑它。

Update2 :如果我使用StringReader.Read(),我可以逃避使用子字符串和索引,但它仍然不是那么动态,因为我需要为每个字段重复这3行。

StringReader reader = new StringReader(stringLine);
reader.Read(myClass._name, 0 myClass._name.Length);
myClass.name = new string(myClass._name);

2 个答案:

答案 0 :(得分:1)

您可以使用FileHelpers libraryNuGet)。

只需使用属性定义输入文件的结构:

[FixedLengthRecord]
public class StudentData
{
    [FieldFixedLength(20)]
    [FieldTrim(TrimMode.Right)] 
    public string name;
    [FieldFixedLength(3)]
    public string age;
    [FieldFixedLength(30)]
    [FieldTrim(TrimMode.Right)] 
    public string address;
    [FieldFixedLength(10)]
    public string bday;
    [FieldFixedLength(3)]
    public string level;
}

然后只需使用FileHelperEngine<T>

读取文件
var engine = new FileHelperEngine<StudentData>();
var students = engine.ReadFile(filename);

答案 1 :(得分:0)

根据您的要求,我提出了一个有趣的解决方案。所有这一切都可能比使用String.SubString()方法更复杂,也更长。

但是此解决方案可以转移到其他类型和其他字符串。我使用AttributesPropertiesReflection的概念来解析字符串固定长度并设置类属性。

注意我确实更改了您的StudentData课程,以遵循更传统的编码风格。按照MSDN上的这个方便指南:http://msdn.microsoft.com/en-us/library/xzf533w0(v=vs.71).aspx

这是新的StudentData课程。请注意,它使用属性而不是字段。 (此处未讨论)。

public class StudentData
{
    string name;
    string age;
    string address;
    string bday;
    string level;

    [FixedLengthDelimeter(0, 20)]
    public string Name { get { return this.name; } set { this.name = value; } }

    [FixedLengthDelimeter(1, 3)]
    public string Age { get { return this.age; } set { this.age = value; } }

    [FixedLengthDelimeter(2, 30)]
    public string Address { get { return this.address; } set { this.address = value; } }

    [FixedLengthDelimeter(3, 10)]
    public string BDay { get { return this.bday; } set { this.bday = value; } }

    [FixedLengthDelimeter(4, 3)]
    public string Level { get { return this.level; } set { this.level = value; } }
}

注意每个属性都有一个名为FixedLengthDelimeter的属性,它带有两个参数。

  1. OrderNumber
  2. FixedLength
  3. OrderNumber参数表示字符串中的顺序(不是位置),而是表示我们从字符串处理的顺序。第二个参数表示解析字符串时字符串的Length。这是完整的属性类。

    [AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
    public sealed class FixedLengthDelimeterAttribute : Attribute
    {
        public FixedLengthDelimeterAttribute(int orderNumber, int fixedLength)
        {
            this.fixedLength = fixedLength;
            this.orderNumber = orderNumber;
        }
    
        readonly int fixedLength;
    
        readonly int orderNumber;
    
        public int FixedLength { get { return this.fixedLength; } }
    
        public int OrderNumber { get { return this.orderNumber; } }
    }
    

    现在该属性非常简单。接受我们在构造函数中讨论过的两个参数。

    最后,还有另一种方法可以将字符串解析为对象类型,例如。

    public static class FixedLengthFormatter
    {
        public static T ParseString<T>(string inputString)
        {
            Type tType = typeof(T);
            var properties = tType.GetProperties(BindingFlags.Instance | BindingFlags.Public); //;.Where(x => x.GetCustomAttributes(typeof(FixedLengthDelimeterAttribute), false).Count() > 0);
    
            T newT = (T)Activator.CreateInstance(tType);
    
            Dictionary<PropertyInfo, FixedLengthDelimeterAttribute> dictionary = new Dictionary<PropertyInfo, FixedLengthDelimeterAttribute>();
            foreach (var property in properties)
            {
                var atts = property.GetCustomAttributes(typeof(FixedLengthDelimeterAttribute), false);
                if (atts.Length == 0)
                    continue;
                dictionary[property] = atts[0] as FixedLengthDelimeterAttribute;
            }
            foreach (var kvp in dictionary.OrderBy(x => x.Value.OrderNumber))
            {
                int length = kvp.Value.FixedLength;
                if (inputString.Length < length)
                    throw new Exception("error on attribute order number:" + kvp.Value.OrderNumber + " the string is too short.");
                string piece = inputString.Substring(0, length);
                inputString = inputString.Substring(length);
                kvp.Key.SetValue(newT, piece.Trim(), null);
            }
            return newT;
        }
    }
    

    上面的方法是字符串解析。这是一个非常基本的实用程序,可以读取FixedLengthDelimeter属性应用Dictionary的所有属性。然后枚举该字典(按OrderNumber排序),然后在输入字符串上调用SubString()方法两次。

    第一个子字符串用于解析下一个Token,而第二个子字符串重置inputString以开始处理下一个令牌。

    最后,因为它正在解析字符串,然后将解析后的字符串应用于提供给方法的类Type的属性。

    现在可以像这样使用:

    string data1 = "Jonh Smith          016Some place in NY, USA         01/01/2014L01";
    StudentData student = FixedLengthFormatter.ParseString<StudentData>(data1);
    

    这是做什么的:

    • 以固定长度格式解析属性属性的字符串。

    这不做什么:

    • 它确实将已解析的字符串转换为另一种类型。因此,所有属性都必须是字符串。 (这可以通过添加一些类型的构建逻辑来轻松调整。)
    • 没有经过充分测试。这仅针对少数样本进行测试。
    • 这绝不是唯一或最好的解决方案。