Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2011年06月17日] [更新 2015年10月03日] Delphi サンプルプログラム集
   
954_文字列の操作と正規表現
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
SkRegExp version 3.0.7 および SkRegularExpressions version 1.1.10
954_SearchStr_Halbow.zip [7,965 KB] 2015年10月03日版(EXE同梱)



  • 2015年10月03日
  • Windows 7 U64(SP1) + Delphi XE(UP1) Proで動作確認
  • コードを若干整備




01_Halbow 資料館の文字列の操作サンプルと正規表現


Halbow 資料館に「第5章 文字列の操作」という記事があります.
Halbow 資料館の記事が書かれたのは,Delphi 3 の頃からです.Windows 98 の時代です.
Windows 2000 以降,文字列操作関係の API 関数も充実してきました.Delphi も文字列操作関係の関数類が追加されてきました.Delphi 2009 になって Unicode 化されました.そこで,この Halbow 資料館の記事のサンプルプログラムと同じ動作を,Delphi 2009 以降でも使用可能にするプログラムを作成してみました.
操作対象の文字列は,Halbow 資料館の記事と同じで,TRichEdit に表示されているテキストです.

正規表現を使用するサンプルも追加していますが,Delphi XEで追加された正規表現用のユニットではなく,ここでは,小宮さん作の Delphi 用正規表現ユニット SkRegEx と TRegEx 互換 ライブラリSkRegularExpressions を使用しています.


Delphi の StrUtils ユニットには文字列の検索や操作関係の関数類が実装されています.文字列の検索や操作に,必ずしも正規表現を使用する必然性はありませんが,正規表現は強力な武器であることに間違いはありません.


[備考]
下記のサイトに Delphi 7 以前で使用可能な正規表現ライブラリがあります.





02_部分文字列を数える - Halbow 資料館の Unicode 対応


指定文字列が,RichEdit のテキスト内にいくつあるか調べるプログラムです.
[5−1 部分文字列を数える]の HowManyWords.pas を Unicode 対応にしたコードのテストです.単なる文字列の検索だけの機能を追加しています.

Unicode 化したと言っても,バイト単位ではなく,文字単位で処理するようにしただけです.したがって,Delphi 2007 以前の AnsiString 版の Delphi でも使用可能です.



図1
設計時画面
  • 他のサンプルもほとんど同じ
図2
実行開始
  • このテキストの内の Delphi という文字列の数を調査
  • 大文字と小文字は区別しないで調査
図3
実行結果

リスト1
部分文字列を数える
単なる文字列の検索にも利用可能
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    RichEdit1: TRichEdit;
    Panel1: TPanel;
    Button1: TButton;
    Edit1: TEdit;
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//-----------------------------------------------------------------------------
//  文字列検索関数
//  Halbow資料館の同名関数をUnicode化
//
//  引数
//  Str      : この文字列の中を検索する
//  SubStr   : 検索文字列
//  SearchOP : False 検索文字の有無のみ調査
//             True  検索数を取得する場合
//
//  戻り値 : 検索数
//
//  [備考]
//  StrEnd(SysUtils.pas)
//  ヌルで終わる文字列の末尾を指すポインタを返す関数
//-----------------------------------------------------------------------------
function HowManyWords(const Str, SubStr: string; SearchOP: Boolean=True):Integer;
var
  LPStr    : PChar;
  LPSubStr : PChar;
  LMaxLen  : Integer;
  LPStrEnd : PChar;
begin
  Result     := 0;
  LMaxLen    := Length(SubStr);
  if LMaxLen = 0 then exit;

  LPStr    := @Str[1];
  LPSubStr := @SubStr[1];
  LPStrEnd := StrEnd(LPStr);

  while LPStrEnd > LPStr do begin
    if StrLIComp(LPStr, LPSubStr, LMaxLen) = 0 then
    begin
      //見つかったら検索文字数だけ次へ
      Inc(result);
      if SearchOP = False then break;
      Inc(LPStr, LMaxLen);
    end else
    begin
      //見つからなかったら1文字だけ次へ
      Inc(LPStr, 1);
    end;
    Application.ProcessMessages;
  end;
end;

//=============================================================================
//  テスト用のテキストをファイルから読み込んで表示
//  テスト用の検索文字列をセット
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  RichEdit1.Lines.LoadFromFile('ATest.txt');

  Edit1.Text := 'Delphi';
end;

//=============================================================================
//  検索文字列が対象テキスト内にいくつあるかを調査
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LCount : Integer;
begin
  LCount := HowManyWords(RichEdit1.Text, Edit1.Text, True);
  MessageBox(Handle, PChar(IntToStr(LCount)), '結果', MB_OK);
end;

end.




03_部分文字列を数える - SkRegularExpressions 使用


Halbow 資料館の [5−1 部分文字列を数える] と同じような動作を正規表現で実行します.
指定文字列が,RichEdit のテキスト内にいくつあるか調べます.
SkRegularExpressions の Matches メソッドを実行した結果の,TMatchCollection の Count プロパティで取得します.このサンプルでは,Delphi と Oracle という両方の文字列の合計の数を調べています.つまり OR 検索です.正規表現では,文字列を | (パイプライン,バーティカルバー,縦棒)で並べて書くとOR検索となります.
テストに使用したテキストの中身は,前のプログラムと全く同じです.

正規表現に一致した文字列を「マッチした文字列」と言っています.このサンプルの場合は,マッチした文字列の数は 7 つということになります.その名の通り,Matches (マッチス) という名前のメソッドを使用しています.



表1
テストで使用した OR 検索の正規表現
正規表現では,このような特別な意味を持つ文字をメタ文字と言う
メタ文字自身を文字として扱う場合は,メタ文字の前に\を書く

文字 記述例 説明
| 文字列 A|文字列 B 文字列 A と文字列 B を検索
文字列 A|文字列 B|文字列 C 文字列 A と文字列 B と文字列 C を検索
以下同様,いくつでも指定可能
\||文字列 A 文字 | と文字列 A を検索


図4
実行結果
  • 大文字と小文字は区別しないで検索

