1

次の構造の文字列があります。

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);
4

2 に答える 2

1

あなたの要件を考慮して、興味深い解決策を思いつきました。String.SubString()いずれにせよ、前述の方法を使用するよりも複雑で時間がかかる可能性があります。

ただし、この解決策は他の型や他の文字列にも適用できます。AttributesProperties、の概念を使用しReflectionて、固定長で文字列を解析し、クラスのプロパティを設定しました。

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

各プロパティには、FixedLengthDelimeter2 つのパラメーターを受け取る Attribute という名前があることに注意してください。

  1. OrderNumber
  2. FixedLength

パラメータは、文字列内のOrderNumber順序 (位置ではなく) を示しますが、文字列から処理する順序を示します。2 番目のパラメーターは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; } }
}

これで、属性は十分に単純になりました。コンストラクターで前に説明した 2 つのパラメーターを受け入れます。

最後に、文字列を次のようなオブジェクト タイプに解析する別の方法があります。

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。次に、そのディクショナリが列挙され ( の順序で) 、入力文字列に対してメソッドが 2 回OrderNumber呼び出されます。SubString()

最初の部分文字列は次のトークンを解析し、2 番目の部分文字列は次のトークンの処理を開始するためにTokenリセットします。inputString

最後に、文字列を解析するときに、解析された文字列をTypeメソッドに提供されたクラスのプロパティに適用します。

これは次のように簡単に使用できます。

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

これが何をするか:

  • 固定長形式のプロパティ属性に対して文字列を解析します。

これがしないこと:

  • 解析された文字列を別の型に変換します。したがって、すべてのプロパティは文字列でなければなりません。(これは、いくつかのタイプキャストロジックを追加することで簡単に適応できます)。
  • 十分にテストされていません。これは、いくつかのサンプルに対してのみテストされています。
  • それが唯一の、または最善の解決策であるとは限りません。
于 2014-01-21T04:58:41.467 に答える
1

FileHelpers ライブラリ( NuGet )を使用できます。

属性を使用して入力ファイルの構造を定義するだけです。

[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);
于 2014-01-21T05:53:46.557 に答える