web-dev-qa-db-ja.com

C#で固定幅ファイルを作成する

C#で固定幅ファイルを作成する最良の方法は何ですか。書き出す長さのフィールドがたくさんあります。 20,80.10,2などをすべて左揃えで言います。これを行う簡単な方法はありますか?

38
Brian G

String.Formatを使用すると、値にスペースを簡単に埋め込むことができます。

string a = String.Format("|{0,5}|{1,5}|{2,5}", 1, 20, 300);
string b = String.Format("|{0,-5}|{1,-5}|{2,-5}", 1, 20, 300);

// 'a' will be equal to "|    1|   20|  300|"
// 'b' will be equal to "|1    |20   |300  |"
52
Wheelie

これは、構成可能な固定幅ファイル書き込みモジュール用に作成したシステムです。 XMLファイルで構成されており、関連する部分は次のようになっています。

_<WriteFixedWidth Table="orders" StartAt="1" Output="Return">
  <Position Start="1" Length="17" Name="Unique Identifier"/>
  <Position Start="18" Length="3" Name="Error Flag"/>
  <Position Start="21" Length="16" Name="Account Number" Justification="right"/>
  <Position Start="37" Length="8" Name="Member Number"/>
  <Position Start="45" Length="4" Name="Product"/>
  <Position Start="49" Length="3" Name="Paytype"/>
  <Position Start="52" Length="9" Name="Transit Routing Number"/>
</WriteFixedWidth>
_

StartAtは、ポジションが0ベースか1ベースかをプログラムに通知します。仕様からオフセットをコピーして、作者がどの開始インデックスを選択したかに関係なく、構成をできるだけ仕様に似せたいので、私はそれを構成可能にしました。

PositionタグのName属性は、DataTableの列の名前を参照します。

次のコードはLINQ-to-XMLを使用して.Net 3.5用に記述されているため、メソッドは、上記の構成のXElementが渡されることを想定しており、XDocument.Load(filename)を使用してXMLファイル、次にXDocumentオブジェクトで.Descendants("WriteFixedWidth")を呼び出して構成要素を取得します。

_    public void WriteFixedWidth(System.Xml.Linq.XElement CommandNode, DataTable Table, Stream outputStream)
    {
        StreamWriter Output = new StreamWriter(outputStream);
        int StartAt = CommandNode.Attribute("StartAt") != null ? int.Parse(CommandNode.Attribute("StartAt").Value) : 0;

        var positions = from c in CommandNode.Descendants(Namespaces.Integration + "Position")
                        orderby int.Parse(c.Attribute("Start").Value) ascending
                        select new
                        {
                            Name = c.Attribute("Name").Value,
                            Start = int.Parse(c.Attribute("Start").Value) - StartAt,
                            Length = int.Parse(c.Attribute("Length").Value),
                            Justification = c.Attribute("Justification") != null ? c.Attribute("Justification").Value.ToLower() : "left"
                        };

        int lineLength = positions.Last().Start + positions.Last().Length;
        foreach (DataRow row in Table.Rows)
        {
            StringBuilder line = new StringBuilder(lineLength);
            foreach (var p in positions)
                line.Insert(p.Start, 
                    p.Justification == "left" ? (row.Field<string>(p.Name) ?? "").PadRight(p.Length,' ')
                                              : (row.Field<string>(p.Name) ?? "").PadLeft(p.Length,' ') 
                    );
            Output.WriteLine(line.ToString());
        }
        Output.Flush();
    }
_

エンジンはStringBuilderです。これは、特にマルチメガバイトのファイルを処理する場合は、不変文字列を連結するよりも高速です。

27
Chris Wenham

FileHelpersを試す:www.filehelpers.com

次に例を示します。 http://www.filehelpers.com/quick_start_fixed.html

9

Stringクラスの.PadRight関数(左揃えデータ用)を使用します。そう:

handle.WriteLine(s20.PadRight(20));
handle.WriteLine(s80.PadRight(80));
handle.WriteLine(s10.PadRight(10));
handle.WriteLine(s2.PadRight(2));
7
Andrew Burgess

http://www.filehelpers.com/ を使用できます

