連載
» 2014年12月02日 17時54分 UPDATE

.NET TIPS:LINQ:文字列コレクションで「LIKE検索」(部分一致検索)をするには?[C#、VB]

LINQを使って文字列のコレクションを処理するときに、SQLのLIKE演算子のような「LIKE検索」を実現する方法を説明する。

[山本康彦,BluewaterSoft/Microsoft MVP for Windows Platform Development]
.NET TIPS
Insider.NET

 

「.NET TIPS」のインデックス

連載目次

対象:.NET 3.5以降


 LINQを使って文字列のコレクションを処理するとき、SQLのLIKE演算子を使いたいと思ったことはないだろうか? SQLなら例えば「LIKE '%ぶた%'」と指定することで、文字列中のどこかに「ぶた」と入っている文字列を選択できる。同様なことをLINQで実現するにはどうしたらよいだろうか? 本稿ではその方法を説明する。

サンプルコードを実行しているようす

サンプルコードを実行しているようす
本文中「C#/VB共通の方法」に示したサンプルコードを実行したところ。


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

文字列の部分一致検索をするコード例(上:C#、下:VB)
このコードの実行結果を冒頭に掲載した。
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演算子を使って文字列の部分一致検索をするコード例(VB)
前出のコードと異なる部分のみ記載した。
なお、Like演算子の比較動作はOption Compareステートメントだけで決定されるので注意してほしい。StringクラスやRegexクラスのように、都度指定することはできない。

*1 Where拡張メソッドの引数には、ラムダ式を与えている。ラムダ式について詳しくは、次のMSDNのドキュメントを参照していただきたい。


利用可能バージョン:.NET Framework 3.5以降
カテゴリ:クラスライブラリ 処理対象:LINQ
使用ライブラリ:Enumerableクラス(System.Linq名前空間)
使用ライブラリ:Stringクラス(System名前空間)
使用ライブラリ:Regexクラス(System.Text.RegularExpressions名前空間)
関連TIPS:正規表現を使って部分文字列を取得するには?[C#、VB]


「.NET TIPS」のインデックス

.NET TIPS

Copyright© 1999-2017 Digital Advantage Corp. All Rights Reserved.

@IT Special

- PR -

TechTargetジャパン

この記事に関連するホワイトペーパー

RSSについて

アイティメディアIDについて

メールマガジン登録

@ITのメールマガジンは、 もちろん、すべて無料です。ぜひメールマガジンをご購読ください。