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(); } } }
説明は以上です。
誤り、指摘等があればご連絡ください。
さんのコメント: さんのコメント: