TextFieldParserは、あらかじめ決められた形式・フォーマットのテキストファイル、例えばCSVファイルなどを解析して読み取るためのクラス。

デリミタ(区切り文字)、コメント文字列等は自由に設定できるので、フォーマットさえ決まっていればこのクラスで読み取り可能。 CSVのようにデリミタでフィールド(データ)を区切るのではなく、データの幅が固定長になっているようなテキストファイルも読み取れる。

Microsoft.VisualBasic.dllへの参照を追加すれば、C#でも使用できる。

主なコンストラクタ

TextFieldParser(string)
読み込むファイルのパスを指定してインスタンスを生成する。
TextFieldParser(Stream, Encoding)
読み込むデータストリームと、エンコーディングを指定してインスタンスを生成する。

主なプロパティ

TextFieldType (FieldType)
読み込む対象のフォーマットを指定する。 デリミタで区切られるデータで構成されるフォーマット(FieldType.Delimited)か、固定長の幅のデータで構成されるフォーマット(FieldType.FixedWidth)かどちらかを指定する。
CommentTokens (string[])
コメントとして扱う行頭文字をstring[]で指定する。 例えば#で始まる行と'で始まる行をコメント行としたい場合は、{"#", "'"}を指定する。
Delimiters (string[])
デリミタとして扱う文字をstring[]で指定する。 例えばCSV形式のように,を区切り文字としたい場合は、{","}を指定する。
FieldWidths (int[])
TextFieldTypeがFieldType.FixedWidthの場合、それぞれのフィールドの幅をint[]で指定する。 例えば任意の1行のフォーマット「名前が10文字、住所が30文字、電話番号が11桁」であれば、{10, 30, 11}のように指定する。
HasFieldsEnclosedInQuotes (bool)
フィールドに改行やデリミタを含める為に引用符を使っているようなフォーマットを考慮する場合は、Trueを指定する。
TrimWhiteSpace (bool)
フィールドを読み取った結果、前後に空白が含まれる場合に、その空白を削除するかどうか指定する。
EndOfData (bool)
このプロパティがTrueのとき、これ以上読み取れるデータがないことを表す。
LineNumber (int)
現在の行番号を取得する。 これ以上読み取れるデータがない場合(EndOfDataがtrueのとき)は、-1が返される。

TextFieldParserには改行文字を指定するプロパティは用意されていない。 StreamReaderと同様に、CR(キャリッジリターン)、LF(ラインフィード)、CR+LFの各改行コードまでを1行として扱う。

主なメソッド

ReadFields()
一行読み取り、指定されたフォーマットに従って解析した結果をstring[]で返す。 フォーマットに反する行を読み取ろうとした場合は、MalformedLineExceptionがスローされる。
PeekChars(int)
ストリームの読み取り位置を変えずに、指定された文字数分読み取る。 このメソッドを呼ぶとき、ただ単に直前に解析した行の続きを読むのではなく、コメント行などを無視して次に解析できる行を読もうとする。 そのため、PeekChars()メソッドではコメント行や不正な形式の行は読み取ることはできない。

主な呼び出し順序

  1. new TextFieldParser()
  2. ReadFields()
  3. Close()

使用例

CSVファイルを読む込む例

CSVファイルを読む込む
using System;
using System.Text;
using Microsoft.VisualBasic.FileIO;

class Sample {
  static void Main()
  {
    using (TextFieldParser parser = new TextFieldParser("sample.csv", Encoding.UTF8)) {
      // フィールドはデリミタにより区切られている(固定長の形式ではない)
      parser.TextFieldType = FieldType.Delimited;

      // 「#」で始まる行をコメントとして扱う
      parser.CommentTokens = new[] {"#"};

      // 「,」をデリミタとして扱う
      parser.Delimiters = new[] {","};

      // 引用符でくくられたフィールドを持つ
      parser.HasFieldsEnclosedInQuotes = true;

      // フィールドの前後に含まれる空白をトリムする
      parser.TrimWhiteSpace = true;

      // データがなくなるまで読み取り続ける
      while (!parser.EndOfData) {
        try {
          Console.Write("{0:D3} : ", parser.LineNumber);

          // 一行分のフィールドを読む
          string[] fields = parser.ReadFields();

          foreach (string field in fields) {
            // 読み取った各フィールドを山括弧でくくって表示する
            Console.Write("<{0}> ", field);
          }

          Console.WriteLine();
        }
        catch (MalformedLineException ex) {
          // 読み込んでいる途中で指定したフォーマットに反するような行が見つかった場合
          Console.WriteLine(ex.Message);

          // 読み込めなかった行に関する情報を表示する
          Console.WriteLine("Error at line {0}: {1}", parser.LineNumber, parser.ErrorLine);
        }
      }
    }
  }
}
CSVファイルを読む込む
Imports System
Imports System.Text
Imports Microsoft.VisualBasic.FileIO