リスト2
正規表現を使用して部分文字列を数える
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    RichEdit1: TRichEdit;
    Panel1: TPanel;
    Button1: TButton;
    Edit1: TEdit;
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  SkRegularExpressions;

{$R *.dfm}

//=============================================================================
//  テスト用のテキストをファイルから読み込んで表示
//  テスト用の検索文字列をセット
//  正規表現が使える
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  RichEdit1.Lines.LoadFromFile('ATest.txt');

  Edit1.Text := 'ORACLE|Delphi';
end;

//=============================================================================
//  検索文字列が対象テキスト内にいくつあるかを調査
//  小宮秀一氏のDelphi用正規表現ユニットを使用した例
//  Delphi XEで追加されたTRegExの互換ライブラリSkRegularExpressionsを使用
//
//  usesにSkRegularExpressionsが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LCollection : TMatchCollection;
begin
  LCollection := TRegEx.Matches(RichEdit1.Text, Edit1.Text, [roIgnoreCase]);
  MessageBox(Handle, PChar(IntToStr(LCollection.Count)), '結果', MB_OK);
end;

end.




04_一括検索 - Halbow 資料館の Unicode 対応と FindText 関数


Halbow 資料館の [5−2 一括検索] と同じ動作をするサンプルプログラムです.
検索した文字列の属性を変更します.ここでは,文字の色を赤くします.
Halbow 資料館のコードを Unicode 対応にしたものと,FindText 関数を使用したコードもテストもしています.

ナル終端の文字列,PChar で文字列を扱うと,ポインタ演算となり,処理は高速になりますが,制御文字等,特殊文字も考慮しなければなりません.FindText 関数のような,RichEdit 等の専用プロパティやメソッドを使用すれば,特殊文字の処理を考慮する必要がありません.



図5
実行開始画面
  • 検索対象テキストはこのページのサンプルプログラムでは全て同じ
図6
実行あ結果
  • Delphi という文字列の色が全て赤くなる

リスト3
FindText 関数を使用してテキストの一括検索
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    RichEdit1: TRichEdit;
    Panel1: TPanel;
    Edit1: TEdit;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

type
  TIntArray = array[1..MAXWORD] of Integer;

{$R *.dfm}

//-----------------------------------------------------------------------------
//  一括検索関数
//  Halbow資料館の同名関数をUnicode化
//  改行文字等,特殊文字の処理が必要な場合は修正して利用可能
//
//  引数
//  Str      : この文字列の中を検索する
//  SubStr   : 検索文字列
//  PosArray : 検索位置の情報を格納した整数配列
//
//  戻り値 : 検索数
//
//  [備考]
//  StrEnd(SysUtils.pas)
//  ヌルで終わる文字列の末尾を指すポインタを返す関数
//-----------------------------------------------------------------------------
function SearchPos(const Str, SubStr: String; var PosArray: TIntArray): Integer;
var
  LPStr    : PChar;
  LPSubStr : PChar;
  LMaxLen  : Integer;
  LIndex   : Integer;
  LPStrEnd : PChar;
begin
  Result     := 0;
  LIndex     := 0;
  LMaxLen    := Length(SubStr);
  if LMaxLen = 0 then exit;

  LPStr    := @Str[1];
  LPSubStr := @SubStr[1];
  LPStrEnd := StrEnd(LPStr);

  while LPStrEnd > LPStr do begin
    if StrLIComp(LPStr, LPSubStr,LMaxLen) = 0 then begin
      //見つかったら検索文字数だけ次へ
      Inc(Result);
      PosArray[Result] := LIndex;
      Inc(LPStr, LMaxLen);
      inc(LIndex, LMaxLen);
    end else begin
      //見つからなかったら1文字だけ次へ
      Inc(LPStr, 1);
      //改行文字は文字数に数えない
      if StrLIComp(LPStr, sLineBreak, 1) <> 0 then begin
        Inc(LIndex, 1);
      end;
    end;
    Application.ProcessMessages;
  end;
end;

//=============================================================================
//  テスト用のテキストをファイルから読み込んで表示
//  テスト用の検索文字列をセット
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  RichEdit1.WantTabs := True;
  RichEdit1.Lines.LoadFromFile('ATest.txt');

  Edit1.Text := 'Delphi';
end;

//=============================================================================
//  検索文字列が対象テキストのどの位置にあるかを調査
//  SearchPosを実行すると,検索結果の文字列の開始位置が整数配列PAに格納される
//  検察した文字列の色を変更
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LPA    : TIntArray;
  LCount : Integer;
  i      : Integer;
begin
  LCount := SearchPos(RichEdit1.Text, Edit1.Text, LPA);

  if LCount > 0 then begin
    RichEdit1.Lines.BeginUpdate;

    //検索結果を順番に取り出して,該当文字列の色を変更
    for i := 1 to LCount do begin
      RichEdit1.SelStart            := LPA[i];
      RichEdit1.SelLength           := Length(Edit1.Text);
      RichEdit1.SelAttributes.Color := clRed;
      Application.ProcessMessages;
    end;

    RichEdit1.Lines.EndUpdate;
  end else begin
    MessageBox(Handle, '検索文字列はありません.' , '結果', MB_OK);
  end;

  RichEdit1.SelLength := 0;
  //最後の検索文字列が見える位置までスクロール
  RichEdit1.Perform(EM_SCROLLCARET, 0, 0);
  RichEdit1.SetFocus;
end;

//=============================================================================
//  検索文字列が対象テキストのどの位置にあるかを調査
//  検察した文字列の色を変更
//
//  TRichEditのFindText関数を使用した例
//  Delphi 5以降のバージョンであればこの関数で十分だろう
//  FindText関数は,その内部ではEM_FINDTEXTメッセージで検索している
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LLen    : Integer;
  LCurPos : Integer;
