特集:正規表現によるテキストファイルパースVisual Studio Magazine(2/8 ページ)

» 2004年12月07日 14時05分 公開
[Francesco Balena, Marco Bellinaso,FTPOnline]
^(?<first>.{6})(?<last>.{8})(?<city>.+)$

 「ドット(.)」は、「任意の文字」表す。すなわち、「.{6}」は、「任意の6文字」である。

 「(?<first>.{6})」という式は、最初の6文字と一致する「first」という名前のグループを作る。同様に「(?<last>.{8})」という式は、次の8文字と一致する「last」という名前のグループを作る。最後の「(?<city>.+)」という式は、残った行末部分までの文字をグループ化した「city」という名前のグループを作る。「^」と「$」という文字は、それぞれ、「行頭」と「行末」だ。

 ファイルをパースするには、VBやC#を使って、この正規表現を使った短いルーチンを書ける(リスト1は、ここからダウンロードできる)。

 下記のVB.NETおよびC#のコードは、正規表現を使って固定長のテキストファイルをどのようにパースするかを示した断片的なものだ。カンマ区切りテキストファイル(ダブルクォーテーションによってクォートされたものも含む)をパースするには、正規表現のパターン部分を編集すれば良く、その他のステートメントを変更する必要はない。

リスト1■VB.NET、C#でテキストファイルをパースするスマートな方法
' VB.NET
Dim sr As New StreamReader("c:\data.txt")
Dim pattern As String = _
"^(?<first>.{6})(?<last>.{8})(?<city>.+)$"
Dim re As New Regex(pattern)
Do While sr.Peek <> -1
Dim ma As Match = re.Match(sr.ReadLine())
Console.Write("First name = " & _
ma.Groups("first").Value.TrimEnd())
Console.Write(", Last name = " & _
ma.Groups("last").Value.TrimEnd())
Console.WriteLine(", City= " & _
ma.Groups("city").Value.TrimEnd())
Loop
sr.Close()

// C#
StreamReader sr = new
StreamReader("c:\\data.txt");
string pattern =
@"^(?<first>.{6})(?<last>.{8})(?<city>.+)$";
Regex re = new Regex(pattern);
while ( sr.Peek != -1 )
{
Match ma = re.Match(sr.ReadLine());
Console.Write("First name = " +
ma.Groups["first"].Value.TrimEnd());
Console.Write(", Last name = " +
ma.Groups["last"].Value.TrimEnd());
Console.WriteLine(", City= " +
ma.Groups["city"].Value.TrimEnd());
}
sr.Close();

 正規表現に基づいたこのアプローチの美しいところは、フィールド長や区切り記号(デリミタ)が変わっても、それに対応させるのが信じられないほど容易という点だ。

 たとえば、固定長フィールドがセミコロンで区切られる場合、コードを変更することなく、次のように正規表現を修正すればよい。

^(?<first>.{6});(?<last>.{8});(?<city>.+)$

 いちど正規表現の動きを理解してしまえば、パーサールーチンの作成や修正は、まるで子供の遊びのように簡単になってしまうだろう。――Francesco Balena

区切り記号で区切られたテキストファイルで正規表現を使う

 さて、ほかのアプリケーションとのデータ交換に主として使われている「区切り記号で区切られたテキストファイル」をパースするプログラムを書きたいとしよう。

 フィールドは、それぞれ、「カンマ」「セミコロン」「タブ」や、その他の特殊記号によって分割される。さらに自体が複雑なのは、そのようなファイルには、シングルクォート(')やダブルクォート(")が値に含まれることを許すという点だ。

 この時、パースするためにStringクラスのSplitメソッドを使うことはできない。なぜなら、クォートされた値が、「"Doe,John"」のように、区切り記号を含んでいるならば、正しい結果とならないからだ。

 正規表現は、まさにそのような時の救い主だ。

© Copyright 2001-2005 Fawcette Technical Publications

注目のテーマ