C#で固定幅ファイルを作成する最良の方法は何ですか。書き出す長さのフィールドがたくさんあります。 20,80.10,2などをすべて左揃えで言います。これを行う簡単な方法はありますか?
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 |"
これは、構成可能な固定幅ファイル書き込みモジュール用に作成したシステムです。 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です。これは、特にマルチメガバイトのファイルを処理する場合は、不変文字列を連結するよりも高速です。
FileHelpersを試す:www.filehelpers.com
Stringクラスの.PadRight関数(左揃えデータ用)を使用します。そう:
handle.WriteLine(s20.PadRight(20));
handle.WriteLine(s80.PadRight(80));
handle.WriteLine(s10.PadRight(10));
handle.WriteLine(s2.PadRight(2));
http://www.filehelpers.com/ を使用できます
私は文字列に拡張メソッドを使用しています。そうです、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);
}
}
StreamWriterを使用し、Write(string)呼び出しでString.Format()を使用して、指定したフィールドの正しい幅の文字列を作成できます。
この質問に対するダレンの答えは私に拡張メソッドを使用するように促しましたが、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);
以前のさまざまなパディング/フォーマットの投稿は十分に機能しますが、ISerializableの実装に興味があるかもしれません。
これは、.NETでの Object Serialization に関するmsdnの記事です。
標準のテキストファイルを使用できませんか?行ごとにデータを読み戻すことができます。
string.Format()を使用します
MyString.PadRight(totalLengthForField、 '')を使用してみてください
右側のすべての数字にスペースを埋めたいですか?
その場合、 String.PadRight または String.Format で順調に進みます。
ASCIIEncoding.UTF8.GetBytes(text)を使用して、バイト配列に変換できます。次に、バイト配列を固定サイズのレコードとしてファイルに書き込みます。
UTF8は、一部の文字を表すために必要なバイト数が異なります。UTF16は少し予測可能で、文字あたり2バイトです。