begin
  LLen := Length(RichEdit1.Text);
  //テキストの先頭から
  LCurPos := 0;

  while True do begin
    LCurPos := RichEdit1.FindText(Edit1.Text, LCurPos, LLen, []);
    if LCurPos < 0 then break;

    RichEdit1.SelStart            := LCurPos;
    RichEdit1.SelLength           := Length(Edit1.Text);
    RichEdit1.SelAttributes.Color := clRed;
    LCurPos := LCurPos + RichEdit1.SelLength;
    Application.ProcessMessages;
  end;

  RichEdit1.SelLength := 0;
  //最後の検索文字列が見える位置までスクロール
  RichEdit1.Perform(EM_SCROLLCARET, 0, 0);
  RichEdit1.SetFocus;
end;

//=============================================================================
//  再テストのために,テキストの文字列の色を全て同じにする
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
begin
  RichEdit1.Lines.BeginUpdate;
  RichEdit1.SelectAll;
  RichEdit1.SelAttributes.Color := clBlack;
  RichEdit1.SelStart            := 0;
  RichEdit1.Lines.EndUpdate;
  RichEdit1.SetFocus;
end;

end.




05_一括検索 - SkRegExpW 使用


同じく Halbow 資料館の [5−2 一括検索] と同じような動作を,正規表現を使用して実行します.検索した文字列の色を赤くします.

正規表現で文字列,テキストを扱う場合,改行やタブ文字等の制御文字や特殊文字も対象となります.そうでないと,それらの文字の制御ができません.そこで,このサンプルでは,検索した,マッチした文字列の内,制御文字を対象外としています.また,文字の位置も,制御文字,特殊文字を含めた位置となります.これは,RichEdit で表示されているテキストにおけるキャレット位置とは違います.行末の文字の次の文字位置は,次の行の先頭ですが,実際には,行末の文字の後に,表示はされていませんが,改行文字があります.この位置の調整が必要です.

このページの正規表現を用いたサンプルコードでは,FindText 関数を使用して,表示テキストの文字位置を再取得しています.



図7
検索の実行結果
  • Oracle と Delphi という文字列の色が赤くなっている

リスト4
正規表現を使用して,テキスト内を一括検索
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    RichEdit1: TRichEdit;
    Panel1: TPanel;
    Button1: TButton;
    Button2: TButton;
    Edit1: TEdit;
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  SkRegExpW;

{$R *.dfm}

//=============================================================================
//  テスト用のテキストをファイルから読み込んで表示
//  テスト用の検索文字列をセット
//  正規表現が使える
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  RichEdit1.WantTabs := True;
  RichEdit1.Lines.LoadFromFile('ATest.txt');

  //改行文字も検索対象とする
  Edit1.Text := 'ORACLE|DELPHI';
end;

//=============================================================================
//  検索文字列が対象テキストのどの位置にあるかを調査
//  検索した文字列の色を変更
//
//  小宮秀一氏のDelphi用正規表現ユニットSkRegExpWを使用した例
//
//  usesにSkRegExpWが必要
//
//  前回のサンプルで使用したversion 1.2.4と今回の3.0.7の違い
//  version 2.0 の変更点
//  Match, MatchPos, MatchLen, NamedGroup, NamedGroupPos, NamedGroupLen
//  これらのプロパティの代わりに Groupsプロパティを使用する
//  OLD_PROPERTYを条件定義すれば利用可能
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LRegExp : TSkRegExp;
  LStr    : String;
  LLen    : Integer;
  LCurPos : Integer;
begin
  LRegExp := TSkRegExp.Create;
  try
    LRegExp.Expression := Edit1.Text;
    LRegExp.IgnoreCase := True;

    RichEdit1.Lines.BeginUpdate;

    if LRegExp.Exec(RichEdit1.Text) then begin

      LCurPos := 0;
      RichEdit1.SelStart := 0;
      repeat
        LStr := LRegExp.Groups[0].Strings[1];
        LLen := LRegExp.Groups[0].Length;

        //正規表現では,検索対象外の文字を空白行で出力することがあるのでその対策
        //制御文字も含めない
        if (LLen > 0) and (Trim(LStr) <> '') then begin
          //検索結果の位置は,制御文字等を含む位置なので,キャレット位置を再検索
          LCurPos := RichEdit1.FindText(LStr, LCurPos, Length(RichEdit1.Text), []);

          RichEdit1.SelStart            := LCurPos;
          RichEdit1.SelLength           := LLen;
          RichEdit1.SelAttributes.Color := clRed;
          LCurPos := LCurPos + LLen;
        end;
        Application.ProcessMessages;
      until (not LRegExp.ExecNext);

    end else begin
      MessageBox(Handle, '検索文字列はありません.' , '結果', MB_OK);
    end;
  finally
    LRegExp.Free;
  end;
  RichEdit1.Lines.EndUpdate;

  RichEdit1.SelLength := 0;
  //最後の検索文字列が見える位置までスクロール
  RichEdit1.Perform(EM_SCROLLCARET, 0, 0);
  RichEdit1.SetFocus;
end;

//=============================================================================
//  再テストのために,テキストの文字列の色を全て同じにする
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  RichEdit1.Lines.BeginUpdate;
  RichEdit1.SelectAll;
  RichEdit1.SelAttributes.Color := clBlack;
  RichEdit1.SelStart            := 0;
  RichEdit1.Lines.EndUpdate;
  RichEdit1.SetFocus;
end;

end.




06_一括検索 - SkRegularExpressions 使用


同じく Halbow 資料館の [5−2 一括検索] と同じような動作を,正規表現を使用して実行します.検索 (マッチ) した文字列の色を赤くします.
TRegEx 互換ライブラリ SkRegularExpressions の Matches 関数を使用しています.



図8
検索実行前
図9
検索結果
  • Oracle と Delphi という文字列の色が赤くなっている

リスト5
正規表現を使用して,テキスト内を一括検索
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    RichEdit1: TRichEdit;
    Panel1: TPanel;
    Button1: TButton;
    Button2: TButton;
    Edit1: TEdit;
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  SkRegularExpressions;

{$R *.dfm}

//=============================================================================
//  テスト用のテキストをファイルから読み込んで表示
//  テスト用の検索文字列をセット
//  正規表現が使える
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  RichEdit1.WantTabs := True;
  RichEdit1.Lines.LoadFromFile('ATest.txt');

  //検索する文字列をセット
  Edit1.Text := 'ORACLE|DELPHI';