Class Sample
  Shared Sub Main()
    Using parser As New TextFieldParser("sample.csv", Encoding.UTF8)
      ' フィールドはデリミタにより区切られている(固定長の形式ではない)
      parser.TextFieldType = FieldType.Delimited

      ' 「#」で始まる行をコメントとして扱う
      parser.CommentTokens = New String() {"#"}

      ' 「,」をデリミタとして扱う
      parser.Delimiters = New String() {","}

      ' 引用符でくくられたフィールドを持つ
      parser.HasFieldsEnclosedInQuotes = True

      ' フィールドの前後に含まれる空白をトリムする
      parser.TrimWhiteSpace = True

      ' データがなくなるまで読み取り続ける
      Do Until parser.EndOfData
        Try
          Console.Write("{0:D3} : ", parser.LineNumber)

          ' 一行分のフィールドを読む
          Dim fields As String() = parser.ReadFields()

          For Each field As String In fields
            ' 読み取った各フィールドを山括弧でくくって表示する
            Console.Write("<{0}> ", field)
          Next

          Console.WriteLine()
        Catch ex As MalformedLineException
          ' 読み込んでいる途中で指定したフォーマットに反するような行が見つかった場合
          Console.WriteLine(ex.Message)

          ' 読み込めなかった行に関する情報を表示する
          Console.WriteLine("Error at line {0}: {1}", parser.LineNumber, parser.ErrorLine)
        End Try
      Loop
    End Using
  End Sub
End Class
sample.csv
123,abc,XYZ
1,23,456,7890
#コメント行
,日本語,

上記のような内容のCSV形式のテキストファイル(エンコーディングはUTF-8)を用意すると、次のような結果となる。

実行結果
001 : <123> <abc> <XYZ> 
002 : <1> <23> <456> <7890> 
003 : <> <日本語> <> 

sample.csvを、次のように不正な形式やクオートを含む内容にした場合は、

sample.csv
#,comment,line,with,delimiter
"column with 
newline","column with ""quotes""","column, with, commas","   column   with    spaces   "
invalid (format line;","
"invalid new line,
あ、i,う。
# end of data

次のような結果となる。

実行結果
001 : <column with 
newline> <column with "quotes"> <column, with, commas> <column   with    spaces> 
004 : 現在の区切り記号を使用して、行 4 を解析できません。
Error at line 6: invalid (format line;","
"invalid new line,
006 : <あ、i> <う。> 

固定長形式のテキストファイルを読む込む例

固定長形式のテキストファイルを読む込む
using System;
using System.Text;
using Microsoft.VisualBasic.FileIO;

class Sample {
  static void Main()
  {
    using (TextFieldParser parser = new TextFieldParser("sample.txt", Encoding.UTF8)) {
      // フィールドは固定長形式
      parser.TextFieldType = FieldType.FixedWidth;

      // 「;」または「-」で始まる行をコメントとして扱う
      parser.CommentTokens = new[] {";", "-"};

      // 各フィールドの桁数は10, 5, 0(可変長)
      parser.FieldWidths = new[] {10, 5, 0};

      // フィールドの前後に含まれる空白をトリムする
      parser.TrimWhiteSpace = true;

      // データがなくなるまで読み取り続ける
      while (!parser.EndOfData) {
        try {
          Console.Write("{0:D3} : ", parser.LineNumber);

          // 一行分のフィールドを読む
          string[] fields = parser.ReadFields();

          foreach (string field in fields) {
            // 読み取った各フィールドを山括弧でくくって表示する
            Console.Write("<{0}> ", field);
          }

          Console.WriteLine();
        }
        catch (MalformedLineException ex) {
          // 読み込んでいる途中で指定したフォーマットに反するような行が見つかった場合
          Console.WriteLine(ex.Message);

          // 読み込めなかった行に関する情報を表示する
          Console.WriteLine("Error at line {0}: {1}", parser.LineNumber, parser.ErrorLine);
        }
      }
    }
  }
}
固定長形式のテキストファイルを読む込む
Imports System
Imports System.Text
Imports Microsoft.VisualBasic.FileIO

Class Sample
  Shared Sub Main()
    Using parser As New TextFieldParser("sample.txt", Encoding.UTF8)
      ' フィールドは固定長形式
      parser.TextFieldType = FieldType.FixedWidth

      ' 「;」または「-」で始まる行をコメントとして扱う
      parser.CommentTokens = New String() {";", "-"}

      ' 各フィールドの桁数は10, 5, 0(可変長)
      parser.FieldWidths = New Integer() {10, 5, 0}

      ' フィールドの前後に含まれる空白をトリムする
      parser.TrimWhiteSpace = True

      ' データがなくなるまで読み取り続ける
      Do Until parser.EndOfData
        Try
          Console.Write("{0:D3} : ", parser.LineNumber)

          ' 一行分のフィールドを読む
          Dim fields As String() = parser.ReadFields()

          For Each field As String In fields
            ' 読み取った各フィールドを山括弧でくくって表示する
            Console.Write("<{0}> ", field)
          Next

          Console.WriteLine()
        Catch ex As MalformedLineException
          ' 読み込んでいる途中で指定したフォーマットに反するような行が見つかった場合
          Console.WriteLine(ex.Message)

          ' 読み込めなかった行に関する情報を表示する
          Console.WriteLine("Error at line {0}: {1}", parser.LineNumber, parser.ErrorLine)
        End Try
      Loop
    End Using
  End Sub
End Class
sample.txt
;Name     Age  Mail Address
-----------------------------------------
Alice     16   alice@example.net
Bob       42   bob@example.com
Charlie   3    charlie@example.com
ありす    9    alice@jp.example.net

上記のような内容のテキストファイル(エンコーディングはUTF-8)を用意すると、次のような結果となる。

実行結果
001 : <Alice> <16> <alice@example.net> 
004 : <Bob> <42> <bob@example.com> 
005 : <Charlie> <3> <charlie@example.com> 
006 : <ありす    9> <ali> <ce@jp.example.net> 

上記の結果に現れているとおり、FieldWidthsプロパティで指定する幅はバイト単位ではなく文字数単位なので、全角・半角が混在する場合は注意が必要となる。