Microsoft.VisualBasic.FileIO名前空間にTextFieldParserというCSVファイルや固定長(ホストから送られてくるCOBOLで作成されてくるシーケンシャルファイルですね)レコードを読み込む便利なクラスがあるので、TextFieldParserを利用したCSVReaderを作成したので、メモとして掲載します。
CSVファイルの仕様(原文)
- http://www.ietf.org/rfc/rfc4180.txt
CSVファイルの一般的書式 (RFC4180 日本語訳)
- http://www.kasai.fm/wiki/rfc4180jp
動作確認環境
- Windows Vista
- 開発環境 Visual Studio 2008 Professional
- .NET 2.0以上
1. CSVReaderクラスの定義
定義は以下の用にしました。
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.VisualBasic.FileIO;
using System.IO;
namespace CSVReaderWriter
{
/// <summary>
/// CSV Reader. Microsoft.VisualBasic.FileIO.TextFieldParser
/// のラッパー
/// Microsoft.VisualBasic.FileIO名前空間を利用するにはMicrosoft.VisualBasic.dllへの
/// 参照が必要です。
/// </summary>
class CSVReader : IDisposable
{
TextFieldParser _parser = null;
#region コンストラクタ
public CSVReader(Stream stream)
{
_parser = new TextFieldParser(stream);
Initialize();
}
public CSVReader(string path)
{
_parser = new TextFieldParser(path);
Initialize();
}
public CSVReader(Stream stream, Encoding defaultEncoding)
{
_parser = new TextFieldParser(stream, defaultEncoding);
Initialize();
}
public CSVReader(string path, Encoding defaultEncoding)
{
_parser = new TextFieldParser(path, defaultEncoding);
Initialize();
}
public CSVReader(TextReader reader)
{
_parser = new TextFieldParser(reader);
Initialize();
}
#endregion
#region プロパティ
/// <summary>
/// コメントトークンを定義します。コメントトークン
/// である文字列を先頭の行に置くと、その行がコメント
/// であり、パーサーによって無視されることを示します。
/// </summary>
public string[] CommentTokens
{
get { return _parser.CommentTokens; }
set { _parser.CommentTokens = value; }
}
/// <summary>
/// テキストファイルの区切り記号を定義します。
/// </summary>
public string[] Delimiters
{
get { return _parser.Delimiters; }
set { _parser.Delimiters = value; }
}
/// <summary>
/// ファイルの終端か否かを返します。
/// 現在のカーソル位置と、ファイルの終端との間に、
/// 空行またはコメント行以外のデータが存在しない場合
/// Trueを返します。
/// </summary>
public bool EndOfData
{
get { return _parser.EndOfData; }
}
/// <summary>
/// 最近発生した MalformedLineException 例外の原因となった行を返します。
/// </summary>
public string ErrorLine
{
get { return _parser.ErrorLine; }
}
/// <summary>
/// 最近 MalformedLineException 例外が発生した行の番号(0ベース)を返します。
/// </summary>
public long ErrorLineNumber
{
get { return _parser.ErrorLineNumber - 1; }
}
/// <summary>
/// 解析するテキスト ファイルの各列の幅を表します。
/// </summary>
public int[] FieldWidths
{
get { return _parser.FieldWidths; }
set { _parser.FieldWidths = value; }
}
/// <summary>
/// 区切り形式のファイルを解析する際、フィールドが引用符で
/// 囲まれているかどうかを示します
/// </summary>
public bool HasFieldsEnclosedInQuotas
{
get { return _parser.HasFieldsEnclosedInQuotes; }
set { _parser.HasFieldsEnclosedInQuotes = value; }
}
/// <summary>
/// 現在の行番号を返します。ストリームから取り出す文字がなくなった
/// 場合は負の値を返します。
/// </summary>
public long LineNumber
{
get { return _parser.LineNumber - 1; }
}
/// <summary>
/// フィールド値から前後の空白をトリムするかどうかを示します。
/// </summary>
public bool TrimWhiteSpace
{
get { return _parser.TrimWhiteSpace; }
set { _parser.TrimWhiteSpace = value; }
}
/// <summary>
/// フィールドが区切られているかのフラグ
/// </summary>
public bool IsFieldTypeDelimited
{
get { return _parser.TextFieldType == FieldType.Delimited; }
}
/// <summary>
/// フィールドが固定長かのフラグ
/// </summary>
public bool IsFieldTypeFixed
{
get { return _parser.TextFieldType == FieldType.FixedWidth; }
}
#endregion
#region メソッド定義
/// <summary>
/// パーサーを閉じる
/// </summary>
public void Close()
{
if (_parser != null) _parser.Close();
}
/// <summary>
/// ストリームから文字をnumberOfChars文字取りだす。
/// 取り出した文字はストリームから削除されません。
/// </summary>
/// <param name="numberOfChars"></param>
/// <returns></returns>
public string PeekChars(int numberOfChars)
{
return _parser.PeekChars(numberOfChars);
}
/// <summary>
/// フィールドに分解された1行を取り出します。
/// </summary>
/// <returns></returns>
public string[] ReadFields()
{
return _parser.ReadFields();
}
/// <summary>
/// 1行文字列として読み出します。
/// </summary>
/// <returns></returns>
public string ReadLine()
{
return _parser.ReadLine();
}
/// <summary>
/// 現在のカーソル位置からStreamから全ての文字列を取り出します。
/// </summary>
/// <returns></returns>
public string ReadToEnd()
{
return _parser.ReadToEnd();
}
/// <summary>
/// 区切り文字を設定する
/// </summary>
/// <param name="delimiters"></param>
public void SetDelimiters(string[] delimiters)
{
_parser.SetDelimiters(delimiters);
}
/// <summary>
/// 固定長フィールドの場合のフィールド幅を設定する。
/// </summary>
/// <param name="fieldWidths"></param>
public void SetFieldWidth(int[] fieldWidths)
{
_parser.SetFieldWidths(fieldWidths);
}
/// <summary>
/// フィールドタイプを区切り文字に変更する
/// </summary>
public void SetFieldTypeDelimited()
{
_parser.TextFieldType = FieldType.Delimited;
}
/// <summary>
/// フィールドタイプを固定幅に変更する
/// </summary>
public void SetFieldTypeFixed()
{
_parser.TextFieldType = FieldType.FixedWidth;
}
/// <summary>
/// 初期化処理。
/// コンストラクタから呼び出され、初期設定を行います。
/// ファイルのデリミタをタブ,フィールドタイプを非固定長
/// TrimWhiteSpace=Falseとして初期化します。
/// </summary>
protected virtual void Initialize()
{
SetDelimiters(new string[] { "\t" });
SetFieldTypeDelimited();
TrimWhiteSpace = false;
}
#endregion
#region IDisposable Members
public virtual void Dispose()
{
Close();
if (_parser != null) _parser.Dispose();
}
void IDisposable.Dispose()
{
this.Dispose();
}
#endregion
}
}
2. 動作サンプルプログラム
前回作成したCSVWriterも使った動作確認プログラムを掲載します。
using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
namespace CSVReaderWriter
{
class Program
{
static void Main(string[] args)
{
DataTable tbl = new DataTable();
tbl.Columns.Add("col1");
tbl.Columns.Add("col2");
tbl.LoadDataRow(new object[] { "te\nst1", "test2" }, false);
tbl.LoadDataRow(new object[] { "test3", "test4" }, false);
tbl.AcceptChanges();
using (CSVWriter w = new CSVWriter(@"D:\temp\test.csv"))
{
w.Separator = ",";
w.WriteOnce(tbl);
}
using (CSVReader r = new CSVReader(@"D:\temp\test.csv"))
{
r.Delimiters = new string[] { "," };
while (!r.EndOfData)
{
Console.Write("Line {0}:", r.LineNumber);
string[] fields = r.ReadFields();
foreach (string field in fields)
{
Console.Write(field);
}
Console.WriteLine();
}
}
Console.ReadLine();
}
}
}
説明は以上です。
誤り、指摘等があればご連絡ください。