end;

//=============================================================================
//  検索文字列が対象テキストのどの位置にあるかを調査
//  検索した文字列の色を変更
//
//  小宮秀一氏のDelphi用正規表現ユニットSkRegularExpressionsを使用した例
//
//  usesにSkRegularExpressionsが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LMatch  : TMatch;
  LStr    : String;
  LLen    : Integer;
  LCurPos : Integer;
begin
  RichEdit1.Lines.BeginUpdate;

  //TMatchで取得する値には,改行文字分が入っているので,実際の表示の位置を求め
  //るためにTRichEditのFindTextを使用
  LCurPos := 0;
  for LMatch in TRegEx.Matches(RichEdit1.Text, Edit1.Text, [roIgnoreCase]) do begin
    LStr := LMatch.Value;
    LLen := LMatch.Length;

    //正規表現では,検索対象外の文字を空白行で出力することがあるのでその対策
    //制御文字も含めない
    if (LLen > 0) and (Trim(LStr) <> '') then begin
      //検索結果の位置は,制御文字等を含む位置なので,キャレット位置を再検索
      LCurPos := RichEdit1.FindText(LStr, LCurPos, Length(RichEdit1.Text), []);

      RichEdit1.SelStart            := LCurPos;
      RichEdit1.SelLength           := LLen;
      RichEdit1.SelAttributes.Color := clRed;
      LCurPos := LCurPos + LLen;
    end;
    Application.ProcessMessages;
  end;

  RichEdit1.Lines.EndUpdate;

  if LCurPos <= 0 then begin
    MessageBox(Handle, '検索文字列はありません.' , '結果', MB_OK);
  end;

  RichEdit1.SelLength := 0;
  //最後の検索文字列が見える位置までスクロール
  RichEdit1.Perform(EM_SCROLLCARET, 0, 0);
  RichEdit1.SetFocus;
end;

//=============================================================================
//  再テストのために,テキストの文字列の色を全て同じにする
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  RichEdit1.Lines.BeginUpdate;
  RichEdit1.SelectAll;
  RichEdit1.SelAttributes.Color := clBlack;
  RichEdit1.SelStart            := 0;
  RichEdit1.Lines.EndUpdate;
  RichEdit1.SetFocus;
end;

end.




07_逐次検索 - FindText 関数


Halbow 資料館の [5−3 逐次検索] と同じ動作をするサンプルプログラムです.
ボタンをクリックするたびに,現在の文字位置 (キャレット位置) から,テキストの最後尾方向に向かって,次の文字列を検索し,その文字列を選択状態にします.FindText 関数を使用したコードです.



図10
実行時画面
図11
次の文字列を検索して反転表示

リスト6
FindText 関数を使用して,テキスト内を逐次検索
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    RichEdit1: TRichEdit;
    Panel1: TPanel;
    Button1: TButton;
    Edit1: TEdit;
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  テスト用のテキストをファイルから読み込んで表示
//  テスト用の検索文字列をセット
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  RichEdit1.HideSelection := False;
  RichEdit1.Lines.LoadFromFile('ATest.txt');

  Edit1.Text := 'Delphi';
end;

//=============================================================================
//  検索文字列を現在のキャレット位置から逐次検索
//  検索した文字列をハイライト表示
//
//  TRichEditのFindText関数を使用した例
//  Delphi 5以降のバージョンであればこの関数で十分だろう
//  FindText関数は,その内部ではEM_FINDTEXTメッセージで検索している
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LCurPos : Integer;
begin
  //現在のキャレット位置を取得
  LCurPos := RichEdit1.SelStart + RichEdit1.SelLength;

  //検索実行
  LCurPos := RichEdit1.FindText(Edit1.Text, LCurPos, Length(RichEdit1.Text), []);

  if (LCurPos >= 0) then begin
    RichEdit1.SelStart  := LCurPos;
    RichEdit1.SelLength := Length(Edit1.Text);
    //最後の検索文字列が見える位置までスクロール
    RichEdit1.Perform(EM_SCROLLCARET, 0, 0);
  end else begin
    MessageBox(Handle, '検索が終了しました.' , '結果', MB_OK);
  end;
  RichEdit1.SetFocus;
end;

end.




08_逐次検索 - SkRegularExpressions 使用


Halbow 資料館の [5−3 逐次検索] と同じような動作をするサンプルプログラムです.
ボタンをクリックするたびに,現在の文字位置 (キャレット位置) から次の文字列を検索し,その文字列を選択状態にします.

ここでは,正規表現で,アルファベットの大文字または小文字,半角スペース文字だけで構成されている文字列だけを検索します.このサンプルプログラムでは,まず,テキスト全体を検索し,マッチした文字列の位置と,文字列を専用のクラス型で記録しておいて,その中から再度検索していきます.
これは,Halbow 資料館の [5−4 逐次逆検索] と同じ方法です.



表2
テストで使用した正規表現関係
* + \ 等の意味についてはその次の表を参照

