2018/08/01(水)【Unity】CSVファイルからListを生成する
マスターデータなんかのリスト作成に。
参考
CSVファイルをコード1行でListに変換する 【Unity】【C#】 | Unity開発Tips
ほとんどコピペ。引数周りだけ変更。
nsCSVReader.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.ComponentModel;
using System.IO;
using UnityEngine;
/// <summary>
/// CSVの列とのマッピングのための属性クラス
/// </summary>
public class CsvColumnAttribute : Attribute
{
public CsvColumnAttribute(int columnIndex)
: this(columnIndex, null)
{
}
public CsvColumnAttribute(int columnIndex, object defaultValue)
{
this.ColumnIndex = columnIndex;
this.DefaultValue = defaultValue;
}
public int ColumnIndex { get; set; }
public object DefaultValue { get; set; }
}
public class nsCSVReader<T> : IEnumerable<T>, IDisposable
where T : class, new()
{
/// <summary>
/// Type毎のデータコンバーター
/// </summary>
private Dictionary<Type, TypeConverter> converters = new Dictionary<Type, TypeConverter>();
/// <summary>
/// 列番号をキーとしてフィールド or プロパティへのsetメソッドが格納されます。
/// </summary>
private Dictionary<int, Action<object, string>> setters = new Dictionary<int, Action<object, string>>();
/// <summary>
/// Tの情報をロードします。
/// setterには列番号をキーとしたsetメソッドが格納されます。
/// </summary>
private void LoadType()
{
Type type = typeof(T);
// Field, Property のみを対象とする
var memberTypes = new MemberTypes[] { MemberTypes.Field, MemberTypes.Property };
// インスタンスメンバーを対象とする
BindingFlags flag = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
foreach (MemberInfo member in type.GetMembers(flag).Where((member) => memberTypes.Contains(member.MemberType)))
{
CsvColumnAttribute csvColumn = GetCsvColumnAttribute(member);
if (csvColumn == null) continue;
int columnIndex = csvColumn.ColumnIndex;
object defaultValue = csvColumn.DefaultValue;
if (member.MemberType == MemberTypes.Field)
{
// field
FieldInfo fieldInfo = type.GetField(member.Name, flag);
setters[columnIndex] = (target, value) =>
fieldInfo.SetValue(target, GetConvertedValue(fieldInfo, value, defaultValue));
}
else
{
// property
PropertyInfo propertyInfo = type.GetProperty(member.Name, flag);
setters[columnIndex] = (target, value) =>
propertyInfo.SetValue(target, GetConvertedValue(propertyInfo, value, defaultValue), null);
}
}
}
/// <summary>
/// 対象のMemberInfoからCsvColumnAttributeを取得する
/// </summary>
/// <param name="member">確認対象のMemberInfo</param>
/// <returns>CsvColumnAttributeのインスタンス、設定されていなければnull</returns>
private CsvColumnAttribute GetCsvColumnAttribute(MemberInfo member)
{
return (CsvColumnAttribute)member.GetCustomAttributes(typeof(CsvColumnAttribute), true).FirstOrDefault();
//return member.GetCustomAttributes<CsvColumnAttribute>().FirstOrDefault();
}
/// <summary>
/// valueを対象のTypeへ変換する。できない場合はdefaultを返す
/// </summary>
/// <param name="type">変換後の型</param>
/// <param name="value">変換元の値</param>
/// <param name="default">規定値</param>
/// <returns></returns>
private object GetConvertedValue(MemberInfo info, object value, object @default)
{
Type type = null;
if (info is FieldInfo)
{
type = (info as FieldInfo).FieldType;
}
else if (info is PropertyInfo)
{
type = (info as PropertyInfo).PropertyType;
}
// コンバーターは同じTypeを使用することがあるため、キャッシュしておく
if (!converters.ContainsKey(type))
{
converters[type] = TypeDescriptor.GetConverter(type);
}
TypeConverter converter = converters[type];
////変換できない場合に例外を受け取りたい場合
//return converter.ConvertFrom(value);
//失敗した場合に CsvColumnAttribute の規定値プロパティを返す場合
try
{
// 変換した値を返す。
return converter.ConvertFrom(value);
}
catch (Exception)
{
// 変換できなかった場合は規定値を返す
return @default;
}
}
private StringReader reader;
private bool skipFirstLine;
private Encoding encoding;
public nsCSVReader(TextAsset textAsset)
: this(textAsset, true)
{
}
public nsCSVReader(TextAsset textAsset, bool skipFirstLine)
: this(textAsset, skipFirstLine, null)
{
}
public nsCSVReader(TextAsset textAsset, bool skipFirstLine, Encoding encoding)
{
this.skipFirstLine = skipFirstLine;
this.encoding = encoding;
// 既定のエンコードの設定
if (this.encoding == null)
{
this.encoding = System.Text.Encoding.GetEncoding("utf-8");
}
// Tを解析する
LoadType();
TextAsset csv = textAsset;
this.reader = new StringReader(csv.text);
// ヘッダーを飛ばす場合は1行読む
if (skipFirstLine)
{
this.reader.ReadLine();
}
}
public void Dispose()
{
using (reader)
{
}
reader = null;
}
public IEnumerator<T> GetEnumerator()
{
string line;
while ((line = reader.ReadLine()) != null)
{
// T のインスタンスを作成
var data = new T();
// 行をセパレータで分解
string[] fields = line.Split(',');
// セル数分だけループを回す
foreach (int columnIndex in Enumerable.Range(0, fields.Length))
{
// 列番号に対応するsetメソッドがない場合は処理しない
if (!setters.ContainsKey(columnIndex)) continue;
// setメソッドでdataに値を入れる
setters[columnIndex](data, fields[columnIndex]);
}
yield return data;
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
/// <summary>
/// 変換失敗時のイベント引数クラス
/// </summary>
public class ConvertFailedEventArgs : EventArgs
{
public ConvertFailedEventArgs(MemberInfo info, object value, object defaultValue, Exception ex)
{
this.MemberInfo = info;
this.FailedValue = value;
this.CorrectValue = defaultValue;
this.Exception = ex;
}
/// <summary>
/// 変換に失敗したメンバーの情報
/// </summary>
public MemberInfo MemberInfo { get; private set; }
/// <summary>
/// 失敗時の値
/// </summary>
public object FailedValue { get; private set; }
/// <summary>
/// 正しい値をイベントで受け取る側が設定してください。規定値はCsvColumnAttribute.DefaultValueです。
/// </summary>
public object CorrectValue { get; set; }
/// <summary>
/// 発生した例外
/// </summary>
public Exception Exception { get; private set; }
}
nsClassList.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using UnityEngine;
/// <summary>
/// Ns class list.
/// </summary>
public abstract class nsClassList<T> : IEnumerable<T> where T : class, new()
{
private List<T> list = new List<T>();
protected nsClassList()
{
}
protected nsClassList(TextAsset textAsset, bool _skipFirstLine)
{
using (var reader = new nsCSVReader<T>(textAsset, _skipFirstLine))
{
list = reader.ToList();
}
}
public void Add(T item)
{
list.Add(item);
}
public void RemoveAt(int index)
{
list.RemoveAt(index);
}
public void Remove(T item)
{
list.Remove(item);
}
public T this[int index]
{
get { return this.list[index]; }
set { this.list[index] = value; }
}
public int Count { get { return list.Count; } }
public IEnumerator<T> GetEnumerator()
{
for (int i = 0; i < list.Count; i++)
{
yield return list[i];
}
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
PlayerMaster.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
/// <summary>
/// 自機のマスターデータ。
/// </summary>
public class PlayerMaster
{
[CsvColumnAttribute(0, 0)]
public int id { get; set; }
[CsvColumnAttribute(1, 0)]
public int body_id { get; set; }
[CsvColumnAttribute(2, 0)]
public int bullet_id { get; set; }
[CsvColumnAttribute(3, 0)]
public int hp { get; set; }
[CsvColumnAttribute(4, 0)]
public int spd { get; set; }
public override string ToString()
{
return string.Format("[PlayerMaster: id={0}, body_id={1}, bullet_id={2}, hp={3}, spd={4}]", id, body_id, bullet_id, hp, spd);
}
}
/// <summary>
/// ClassListを継承したPlayerModelListを定義
/// </summary>
public class PlayerMasterList : nsClassList<PlayerMaster>
{
public PlayerMasterList(TextAsset textAsset, bool _skipFirstLine) : base(textAsset, _skipFirstLine) { }
}
使い方
PlayerMasterList PlayerMasterList = new PlayerMasterList(TextAsset, true);