5
Yitzchok

私は文字列に拡張メソッドを使用しています。そうです、XMLコメントはOTTのように見えるかもしれませんが、他の開発者に再利用したい場合は...

public static class StringExtensions
{

    /// <summary>
    /// FixedWidth string extension method.  Trims spaces, then pads right.
    /// </summary>
    /// <param name="self">extension method target</param>
    /// <param name="totalLength">The length of the string to return (including 'spaceOnRight')</param>
    /// <param name="spaceOnRight">The number of spaces required to the right of the content.</param>
    /// <returns>a new string</returns>
    /// <example>
    /// This example calls the extension method 3 times to construct a string with 3 fixed width fields of 20 characters, 
    /// 2 of which are reserved for empty spacing on the right side.
    /// <code>
    ///const int colWidth = 20;
    ///const int spaceRight = 2;
    ///string headerLine = string.Format(
    ///    "{0}{1}{2}",
    ///    "Title".FixedWidth(colWidth, spaceRight),
    ///    "Quantity".FixedWidth(colWidth, spaceRight),
    ///    "Total".FixedWidth(colWidth, spaceRight));
    /// </code>
    /// </example>
    public static string FixedWidth(this string self, int totalLength, int spaceOnRight)
    {
        if (totalLength < spaceOnRight) spaceOnRight = 1; // handle silly use.

        string s = self.Trim();

        if (s.Length > (totalLength - spaceOnRight))
        {
            s = s.Substring(0, totalLength - spaceOnRight);
        }

        return s.PadRight(totalLength);
    }
}
2
Darren

StreamWriterを使用し、Write(string)呼び出しでString.Format()を使用して、指定したフィールドの正しい幅の文字列を作成できます。

1
Scott Dorman

この質問に対するダレンの答えは私に拡張メソッドを使用するように促しましたが、Stringを拡張する代わりに、StringBuilderを拡張しました。私は2つの方法を書きました:

public static StringBuilder AppendFixed(this StringBuilder sb, int length, string value)
{
    if (String.IsNullOrWhiteSpace(value))
        return sb.Append(String.Empty.PadLeft(length));

    if (value.Length <= length)
        return sb.Append(value.PadLeft(length));
    else
        return sb.Append(value.Substring(0, length));
}

public static StringBuilder AppendFixed(this StringBuilder sb, int length, string value, out string rest)
{
    rest = String.Empty;

    if (String.IsNullOrWhiteSpace(value))
        return sb.AppendFixed(length, value);

    if (value.Length > length)
        rest = value.Substring(length);

    return sb.AppendFixed(length, value);
}

1つ目は、長すぎる文字列を黙って無視し、単にその終わりを切り捨てます。2つ目は、メソッドのoutパラメータを介して切り捨てられた部分を返します。

例:

string rest;    

StringBuilder clientRecord = new StringBuilder();
clientRecord.AppendFixed(40, doc.ClientName, out rest); 
clientRecord.AppendFixed(40, rest);
clientRecord.AppendFixed(40, doc.ClientAddress, out rest);
clientRecord.AppendFixed(40, rest);
1
Jojo

以前のさまざまなパディング/フォーマットの投稿は十分に機能しますが、ISerializableの実装に興味があるかもしれません。

これは、.NETでの Object Serialization に関するmsdnの記事です。

0
CrashCodes

標準のテキストファイルを使用できませんか?行ごとにデータを読み戻すことができます。

0
artur02

string.Format()を使用します

http://msdn.Microsoft.com/en-us/library/aa331875.aspx

0
Chris Ballance

MyString.PadRight(totalLengthForField、 '')を使用してみてください

0
viggity

右側のすべての数字にスペースを埋めたいですか?

その場合、 String.PadRight または String.Format で順調に進みます。

0
Tobi

ASCIIEncoding.UTF8.GetBytes(text)を使用して、バイト配列に変換できます。次に、バイト配列を固定サイズのレコードとしてファイルに書き込みます。

UTF8は、一部の文字を表すために必要なバイト数が異なります。UTF16は少し予測可能で、文字あたり2バイトです。

0
davenpcj