固定長ファイルをSJISのバイト指定で出力するためのクラスです。
SJIS以外の文字コードも指定可能です(※試してません)。
私が必要になった機能のみを実装したものです。
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
namespace FixedText
{
/// <summary>
/// クラス、構造体の固定長ファイル出力定義用属性
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class FixedTextFileAttribute : Attribute
{
/// <summary>
/// エンコード
/// </summary>
public string Encode;
/// <summary>
/// クラスまたは構造体に定義されたエンコーディングを取得する
/// </summary>
/// <typeparam name="T">対象のクラス、構造体</typeparam>
/// <returns>エンコード</returns>
public static Encoding GetEncoding<T>()
{
var encAttr = Attribute.GetCustomAttribute(
typeof(T), typeof(FixedTextFileAttribute)) as FixedTextFileAttribute;
return Encoding.GetEncoding(encAttr.Encode);
}
/// <summary>
/// 指定された属性に従って固定長テキストを作成して返す
/// </summary>
/// <typeparam name="T">出力対象データクラス、構造体</typeparam>
/// <param name="o">出力データが格納されたオブジェクト</param>
/// <returns></returns>
public static string ConvertFixedText<T>(object o)
{
var values = (T)o;
//指定されたエンコードを取得する
Encoding enc = GetEncoding<T>();
//データを固定長文字列にする
StringBuilder result = new StringBuilder();
foreach (PropertyInfo info in typeof(T).GetProperties())
{
//出力対象除外項目判定:Reject属性が設定されている項目を除外する
var reject = Attribute.GetCustomAttribute(info, typeof(RejectAttribute));
if (reject != null)
{
continue;
}
//固定長定義属性を取得する
var fixedAttr = Attribute.GetCustomAttribute(
info, typeof(FixedAttribute)) as FixedAttribute;
//属性が設定されていないならその項目を出力対象から除外する
if(fixedAttr == null)
{
continue;
}
//出力項目値を出力する
string value = info.GetValue(values, null).ToString();
//指定バイトを超えている場合、後ろから1文字づつ削って指定バイトに調整する
while (fixedAttr.ByteLength < enc.GetByteCount(value))
{
value = value.Substring(0, value.Length - 1);
}
//不足長を指定文字で埋める
//※PadRight,PadLeftの指定桁は文字数であり、バイト数ではない。
// 従って、不足バイト数ではなく不足文字数に換算して指定する
int padLen = fixedAttr.ByteLength - (enc.GetByteCount(value) - value.Length);
switch (fixedAttr.PadType)
{
case EPadType.After:
result.Append(value.PadRight(padLen, fixedAttr.PadChar));
break;
case EPadType.Before:
result.Append(value.PadLeft(padLen, fixedAttr.PadChar));
break;
}
}
return result.ToString();
}
/// <summary>
/// 固定長でファイルに出力する
/// </summary>
/// <typeparam name="T">出力対象データクラス、構造体</typeparam>
/// <param name="fileName">出力ファイル名(パス付き)</param>
/// <param name="values">出力対象データ</param>
public static void WriteFixedTextFile<T>(string fileName, IEnumerable<T> values)
{
//フォルダが存在しないなら作成する
string path = Path.GetDirectoryName(fileName);
if (!string.IsNullOrWhiteSpace(path))
{
Directory.CreateDirectory(Path.GetDirectoryName(fileName));
}
//出力する固定長文字列を作成する
StringBuilder builder = new StringBuilder();
foreach (T value in values)
{
builder.AppendLine(ConvertFixedText<T>(value));
}
//作成した固定長データを指定されたエンコードのテキストファイルに出力する
File.WriteAllText(fileName, builder.ToString(), GetEncoding<T>());
}
}
/// <summary>
/// 項目毎の固定長出力定義属性
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class FixedAttribute : Attribute
{
//バイト長
public int ByteLength;
//不足桁への文字埋めで使用する文字
public char PadChar = ' ';
//前埋め/後埋め
public EPadType PadType = EPadType.After;
}
/// <summary>
/// 固定長出力しない項目を指定するための属性
/// </summary>
[AttributeUsage(AttributeTargets.Property)]
public class RejectAttribute : Attribute
{
}
/// <summary>
/// 不足桁文字埋めタイプ
/// </summary>
public enum EPadType
{
Before //前埋め
,After //後埋め
}
}
使い方
出力データ定義クラス(または構造体)を作成する
出力データを格納するクラスまたは構造体を作成します。
そして、属性を使って文字コード、長さ、前埋め、後埋め、埋める文字を指定します。
(※項目はプロパティで宣言してください。フィールド属性への対応をしていません)
-
クラスの属性で文字コードを指定します
-
各プロパティの属性で長さ、前埋め、後埋め、埋める文字を指定します
-
出力対象外のプロパティがある場合はReject属性を使います
[FixedTextFile(Encode = "Shift_JIS")]
public class Todofuken
{
/// <summary>
/// 都道府県コード
/// </summary>
[Fixed(ByteLength = 2, PadChar = '0', PadType = EPadType.Before)]
public int Code { set; get; }
/// <summary>
/// 都道府県名
/// </summary>
[Fixed(ByteLength = 10, PadChar = ' ', PadType = EPadType.After)]
public string Name { set; get; }
/// <summary>
/// 県庁所在地
/// </summary>
[Reject]
public string PrefecturalCapital { set; get; }
/// <summary>
/// 面積
/// </summary>
[Fixed(ByteLength = 10, PadChar = ' ', PadType = EPadType.Before)]
public decimal Area { set; get; }
/// <summary>
/// コンストラクタ
/// </summary>
/// <param name="code">都道府県コード</param>
/// <param name="name">都道府県名</param>
/// <param name="capital">県庁所在地</param>
/// <param name="area">面積</param>
public Todofuken(int code, string name, string capital, decimal area)
{
this.Code = code;
this.Name = name;
this.PrefecturalCapital = capital;
this.Area = area;
}
}
固定長ファイル出力メソッドを呼び出す
FixedTextFileAttribute の WriteFixedTextFile メソッドを使用して、固定長ファイルを作成します。
Todofuken[] data = {
new Todofuken(1,"北海道", "札幌市", 83424.22m)
, new Todofuken(2, "青森県", "青森市", 9645.40m)
, new Todofuken(3, "岩手県", "盛岡市", 15275.01m)
, new Todofuken(4, "宮城県", "仙台市", 7282.14m)
, new Todofuken(5, "秋田県", "秋田市", 11637.54m)
};
//固定長ファイルを出力する
FixedTextFileAttribute.WriteFixedTextFile<Todofuken>(@"都道府県一覧.txt", data);
01北海道 83424.22 02青森県 9645.40 03岩手県 15275.01 04宮城県 7282.14 05秋田県 11637.54