正規表現 説明
[A-Z a-z] [] 内の文字のどれか1つの文字
半角の A から Z,半角スペース文字,半角の a から z のどれか1文字
[A-Z a-z]* [] 内の文字の組み合わせだけで構成されている文字列か空文字
[] 内の文字がいくつあっても,同じ文字が連続していてもマッチする
該当する文字列がない場合は,空文字が検索結果となる (空文字としてマッチ)
[A-Z a-z]+ [] 内の文字の組み合わせだけで構成されている文字列
[] 内の文字がいくつあっても,同じ文字が連続していてもマッチする
該当する文字列がない場合は,検索結果として出力されない
[\[A-Z a-z] 文字 [,半角の A から Z,半角スペース文字,半角の a から z のどれか 1 文字
[] 内では,メタ文字を文字として指定する場合,前に \ が必要ないこともある
[-]+ かがきぎくぐけげこ の 1 文字以上で構成された文字列
50 音表の順ではなく,漢字コード表の順なので,濁音も入ることに注意
かきくけこ の組み合わせの文字列を検索したければ,[かきくけこ] と書く


表3
文字数の指定に使用する特殊文字
* + ? [ ] . $ ^ | ( ) は,正規表現ではメタ文字と言われ,特殊な意味を持つ文字
メタ文字自身を文字として指定するには,メタ文字の前に\を付けて,例えば \*  と書く
\ も特殊文字で,ある文字との組み合わせて特殊な意味を持つ.例えば \n は改行文字

正規表現 説明
* * の直前の文字が全くないか,1 個以上連続するという意味
[] の後に書けば,[] 内の文字が 1 つ以上連続した文字列を意味する
+ + の直前の文字が1個以上連続するという意味
今回のサンプルプログラムでは,本当はこちらを使用すべき
? ? の直前の文字が全くないか,1 個だけあるという意味
[] の後に書くと,文字列としてではなく,[] 内の文字 1 個 1 個の検出となる
文字の出現頻度等の調査に利用できる


図12
検索開始
  • 最初のマッチ文字列は,Delphi
図13
次検索
  • 次の検索結果 (マッチした) 文字列を反転表示
図14
最後の検索結果文字列
  • 最後の検索結果 (最後のマッチ) 文字列は The Oracle at Delphi

リスト7
正規表現を使用して,テキスト内を逐次検索
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls;

type
  TplDataListItem =class(TObject)
  private
    FFindStr : String;
    FFindPos : Integer;
  public
    property FindStr : String  read FFindStr write FFindStr;
    property FindPos : Integer read FFindPos write FFindPos;
  end;

  TForm1 = class(TForm)
    RichEdit1: TRichEdit;
    Panel1: TPanel;
    Button1: TButton;
    Edit1: TEdit;
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  SkRegularExpressions;

{$R *.dfm}

//=============================================================================
//  テスト用のテキストをファイルから読み込んで表示
//  テスト用の検索文字列をセット
//  正規表現が使える
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  RichEdit1.HideSelection := False;
  RichEdit1.Lines.LoadFromFile('ATest.txt');

  //AからZまでと半角の空白とaからzの組み合わせの文字列
  //[A-Z a-z\n] とすれば1文字づつが検索対象となる
  Edit1.Text := '[A-Z a-z]*';
end;

//=============================================================================
//  検索文字列の逐次検索
//
//  小宮秀一氏のDelphi用正規表現ユニットSkRegularExpressionsを使用した例
//
//  Halbow資料館のコードでは,文字列の位置のみを整数配列に格納しているが,正規
//  表現では,複数の異なる文字列を検索する場合もあるので,検索結果の文字列も格
//  納するようにしている
//
//  対象テキストが途中で編集されないという前提であれば
//  MatchList : TMatchCollection;
//  と定義して,MatchListをグローバル変数して保持しておくことも考えられる
//  MatchList := TRegEx.Matches(RichEdit1.Text, Edit1.Text, [roIgnoreCase]);
//
//  usesにSkRegularExpressionsが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LMatch        : TMatch;
  LDataList     : TList;
  LDataItem     : TplDataListItem;
  LPos          : Integer;
  LCurPos       : Integer;
  LCurPosOrigin : Integer;
  LStr          : String;
  LLen          : Integer;
  i             : Integer;
begin
  LDataList := TList.Create;

  //現在のキャレット位置を取得
  LCurPosOrigin := RichEdit1.SelStart + RichEdit1.SelLength;

  //検索実行
  LCurPos := 0;
  for LMatch in TRegEx.Matches(RichEdit1.Text, Edit1.Text, [roIgnoreCase]) do begin
    LStr := LMatch.Value;
    LLen := LMatch.Length;

    //正規表現では,検索対象外の文字を空白行で出力することがあるのでその対策
    //制御文字も含めない
    if (LLen > 0) and (Trim(LStr) <> '') then begin
      //検索結果の位置は,制御文字等を含む位置なのでキャレット位置を再検索
      LCurPos := RichEdit1.FindText(LStr, LCurPos, Length(RichEdit1.Text), []);

      LDataItem := TplDataListItem.Create;
      LDataItem.FindPos := LCurPos;
      LDataItem.FindStr := LStr;
      LCurPos           := LCurPos + LLen;
      LDataList.Add(LDataItem);
    end;
    Application.ProcessMessages;
  end;

  if LDataList.Count > 0 then begin
    LCurPos := LCurPosOrigin;
    LPos   := 0;
    for i := 0 to LDataList.Count - 1 do begin
      LPos := TplDataListItem(LDataList[i]).FindPos;
      LLen := Length(TplDataListItem(LDataList[i]).FindStr);

      if LPos >= LCurPos then begin
        RichEdit1.SelStart  := LPos;
        RichEdit1.SelLength := LLen;
        break;
      end;
      Application.ProcessMessages;
    end;
    if LCurPos > LPos then begin
      MessageBox(Handle, '検索が終了しました.' , '結果', MB_OK);
    end;
  end else begin
    MessageBox(Handle, '検索文字列はありません.' , '結果', MB_OK);
  end;
  RichEdit1.SetFocus;

  if LDataList <> nil then begin
    for i := LDataList.Count - 1 downto 0 do begin
      TplDataListItem(LDataList.Items[i]).Free;
    end;
    LDataList.Clear;
    FreeAndNil(LDataList);
  end;
end;

end.




09_逐次逆検索 - EM_FINDTEXT メッセージ使用


Halbow 資料館の [5−4 逐次逆検索] と同じ動作をするサンプルプログラムです.
テキストの任意の位置から,テキストの先頭方向に検索していきます.EM_FINDTEXT メッセージを使用します.このメッセージは,FindText 関数内部でも使用されていますが,関数内部で順方向検索に設定されています.
そこで,本サンプルでは EM_FINDTEXT メッセージを直接使用して,そのパラメータで検索の方向を指定して検索していきます.



図15
検索開始
  • 最初に見つかった Delphi という文字列
図16
次の検索結果

リスト8
EM_FINDTEXT メッセージを使用して,テキスト内を逆方向に逐次検索
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    RichEdit1: TRichEdit;
    Panel1: TPanel;
    Button1: TButton;
    Edit1: TEdit;
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses RichEdit;

{$R *.dfm}

//=============================================================================
//  テスト用のテキストをファイルから読み込んで表示
//  テスト用の検索文字列をセット
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  RichEdit1.HideSelection := False;
  RichEdit1.Lines.LoadFromFile('ATest.txt');
  RichEdit1.SelStart := Length(RichEdit1.Text);

  Edit1.Text := 'DELPHI';
end;

//=============================================================================
//  逐次逆方向検索
//  EM_FINDTEXTメッセージを使用した方法
//
//  ueseにRichEditが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LCurPos : Integer;
  LFind   : TFindText;
  LuFlags : Integer;
begin
  //fuFlagsの値をFR_DOWN(usesにCommDlgが必要=1)にすれば順方向検索となる
  LuFlags := 0;
  //検索開始キャレット位置.ここから先頭方向に検索
  LCurPos := RichEdit1.SelStart;

  //検索開始位置と検索文字列を設定
  LFind.chrg.cpMin := LCurPos;
  LFind.chrg.cpMax := LCurPos + Length(RichEdit1.Text);
  LFind.lpstrText  := PChar(Edit1.Text);

  //検索実行.戻り値は検索した文字列の先頭位置
  LCurPos := SendMessage(RichEdit1.Handle, EM_FINDTEXT, LuFlags, LPARAM(@LFind));

  if (LCurPos >= 0) then begin
    RichEdit1.SelStart  := LCurPos;
    RichEdit1.SelLength := Length(Edit1.Text);
    RichEdit1.Perform(EM_SCROLLCARET, 0, 0);
  end else begin
    MessageBox(Handle, '検索が終了しました.' , '結果', MB_OK);
  end;
  RichEdit1.SetFocus;
end;

end.




10_逐次逆検索 - SkRegularExpressions 使用


Halbow 資料館の [5−4 逐次逆検索] と同じような動作をするサンプルプログラムです.
ボタンをクリックするたびに,現在の文字位置 (キャレット位置) からテキストの先頭に向かって文字列を検索し,その文字列を選択状態にします.

ここでは,正規表現で,アルファベットの大文字または小文字,半角スペース文字だけで構成されている文字列を検索します.このサンプルプログラムでは,まず,テキスト全体を検索し,マッチした文字列の位置と,文字列を専用のクラス型で記録しておいて,その中から再度検索していきます.
これは,Halbow 資料館の [5−4 逐次逆検索] と同じ方法ですが,Halobw 資料館のコードでは,文字の位置だけを整数配列に記録しています.正規表現では,マッチする文字列もその長さも同じとは限りません.そこで,文字位置とマッチした文字列を記録しています.



図17
検索開始
図18
次の検索結果

リスト9
正規表現を使用して,テキスト内を逆方向に逐次検索
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls;

type
  TplDataListItem =class(TObject)
  private
    FFindStr : String;
    FFindPos : Integer;
  public
    property FindStr : String  read FFindStr write FFindStr;
    property FindPos : Integer read FFindPos write FFindPos;
  end;

  TForm1 = class(TForm)
    RichEdit1: TRichEdit;
    Panel1: TPanel;
    Button1: TButton;
    Edit1: TEdit;
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  SkRegularExpressions;

{$R *.dfm}

//=============================================================================
//  テスト用のテキストをファイルから読み込んで表示
//  テスト用の検索文字列をセット
//  正規表現が使える
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  RichEdit1.HideSelection := False;
  RichEdit1.Lines.LoadFromFile('ATest.txt');
  RichEdit1.SelStart := Length(RichEdit1.Text);

  //AからZまでと半角の空白とaからzの組み合わせの文字列
  //[A-Z a-z] とすれば1文字づつが検索対象となる
  Edit1.Text := '[A-Z a-z]*';
end;

//=============================================================================
//  逐次逆方向検索
//
//  小宮秀一氏のDelphi用正規表現ユニットSkRegularExpressionsを使用した例
//
//  Halbow資料館のコードでは,文字列の位置のみを整数配列に格納しているが,正規
//  表現では,複数の異なる文字列を検索する場合もあるので,検索結果の文字列も格
//  納するようにしている
//
//  対象テキストが途中で編集されないという前提であれば
//  MatchList : TMatchCollection;
//  と定義して,MatchListをグローバル変数して保持しておくことも考えられる
//  MatchList := TRegEx.Matches(RichEdit1.Text, Edit1.Text, [roIgnoreCase]);
//
//  usesにSkRegularExpressionsが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LMatch        : TMatch;
  LDataList     : TList;
  LDataItem     : TplDataListItem;
  LPos          : Integer;
  LCurPos       : Integer;
  LCurPosOrigin : Integer;
  LStr          : String;
  LLen          : Integer;
  i             : Integer;
begin
  LDataList := TList.Create;

  //現在のキャレット位置を取得
  LCurPosOrigin := RichEdit1.SelStart;

  //検索実行
  LCurPos := 0;
  for LMatch in TRegEx.Matches(RichEdit1.Text, Edit1.Text, [roIgnoreCase]) do begin
    LStr := LMatch.Value;
    LLen := LMatch.Length;

    //正規表現では,検索対象外の文字を空白行で出力することがあるのでその対策
    //制御文字も含めない
    if (LLen > 0) and (Trim(LStr) <> '') then begin
      //検索結果の位置は,制御文字等を含む位置なのでキャレット位置を再検索
      LCurPos := RichEdit1.FindText(LStr, LCurPos, Length(RichEdit1.Text), []);

      LDataItem := TplDataListItem.Create;
      LDataItem.FindPos := LCurPos;
      LDataItem.FindStr := LStr;
      LCurPos           := LCurPos + LLen;
      LDataList.Add(LDataItem);
    end;
    Application.ProcessMessages;
  end;

  if LDataList.Count > 0 then begin
    LCurPos := LCurPosOrigin;
    LPos   := 0;
    for i := LDataList.Count - 1 downto 0 do begin
      LPos := TplDataListItem(LDataList[i]).FindPos;
      LLen := Length(TplDataListItem(LDataList[i]).FindStr);

      if LPos < LCurPos then begin
        RichEdit1.SelStart  := LPos;
        RichEdit1.SelLength := LLen;
        break;
      end;
      Application.ProcessMessages;
    end;

    if LCurPos <= LPos then begin
      MessageBox(Handle, '検索が終了しました.' , '結果', MB_OK);
    end;
  end else begin
    MessageBox(Handle, '検索文字列はありません.' , '結果', MB_OK);
  end;
  RichEdit1.SetFocus;

  if LDataList <> nil then begin
    for i := LDataList.Count - 1 downto 0 do begin
      TplDataListItem(LDataList.Items[i]).Free;
    end;
    LDataList.Clear;
    FreeAndNil(LDataList);
  end;
end;

end.




11_指定文字列の逐次置換 - EM_REPLACESEL メッセージ使用


Halbow 資料館にはありませんが,指定文字列を置換するサンプルプログラムです.
EM_SCROLLCARET メッセージを使用して検索した文字列を置換します.検索するたびに確認のダイアログを表示するようにしてみました.
部分文字列に対する置換には,この他,ReplaceText 関数,ReplaceStr 関数や StringReplace 関数等があります.



図19
置換実行中
  • 上の TEdit は検索する文字列
  • 下の TEdit は置換後の文字列
  • Delphi という文字列 (大文字,小文字の区別なしで検索) を DELPHI (大文字) に変換

リスト10
EM_REPLACESEL メッセージを使用して,テキスト内の文字列を逐次置換
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    RichEdit1: TRichEdit;
    Panel1: TPanel;
    Button1: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  テスト用のテキストをファイルから読み込んで表示
//  テスト用の検索文字列をセット
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  RichEdit1.HideSelection := False;
  RichEdit1.Lines.LoadFromFile('ATest.txt');

  Edit1.Text := 'Delphi';
  Edit2.Text := 'DELPHI';
end;

//=============================================================================
//  検索文字列を現在のキャレット位置から逐次検索して置換する
//  置換確認用のダイアログを表示
//
//  EM_REPLACESELメッセージを使用
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LCurPos  : Integer;
  LDestStr : String;
begin
  LCurPos := 0;

  while True do begin
    //現在のキャレット位置を取得
    LCurPos := RichEdit1.SelStart + RichEdit1.SelLength;

    //検索実行
    LCurPos := RichEdit1.FindText(Edit1.Text, LCurPos, Length(RichEdit1.Text), []);
    if LCurPos < 0 then break;

    if (LCurPos >= 0) then begin
      RichEdit1.SelStart  := LCurPos;
      RichEdit1.SelLength := Length(Edit1.Text);
      RichEdit1.Perform(EM_SCROLLCARET, 0, 0);

      if MessageBox(0, '置換しますか.', '文字列置換', MB_OKCANCEL) = ID_OK then begin
        LDestStr := Edit2.Text;
        RichEdit1.Perform(EM_REPLACESEL, 1, LPARAM(PChar(LDestStr)));

        //キャレットを置換した文字列の次に移動しておく
        RichEdit1.SelStart := LCurPos + Length(LDestStr);
      end else begin
        break;
      end;
    end;
  end;

  if LCurPos < 0 then begin
    MessageBox(Handle, '終了しました.' , '結果', MB_OK);
  end;

  RichEdit1.SetFocus;
end;

end.




12_半角英字の文字列を一括変換 - SkRegExpW 使用


Halbow 資料館の [5−5 1バイト文字列の一括変換] と同じ動作をするサンプルです.
Halbow 資料館では,1 バイトとなっていますが,実際には半角スペース文字と英文字で構成された文字列の置換です.Halbow 資料館のサンプルと同じように,マッチした文字列の前に<b>を,後ろに </b> という文字列を追加します.

このサンプルプログラムをそのまま HTML テキストに適用すると,この条件でマッチした文字列全てを置換してしまいます.実際には,<a> タグ等,特定のタグ内の文字列を除外するとか,選択範囲内のテキストだけを対象にする等の工夫が必要です.




表4
テストで使用した置換専用の特殊文字(置換専用メタ文字)

正規表現 説明
$& 検索の結果,取得できた文字列 (マッチした文字列)


図20
置換前
図21
置換実行結果
    • マッチした文字列の前に <b>
      後に </b> が付加されている

リスト11
正規表現を使用して,テキスト内の文字列を一括置換
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    RichEdit1: TRichEdit;
    Panel1: TPanel;
    Button1: TButton;
    Edit1: TEdit;
    Splitter1: TSplitter;
    RichEdit2: TRichEdit;
    Button2: TButton;
    Button3: TButton;
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
    procedure Button3Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses SkRegExpW;

{$R *.dfm}

//=============================================================================
//  テスト用のテキストをファイルから読み込んで表示
//  テスト用の検索文字列をセット
//  正規表現が使える
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  RichEdit1.Clear;
  RichEdit2.Clear;
  RichEdit1.ScrollBars := ssVertical;
  RichEdit2.ScrollBars := ssVertical;

  RichEdit1.Lines.LoadFromFile('ATest.txt');

  //半角英字の組み合わせの文字列を検索
  Edit1.Text := '[a-z A-Z!-"]*';
end;

//=============================================================================
//  半角英字の一括置換
//  文字列の前に<b>を,後ろに</b>を付加
//
//  小宮秀一氏のDelphi用正規表現ユニットSkRegExpWを使用した例
//
//  usesにSkRegExpWが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LRegExp     : TSkRegExp;
  LReplaceStr : String;
begin
  RichEdit2.Clear;
  RichEdit2.Update;

  LRegExp := TSkRegExp.Create;
  try
    LRegExp.Expression := Edit1.Text;
    LRegExp.IgnoreCase := True;

    //置換する文字列.SkRegExpWのヘルプの[置換専用のメタ文字]参照
    LReplaceStr := '<b>' + '$&' + '</b>';
    //置換実行
    RichEdit2.Text := LRegExp.Replace(RichEdit1.Text, LReplaceStr);
  finally
    LRegExp.Free;
  end;
end;

//=============================================================================
//  半角英字の一括置換
//  文字列の前に<b>を,後ろに</b>を付加
//
//  関数RegReplaceを使用した例
//
//  usesにSkRegExpWが必要
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  LReplaceStr : String;
begin
  RichEdit2.Clear;
  RichEdit2.Update;

  //置換する文字列.SkRegExpWのヘルプの[置換専用のメタ文字]参照
  LReplaceStr := '<b>' + '$&' + '</b>';
  //置換実行
  RichEdit2.Text := RegReplace(Edit1.Text, RichEdit1.Text, LReplaceStr, []);
end;

//=============================================================================
//  テストのためにクリア
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
begin
  RichEdit2.Clear;
end;

end.




13_半角英字の文字列を一括変換 - SkRegularExpressions 使用


Halbow 資料館の [5−5 1バイト文字列の一括変換] と同じ動作をするサンプルです.
Halbow 資料館では,1 バイトとなっていますが,実際には半角スペース文字と英文字で構成された文字列の置換です.Halbow 資料館のサンプルと同じように,マッチした文字列の前に <b> を,後ろに </b> という文字列を追加します.

このサンプルプログラムをそのまま HTML テキストに適用すると,この条件でマッチした文字列全てを置換してしまいます.実際には,<a> タグ等,特定のタグ内の文字列を除外するとか,選択範囲内のテキストだけを対象にする等の工夫が必要です.




図22
置換前
図23
置換実行後
  • マッチした文字列の前に <b>
    後ろに </b> が付加されている

リスト12
正規表現を使用して,テキスト内の文字列を一括置換
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls;

type
  TForm1 = class(TForm)
    RichEdit1: TRichEdit;
    Panel1: TPanel;
    Edit1: TEdit;
    Splitter1: TSplitter;
    RichEdit2: TRichEdit;
    Button1: TButton;
    Button2: TButton;
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

uses
  SkRegularExpressions;

{$R *.dfm}

//=============================================================================
//  テスト用のテキストをファイルから読み込んで表示
//  テスト用の検索文字列をセット
//  正規表現が使える
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  RichEdit1.Clear;
  RichEdit2.Clear;
  RichEdit1.ScrollBars := ssVertical;
  RichEdit2.ScrollBars := ssVertical;

  RichEdit1.Lines.LoadFromFile('ATest.txt');

  //半角英字の組み合わせの文字列を検索
  Edit1.Text := '[a-z A-Z!-"]*';
end;

//=============================================================================
//  半角英字の一括置換
//  文字列の前に<b>を,後ろに</b>を付加
//
//  小宮秀一氏のDelphi用正規表現ユニットSkRegularExpressionsを使用した例
//
//  usesにSkRegularExpressionsが必要
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LReplaceStr : String;
begin
  RichEdit2.Clear;
  RichEdit2.Update;

  //置換する文字列.SkRegExpWのヘルプの[置換専用のメタ文字]参照
  LReplaceStr := '<b>' + '$&' + '</b>';
  //置換実行
  RichEdit2.Text := TRegEx.Replace(RichEdit1.Text, Edit1.Text, LReplaceStr);
end;

//=============================================================================
//  テストのためにクリア
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  RichEdit2.Clear;
end;

end.




14_半角英字文字列の条件付き一括変換 - SkRegularExpressions


[13_半角英字の文字列を一括変換] とほとんど同じですが,マッチした文字列の内容によって,処理を分けたい場合の参考サンプルです.
ここでは,Delphi という文字列に対してだけ別の処理をします.TMatchEvaluator の処理用関数を指定します.この関数名は任意です.



図24
実行開始画面
  • 半角の英文字の文字列を検索
図25
置換結果
  • Delphi という文字列だけは別置換
  • 他のマッチ文字列は全て同じ置換

リスト13
正規表現を使用して,テキスト内の文字列を条件付きで置換
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls, ComCtrls, ExtCtrls, SkRegularExpressions;

type
  TForm1 = class(TForm)
    RichEdit1: TRichEdit;
    Panel1: TPanel;
    Edit1: TEdit;
    Splitter1: TSplitter;
    RichEdit2: TRichEdit;
    Button1: TButton;
    Button2: TButton;
    procedure FormShow(Sender: TObject);
    procedure Button1Click(Sender: TObject);
    procedure Button2Click(Sender: TObject);
  private
    { Private 宣言 }
    function EValReplace(const AMatch: TMatch): String;
  public
    { Public 宣言 }
  end;

var
  Form1: TForm1;

implementation

{$R *.dfm}

//=============================================================================
//  テスト用のテキストをファイルから読み込んで表示
//  テスト用の検索文字列をセット
//  正規表現が使える
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  RichEdit1.Clear;
  RichEdit2.Clear;
  RichEdit1.ScrollBars := ssVertical;
  RichEdit2.ScrollBars := ssVertical;

  RichEdit1.Lines.LoadFromFile('ATest.txt');

  //半角英字の組み合わせの文字列を検索
  Edit1.Text := '[a-z A-Z!-"]*';
end;

//=============================================================================
//  条件付きの置換用関数
//=============================================================================
function TForm1.EValReplace(const AMatch: TMatch): String;
begin
  //検索結果の文字列が'Delphi'のと時は'Delphi 2010'に
  //それ以外の時は文字列の左端にを,右端にを付加
  if UpperCase(AMatch.Value) = 'DELPHI' then begin
    Result := 'Delphi 2010';
  end else begin
    Result := '<b>' + AMatch.Value + '</b>';
  end;
end;

//=============================================================================
//  半角英字の一括置換
//  検索結果の文字列によって処理を分ける場合
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  LEvaluator : TMatchEvaluator;
begin
  RichEdit2.Clear;
  RichEdit2.Update;

  //置換処理用関数を定義
  LEvaluator := EValReplace;
  //置換実行
  RichEdit2.Text := TRegEx.Replace(RichEdit1.Text, Edit1.Text, LEvaluator, []);
end;

//=============================================================================
//  テストのためにクリア
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
begin
  RichEdit2.Clear;
end;

end.