C#で固定長ファイルを作成する。バイト長で長さを指定する。シフトJISのファイルを作成する。

固定長ファイルを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