LINQ:文字列コレクションで「LIKE検索」(部分一致検索)をするには?[C#、VB]:.NET TIPS
LINQを使って文字列のコレクションを処理するときに、SQLのLIKE演算子のような「LIKE検索」を実現する方法を説明する。
対象:.NET 3.5以降
LINQを使って文字列のコレクションを処理するとき、SQLのLIKE演算子を使いたいと思ったことはないだろうか? SQLなら例えば「LIKE '%ぶた%'」と指定することで、文字列中のどこかに「ぶた」と入っている文字列を選択できる。同様なことをLINQで実現するにはどうしたらよいだろうか? 本稿ではその方法を説明する。
LINQ to SQLの場合
LINQ to SQLではSqlMethodsクラス(System.Data.Linq.SqlClient)のLikeメソッドを利用すればよい。本稿では、この説明は省略する。
SqlMethods.Likeメソッドは、LINQ to SQLのコンテキスト以外の場所で使うと、実行時にSystem.NotSupportedExceptionが発生する。では、そのような一般的なLINQのコンテキストではどうすればよいだろうか? 以降ではこの方法を解説する。
C#/VB共通の方法
「LIKE検索」を実現するには、LINQのWhereメソッド(System.Linq名前空間のEnumerableクラスに定義された拡張メソッド)に与えるラムダ式を工夫する*1。Stringクラス(System名前空間)のContains/StartsWith/EndsWithメソッドを使うか(それぞれ「%」を検索語の前後に付加/「%」を検索語に後置/「%」を検索語に前置に対応する)、Regexクラス(System.Text.RegularExpressions名前空間)を利用して正規表現によるマッチングを行えばよい(次のコード)。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions; // Regexクラスを使用する
class Program
{
// コンソール出力用のメソッド
static void WriteItems(string header, IEnumerable<string> items)
{
var output = string.Join(", ", items.ToArray());
Console.WriteLine("{0}: {1}", header, output);
}
static void Main(string[] args)
{
// サンプルデータ(文字列の配列)
string[] sampleData = { "ぶた", "こぶた", "ぶたまん", "ねぶたまつり",
"ねぷたまつり", "きつね", "ねこ", };
WriteItems("sampleData", sampleData);
Console.WriteLine();
Console.WriteLine("Stringクラスのメソッドを利用する");
// LIKE '%ぶた%':String.Containsを使う
var 前後パーセント = sampleData.Where(item => item.Contains("ぶた"));
WriteItems("LIKE '%ぶた%'", 前後パーセント);
// → LIKE '%ぶた%': ぶた, こぶた, ぶたまん, ねぶたまつり
// LIKE 'ぶた%':String.StartsWithを使う
var 後パーセント = sampleData.Where(item => item.StartsWith("ぶた"));
WriteItems("LIKE 'ぶた%'", 後パーセント);
// → LIKE 'ぶた%': ぶた, ぶたまん
// LIKE '%ぶた':String.EndsWithを使う
var 前パーセント = sampleData.Where(item => item.EndsWith("ぶた"));
WriteItems("LIKE '%ぶた'", 前パーセント);
// → LIKE '%ぶた': ぶた, こぶた
Console.WriteLine();
Console.WriteLine("正規表現でのマッチングを行う");
// LIKE 'ね_たまつり':正規表現の「.」ワイルドカードを使う
var regex1 = new Regex("^ね.たまつり$");
var 任意文字一致 = sampleData.Where(item => regex1.IsMatch(item));
WriteItems("LIKE 'ね_たまつり'", 任意文字一致);
// → LIKE 'ね_たまつり': ねぶたまつり, ねぷたまつり
// LIKE '[か-こ]%':正規表現の文字クラスを使う
var regex2 = new Regex("^[か-こ]");
var 文字範囲一致 = sampleData.Where(item => regex2.IsMatch(item));
WriteItems("LIKE '[か-こ]%'", 文字範囲一致);
// → LIKE '[か-こ]%': こぶた, きつね
#if DEBUG
Console.ReadKey();
#endif
}
}
Imports System.Text.RegularExpressions ' Regexクラスを使用する
Module Module1
' コンソール出力用のメソッド
Sub WriteItems(header As String, items As IEnumerable(Of String))
Dim output = String.Join(", ", items.ToArray())
Console.WriteLine("{0}: {1}", header, output)
End Sub
Sub Main()
' サンプルデータ(文字列の配列)
Dim sampleData As String() = {"ぶた", "こぶた", "ぶたまん", "ねぶたまつり",
"ねぷたまつり", "きつね", "ねこ"}
WriteItems("sampleData", sampleData)
Console.WriteLine()
Console.WriteLine("Stringクラスのメソッドを利用する")
' LIKE '%ぶた%':String.Containsを使う
Dim 前後パーセント = sampleData.Where(Function(item) item.Contains("ぶた"))
WriteItems("LIKE '%ぶた%'", 前後パーセント)
' → LIKE '%ぶた%': ぶた, こぶた, ぶたまん, ねぶたまつり
' LIKE 'ぶた%':String.StartsWithを使う
Dim 後パーセント = sampleData.Where(Function(item) item.StartsWith("ぶた"))
WriteItems("LIKE 'ぶた%'", 後パーセント)
' → LIKE 'ぶた%': ぶた, ぶたまん
' LIKE '%ぶた':String.EndsWithを使う
Dim 前パーセント = sampleData.Where(Function(item) item.EndsWith("ぶた"))
WriteItems("LIKE '%ぶた'", 前パーセント)
' → LIKE '%ぶた': ぶた, こぶた
Console.WriteLine()
Console.WriteLine("正規表現でのマッチングを行う")
' LIKE 'ね_たまつり':正規表現の「.」ワイルドカードを使う
Dim regex1 = New Regex("^ね.たまつり$")
Dim 任意文字一致 = sampleData.Where(Function(item) regex1.IsMatch(item))
WriteItems("LIKE 'ね_たまつり'", 任意文字一致)
' → LIKE 'ね_たまつり': ねぶたまつり, ねぷたまつり
' LIKE '[か-こ]%':正規表現の文字クラスを使う
Dim regex2 = New Regex("^[か-こ]")
Dim 文字範囲一致 = sampleData.Where(Function(item) regex2.IsMatch(item))
WriteItems("LIKE '[か-こ]%'", 文字範囲一致)
' → LIKE '[か-こ]%': こぶた, きつね
#If DEBUG Then
Console.ReadKey()
#End If
End Sub
End Module
このコードの実行結果を冒頭に掲載した。
Visual Studioからデバッグ実行したとき、コンソールがすぐに閉じてしまわないように「Console.ReadKey()」と記述してある。そこで何かキーを押すとプログラムは終了する。
このVBのコードでは、Visual Basic 2010の新機能である「配列リテラル」の記法を使ってsampleData変数を初期化している。
VBのLike演算子を使う方法
VBにはLike演算子が用意されている。それを使うと、次のコードのように書ける。
' LIKE '%ぶた%'
Dim 前後パーセント = sampleData.Where(Function(item) item Like "*ぶた*")
WriteItems("LIKE '%ぶた%'", 前後パーセント)
' → LIKE '%ぶた%': ぶた, こぶた, ぶたまん, ねぶたまつり
' LIKE 'ぶた%'
Dim 後パーセント = sampleData.Where(Function(item) item Like "ぶた*")
WriteItems("LIKE 'ぶた%'", 後パーセント)
' → LIKE 'ぶた%': ぶた, ぶたまん
' LIKE '%ぶた'
Dim 前パーセント = sampleData.Where(Function(item) item Like "*ぶた")
WriteItems("LIKE '%ぶた'", 前パーセント)
' → LIKE '%ぶた': ぶた, こぶた
' LIKE 'ね_たまつり'
Dim 任意文字一致 = sampleData.Where(Function(item) item Like "ね?たまつり")
WriteItems("LIKE 'ね_たまつり'", 任意文字一致)
' → LIKE 'ね_たまつり': ねぶたまつり, ねぷたまつり
' LIKE '[か-こ]%'
Dim 文字範囲一致 = sampleData.Where(Function(item) item Like "[か-こ]*")
WriteItems("LIKE '[か-こ]%'", 文字範囲一致)
' → LIKE '[か-こ]%': こぶた, きつね
前出のコードと異なる部分のみ記載した。
なお、Like演算子の比較動作はOption Compareステートメントだけで決定されるので注意してほしい。StringクラスやRegexクラスのように、都度指定することはできない。
*1 Where拡張メソッドの引数には、ラムダ式を与えている。ラムダ式について詳しくは、次のMSDNのドキュメントを参照していただきたい。
- MSDN:ラムダ式 (C# プログラミング ガイド)
- MSDN:ラムダ式(Visual Basic)
利用可能バージョン:.NET Framework 3.5以降
カテゴリ:クラスライブラリ 処理対象:LINQ
使用ライブラリ:Enumerableクラス(System.Linq名前空間)
使用ライブラリ:Stringクラス(System名前空間)
使用ライブラリ:Regexクラス(System.Text.RegularExpressions名前空間)
関連TIPS:正規表現を使って部分文字列を取得するには?[C#、VB]
Copyright© Digital Advantage Corp. All Rights Reserved.