Delphi Programming / Object Pascal
サイトのトップページへ リンクのページへ ロゴマーク
[掲載 2011年06月19日] [更新 2016年12月24日] Delphi サンプルプログラム集
   
955_文字列の操作 ( 2 ) と正規表現
動作確認等 Windows 7 U64(SP1) + Delphi XE(UP1) Pro
SkRegularExpressions 0.91 (Delphi 2006 以降用)
955_SearchStr2_Halbow.zip [6,458 KB] 2018年08月15日版(EXE同梱)



  • 2012年01月22日
  • デリミタ関係の記事を追加
  • 2013年06月04日
  • ExtractHTTPFields 関数を使用した文字列の分解の参考コードを追加
  • 2015年10月09日
  • デリミタの処理関係サンプルを内容別に別項目とした
  • 2016年12月24日
  • デリミタの処理 (文字列の分割) 関係サンプルを新規作成したページに移動




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


Halbow 資料館に [文字列の操作(その2)] という記事があります.
Halbow 資料館の記事が書かれたのは,Delphi 3 の頃からです.Windows 98 の時代です.Delphi も2009 になって Unicode化 されました.Halbow 資料館の該当記事のサンプルコードも,一部を除き,Delphi 4 以降でも使用できますが,同じ動作,機能を,正規表現で実行するプログラムを作成してみました.
正規表現には,小宮さん作の TRegEx 互換 ライブラリ SkRegularExpressions を使用しています.


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


また,Delphi XE3 では以下のような TCharHelper, TStringHelper が実装されて,文字や文字列の操作が便利になっています.





02_指定文字列で囲まれた文字列の基本テスト


このページのサンプルは,主に,ある文字列で囲まれた文字列を扱います.そのための正規表現をテストしてみます.まず,正規表現ではデフォルトで大文字と小文字を区別して検索します.それを確認します.


表1
テストで使用した正規表現関連

正規表現 説明
ABC.XYZ 文字列 ABC と XYZ の間に,改行文字以外の文字が1文字ある文字列
. (ドット・ピリオド)は改行文字以外の文字1文字を意味する
ABC..XYZ 文字列 ABC と XYZ の間に改行文字以外の文字が2文字ある文字列
. (ドット・ピリオド)は改行文字以外の1文字を意味する
以下同様に . の数が文字数を意味する
ABC.+XYZ 文字列 ABC と XYZ の間に改行文字以外の文字が1文字以上ある文字列
間に文字がない場合はマッチしない
ABC.*XYZ 文字列 ABC と XYZ の間に改行文字以外の文字が0個以上ある文字列
間に文字がない場合は,ABCXYZがマッチする
ABC[A-Z]+XYZ 文字列 ABC と XYZ の間の文字列が AからZだけで構成された文字列
間に文字がない場合はマッチしない
ABC[A-Z]*XYZ 文字列 ABC と XYZ の間の文字列が AからZだけで構成された文字列
間に文字がない場合は,ABCXYZがマッチする
ABC( )XYZ 文字列 ABC と XYZ の間の文字列が,( )内の正規表現の文字列
) の後ろに + または * がある時は,上記の [ ] の場合と同じ


図1
設計時画面
  • 他のサンプルもほとんど同じ
図2
大文字で検索
  • <B> と </B> に囲まれた文字列がマッチする
図3
小文字で検索
  • 小文字の b がないのでマッチしない

リスト1
大文字と小文字を区別して検索
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    RichEdit1: TRichEdit;
    Splitter1: TSplitter;
    RichEdit2: TRichEdit;
    Button1: TButton;
    Button2: TButton;
    Edit1: TEdit;
    Edit2: 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}

//=============================================================================
//  テスト用の文字列をRichEditにセット
//  検索用の正規表現をセット
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  RichEdit1.Clear;
  RichEdit1.Lines.Add('ABC<B>兵庫県西脇市</B>XYZ');

  //<b>と</b>間の文字列を<b></b>を含めて検索
  //大文字と小文字で指定して結果を調査
  Edit1.Text := '<B>.+</B>';
  Edit2.Text := '<b>.+</b>';
end;

//=============================================================================
//  Edit1に設定した正規表現実行
//  検索オプションの指定はデフォルト
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  Match : TMatch;
  AStr  : String;
begin
  RichEdit2.Lines.Clear;

  for Match in TRegEx.Matches(RichEdit1.Text, Edit1.Text, []) do begin
    AStr := Match.Value;
    RichEdit2.Lines.Add(AStr);
  end;
end;

//=============================================================================
//  Edit2に設定した正規表現実行
//  検索オプションの指定はデフォルト
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  Match : TMatch;
  AStr  : String;
begin
  RichEdit2.Lines.Clear;

  for Match in TRegEx.Matches(RichEdit1.Text, Edit2.Text, []) do begin
    AStr := Match.Value;
    RichEdit2.Lines.Add(AStr);
  end;
end;

end.


Button1 クリックのコードは,従来のforループを使用して,以下のようにも書けます.


procedure TForm1.Button1Click(Sender: TObject);
var
  i          : Integer;
  Collection : TMatchCollection;
  AStr       : String;
begin
  RichEdit2.Lines.Clear;

  Collection := TRegEx.Matches(RichEdit1.Text, Edit1.Text, []);

  for i := 0 to Collection.Count - 1 do begin
    AStr := Collection.Item[i].Value;
    RichEdit2.Lines.Add(AStr);
  end;
end;




03_正規表現検索オプションの MultiLine と SingleLine


大文字と小文字の区別の可否は,Matches メソッドの引数のオプション (TRegExOption 型) で設定できます.このオプションに MultiLine と SingleLine の指定もあります.これのテストです.
囲み文字列の間に改行が入っている場合は,SingleLine にすれば,行をまたがって検索します.


図4
MultiLine 設定で検索
  • 行をまたがる検索はしない
図5
SingleLine 設定で検索
  • マッチする

リスト2
指定文字列で囲まれた文字列を,行をまたがって検索する場合
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    RichEdit1: TRichEdit;
    Splitter1: TSplitter;
    RichEdit2: TRichEdit;
    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}

//=============================================================================
//  テスト用の文字列をRichEditにセット
//  検索用の正規表現をセット
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  RichEdit1.Clear;
  RichEdit1.Lines.Add('ABC<b>兵庫');
  RichEdit1.Lines.Add('県西脇市</B>XYZ');

  //<b>と</b>間の文字列を<b></b>を含めて検索
  Edit1.Text := '<B>.+</B>';
end;

//=============================================================================
//  Edit1に設定した正規表現実行(MultiLine)
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  Option : TRegExOptions;
  Match  : TMatch;
  AStr   : String;
begin
  RichEdit2.Lines.Clear;

  //検索のオプションを指定
  //大文字と小文字を区別なし
  //MultiLine検索
  Option := [roIgnoreCase, roMultiLine];

  for Match in TRegEx.Matches(RichEdit1.Text, Edit1.Text, Option) do begin
    AStr := Match.Value;
    RichEdit2.Lines.Add(AStr);
  end;
end;

//=============================================================================
//  Edit1に設定した正規表現実行(SingleLine)
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  Option : TRegExOptions;
  Match  : TMatch;
  AStr   : String;
begin
  RichEdit2.Lines.Clear;

  //検索のオプションを指定
  //大文字と小文字を区別なし
  //SingleLine検索
  Option := [roIgnoreCase, roSingleLine];

  for Match in TRegEx.Matches(RichEdit1.Text, Edit1.Text, Option) do begin
    AStr := Match.Value;
    RichEdit2.Lines.Add(AStr);
  end;
end;

end.




04_正規表現における最長マッチと最短マッチ


正規表現では,デフォルトで最長マッチという検索をします.これは,マッチする文字列を行末から検索します.<B> と </B> で囲まれた文字列の場合,</B> を行末から検索します.したがって,1 行に複数個の文字列の組み合わせがあると,期待した結果が得られません.
それを避けるには,最短マッチ検索を指定します.そうすると,<B> の後ろから検索してくれます.この指定は,検索文字数を指定するメタ文字の後に,?  を付加します.


図6
最長マッチで検索
  • <B> と </B> で囲まれる最長の文字列とマッチ
図7
最短マッチで検索
  • <B> と </B> で囲まれる最短の文字列とマッチ

リスト3
最長マッチと最短マッチのテスト
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    RichEdit1: TRichEdit;
    Splitter1: TSplitter;
    RichEdit2: TRichEdit;
    Button1: TButton;
    Button2: TButton;
    Edit1: TEdit;
    Edit2: 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.Text := 'ABC<b>兵庫県</b>BBB<B>西脇市</B>XYZ';

  //<b>と</b>間の文字列を最長マッチで検索
  //Perl互換正規表現のデフォルトは最長マッチ
  Edit1.Text := '<B>.+</B>';

  //<b>と</b>間の文字列を最短マッチで検索
  Edit2.Text := '<B>.+?</B>';
end;

//=============================================================================
//  Edit1に設定した正規表現実行
//  Perl互換正規表現のデフォルトである最長マッチで検索
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  Option : TRegExOptions;
  Match  : TMatch;
  AStr   : String;
begin
  RichEdit2.Lines.Clear;

  //検索のオプションを指定
  //大文字と小文字を区別しない
  Option := [roIgnoreCase];

  for Match in TRegEx.Matches(RichEdit1.Text, Edit1.Text, Option) do begin
    AStr := Match.Value;
    RichEdit2.Lines.Add(AStr);
  end;
end;

//=============================================================================
//  Edit2に設定した正規表現実行
//  正規表現に最短マッチのメタ文字?を入れて検索
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  Option : TRegExOptions;
  Match  : TMatch;
  AStr   : String;
begin
  RichEdit2.Lines.Clear;

  //検索のオプションを指定
  //大文字と小文字を区別しない
  Option := [roIgnoreCase];

  for Match in TRegEx.Matches(RichEdit1.Text, Edit2.Text, Option) do begin
    AStr := Match.Value;
    RichEdit2.Lines.Add(AStr);
  end;
end;

end.




05_正規表現におけるグループ化


これまでは,指定文字列で囲まれた文字列は,囲み文字列も含めてマッチしました.この囲み文字例の囲まれた部分だけを取り出したい場合は,それをグループ化します.グループ化するには,該当部分の正規表現を ( ) で括ります.実行後,この ( ) 内の正規表現の結果だけを取り出すことができます.
これには,TMatch クラスの Groups プロパティを使用します.グループの番号は 0 から始まり,0 は通常のマッチ結果です.1 が最初のグループとなります.


図8
グループ化
  • 正規表現の () 内に該当する値を取り出す

リスト4
グループ化で,各グループの正規表現のマッチ結果を取り出す
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    RichEdit1: TRichEdit;
    Splitter1: TSplitter;
    RichEdit2: TRichEdit;
    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}

//=============================================================================
//  テスト用の文字列をRichEditにセット
//  検索用の正規表現をセット
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  RichEdit1.Text := 'AAA<b>兵庫県</b>BBB<B>西脇市</B>CCC';

  //<b>と</b>間の文字列をグループ化して検索
  Edit1.Text := '<B>(.*?)</B>';
end;

//=============================================================================
//  Edit1に設定した正規表現実行
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  Option : TRegExOptions;
  Match  : TMatch;
  AStr   : String;
  i      : Integer;
begin
  RichEdit2.Lines.Clear;

  //検索のオプションを指定
  Option := [roIgnoreCase, roSingleLine];

  for Match in TRegEx.Matches(RichEdit1.Text, Edit1.Text, Option) do begin
    //Groups.Item[0]はグループなしの場合の値なので除外
    //このサンプルでは,実際にはi = 1しか存在しない(グループ化の()が1つだけ)
    for i := 1 to Match.Groups.Count - 1 do begin
      AStr := Match.Groups.Item[i].Value;
      RichEdit2.Lines.Add(AStr);
    end;
  end;
end;

end.


囲み文字列は固定ですが,グループ内の正規表現で,いろいろな条件の文字列が検索できます.
次の例は,文字列 ABC と XYZ の間に東京という文字列か,大阪という文字列がある文字列だけを検索します.間に他の文字列がある場合はマッチしません.
特定の文字列で囲まれた文字列の検索では,全ての文字を指定する以外は,( ) で括ったグループ化の正規表現を使用することが多いようです.


図9
グループ化で,間の文字列の条件を設定

リスト5
グループ化で,間の文字列の条件を設定
//=============================================================================
//  テスト用の文字列をRichEditにセット
//  検索用の正規表現をセット
//=============================================================================
procedure TForm1.FormShow(Sender: TObject);
begin
  RichEdit1.Text := 'ABC東京XYZ ABC京都XYZ ABC大阪XYZ ABC長野XYZ';

  Edit1.Text := 'ABC(東京|大阪)XYZ';
end;

//=============================================================================
//  グループ化の例
//  ABCとXYZで囲まれた文字列が,東京または大阪の文字列を検索
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  Option : TRegExOptions;
  Match  : TMatch;
begin
  RichEdit2.Lines.Clear;

  //検索のオプションを指定
  Option := [roIgnoreCase, roSingleLine];

  for Match in TRegEx.Matches(RichEdit1.Text, Edit1.Text, Option) do begin
    RichEdit2.Lines.Add(Match.Value);
  end;
end;




06_特定の文字列で囲まれた部分文字列


ここからが,Halbow 資料館のサンプルと同じ機能を正規表現を用いて実行するサンプルです.
最初は, Halbow 資料館の [特定の文字列で囲まれた部分文字列をとりだす] と同じ機能のサンプルプログラムです.
Halbow 資料館のコードも,Delphi 2010, XE で動作します.


図10
設計時画面
  • 以降のサンプルプログラムもほぼ同じ
図11
実行結果
  • 指定文字列で囲まれた文字列だけ取得

リスト6
特定の文字列で囲まれた部分文字列をとりだす
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    RichEdit1: TRichEdit;
    Splitter1: TSplitter;
    RichEdit2: TRichEdit;
    Button1: TButton;
    Button2: TButton;
    Button3: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: TEdit;
    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 SkRegularExpressions;

{$R *.dfm}

//=============================================================================
//  テスト用のテキストをファイルから読み込んで表示
//  検索用の正規表現をセット
//=============================================================================

procedure TForm1.FormShow(Sender: TObject);
begin
  RichEdit1.Clear;
  RichEdit2.Clear;
  RichEdit1.ScrollBars := ssBoth;
  RichEdit2.ScrollBars := ssBoth;

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

  //<b>と</b>で囲まれた文字列取得用の正規表現
  Edit1.Text := '<B>(.+?)</B>';

  //ここから と ここまで で囲まれた文字列取得用の正規表現
  Edit2.Text := 'ここから(.+?)ここまで';

  //$$$と$$$で囲まれた文字列取得用の正規表現
  //$ はメタ文字なので,前に\を付ける
  Edit3.Text := '\$\$\$(.+?)\$\$\$';
end;

//=============================================================================
//  Edit1に設定した正規表現実行
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  Option : TRegExOptions;
  Match  : TMatch;
  i      : Integer;
  AStr   : String;
begin
  RichEdit2.Lines.Clear;
  RichEdit2.Lines.BeginUpdate;

  //検索のオプションを指定
  Option := [roIgnoreCase, roSingleLine];

  for Match in TRegEx.Matches(RichEdit1.Text, Edit1.Text, Option) do begin
    for i := 1 to Match.Groups.Count -1 do begin
      AStr := Match.Groups.Item[i].Value;
      RichEdit2.Lines.Add(AStr);
    end;
    Application.ProcessMessages;
  end;

  RichEdit2.Lines.EndUpdate;
end;

//=============================================================================
//  Edit2に設定した正規表現実行
//=============================================================================
procedure TForm1.Button2Click(Sender: TObject);
var
  Option : TRegExOptions;
  Match  : TMatch;
  i      : Integer;
  AStr   : String;
begin
  RichEdit2.Lines.Clear;
  RichEdit2.Lines.BeginUpdate;

  //検索のオプションを指定
  Option := [roIgnoreCase, roSingleLine];

  for Match in TRegEx.Matches(RichEdit1.Text, Edit2.Text, Option) do begin
    for i := 1 to Match.Groups.Count -1 do begin
      AStr := Match.Groups.Item[i].Value;
      RichEdit2.Lines.Add(AStr);
    end;
    Application.ProcessMessages;
  end;

  RichEdit2.Lines.EndUpdate;
end;

//=============================================================================
//  Edit3に設定した正規表現実行
//=============================================================================
procedure TForm1.Button3Click(Sender: TObject);
var
  Option : TRegExOptions;
  Match  : TMatch;
  i      : Integer;
  AStr   : String;
begin
  RichEdit2.Lines.Clear;
  RichEdit2.Lines.BeginUpdate;

  //検索のオプションを指定
  Option := [roIgnoreCase, roSingleLine];

  for Match in TRegEx.Matches(RichEdit1.Text, Edit3.Text, Option) do begin
    for i := 1 to Match.Groups.Count -1 do begin
      AStr := Match.Groups.Item[i].Value;
      RichEdit2.Lines.Add(AStr);
    end;
    Application.ProcessMessages;
  end;

  RichEdit2.Lines.EndUpdate;
end;

end.




07_特定の文字列で囲まれた部分文字列を削除


Halbow 資料館の [特定の文字列で囲まれた部分文字列を削除する] と同じ機能のサンプルプログラムです.
Halbow 資料館のコードも,Delphi 2010, XE で動作します.


[備考]
文字列から特定の文字列を削除するだけであれば,StringReplace, ReplaceStr, ReplaceText 関数等が利用できます.置き換える文字列を空白文字にします.


図12
実行結果

リスト7
文字列の中から特定の文字列で囲まれた部分文字列を削除する
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    RichEdit1: TRichEdit;
    Splitter1: TSplitter;
    RichEdit2: TRichEdit;
    Button1: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: 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.Clear;
  RichEdit2.Clear;
  RichEdit1.ScrollBars := ssBoth;
  RichEdit2.ScrollBars := ssBoth;

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

  Edit1.Text := '<B>.+?</B>';
  Edit2.Text := 'ここから.+?ここまで';
  Edit3.Text := '\$\$\$.+?\$\$\$';
end;

//=============================================================================
//  設定した正規表現で検索した結果を置換
//  検索結果を囲み文字列で置き換えているだけ
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  Option      : TRegExOptions;
  AReplaceStr : String;
  DestStr     : String;
begin
  RichEdit2.Lines.Clear;
  RichEdit2.Lines.BeginUpdate;

  //検索のオプションを指定
  Option := [roIgnoreCase, roSingleLine];

  AReplaceStr := '<b></b>';
  //置換実行
  DestStr := TRegEx.Replace(RichEdit1.Lines[0], Edit1.Text, AReplaceStr, Option);
  RichEdit2.Lines.Add(DestStr);
  DestStr := TRegEx.Replace(RichEdit1.Lines[1], Edit1.Text, AReplaceStr, Option);
  RichEdit2.Lines.Add(DestStr);

  AReplaceStr := 'ここからここまで';
  //置換実行
  DestStr := TRegEx.Replace(RichEdit1.Lines[2], Edit2.Text, AReplaceStr, Option);
  RichEdit2.Lines.Add(DestStr);

  //検索で $ という文字を指定すると時は \$ とするが,置換では $$ と書く
  AReplaceStr := '$$$$$$$$$$$$';
  //置換実行
  DestStr := TRegEx.Replace(RichEdit1.Lines[3], Edit3.Text, AReplaceStr, Option);
  RichEdit2.Lines.Add(DestStr);

  RichEdit2.Lines.EndUpdate;
end;

end.




08_特定の文字列で囲まれた部分文字列を囲み文字列ごと削除


Halbow 資料館の [特定の文字列で囲まれた部分文字列を囲み文字列ごと削除する] と同じ機能のサンプルプログラムです.
マッチした文字列を空文字にして Replace メソッドを実行します.
Halbow 資料館のコードも,Delphi 2010, XE で動作します.


図13
実行結果

リスト8
文字列の中から特定の文字列で囲まれた部分文字列を囲み文字列ごと削除する
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    RichEdit1: TRichEdit;
    Splitter1: TSplitter;
    RichEdit2: TRichEdit;
    Button1: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: 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.Clear;
  RichEdit2.Clear;
  RichEdit1.ScrollBars := ssBoth;
  RichEdit2.ScrollBars := ssBoth;

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

  Edit1.Text := '<B>.+?</B>';
  Edit2.Text := 'ここから.+?ここまで';
  Edit3.Text := '\$\$\$.+?\$\$\$';
end;

//=============================================================================
//  設定した正規表現で検索した結果を置換
//  検索結果の文字列として,空文字にしているだけ
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  Option      : TRegExOptions;
  AReplaceStr : String;
  DestStr     : String;
begin
  RichEdit2.Lines.Clear;
  RichEdit2.Lines.BeginUpdate;

  //検索のオプションを指定
  Option := [roIgnoreCase, roSingleLine];

  //置換後の文字列
  AReplaceStr := '';

  //置換実行
  DestStr := TRegEx.Replace(RichEdit1.Lines[0], Edit1.Text, AReplaceStr, Option);
  RichEdit2.Lines.Add(DestStr);
  DestStr := TRegEx.Replace(RichEdit1.Lines[1], Edit1.Text, AReplaceStr, Option);
  RichEdit2.Lines.Add(DestStr);

  //置換実行
  DestStr := TRegEx.Replace(RichEdit1.Lines[2], Edit2.Text, AReplaceStr, Option);
  RichEdit2.Lines.Add(DestStr);

  //置換実行
  DestStr := TRegEx.Replace(RichEdit1.Lines[3], Edit3.Text, AReplaceStr, Option);
  RichEdit2.Lines.Add(DestStr);

  RichEdit2.Lines.EndUpdate;
end;

end.




09_特定の文字列で囲まれた部分に文字列を挿入


Halbow 資料館の [特定の文字列で囲まれた部分に文字列を挿入する] と同じ機能のサンプルプログラムです.<B> と </B> の間に何も文字列がない状態を検出するので,正規表現は,.*? を使用します (文字の量指定子には * を使用).
マッチした文字列を Replace メソッドで書き換えているだけです.
Halbow 資料館のコードも,Delphi 2010, XE で動作します.


図14
実行結果

リスト9
特定の文字列で囲まれた部分に文字列を挿入する
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    RichEdit1: TRichEdit;
    Splitter1: TSplitter;
    RichEdit2: TRichEdit;
    Button1: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: 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.Clear;
  RichEdit2.Clear;
  RichEdit1.ScrollBars := ssBoth;
  RichEdit2.ScrollBars := ssBoth;

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

  //空文字を検索する必要があるので .+? ではなく .*?
  Edit1.Text := '<B>.*?</B>';
  Edit2.Text := 'ここから.*?ここまで';
  Edit3.Text := '\$\$\$.*?\$\$\$';
end;

//=============================================================================
//  設定した正規表現で検索した結果を置換
//  囲む文字列を変数にすれば汎用的になる
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  Option      : TRegExOptions;
  AReplaceStr : String;
  DestStr     : String;
begin
  RichEdit2.Lines.Clear;
  RichEdit2.Lines.BeginUpdate;

  //検索のオプションを指定
  Option := [roIgnoreCase, roSingleLine];

  AReplaceStr := '<b>タグで囲まれた文字列</b>';
  //置換実行
  DestStr := TRegEx.Replace(RichEdit1.Lines[0], Edit1.Text, AReplaceStr, Option);
  RichEdit2.Lines.Add(DestStr);

  AReplaceStr := '<b>強調表示用タグ囲み文字列</b>';
  DestStr := TRegEx.Replace(RichEdit1.Lines[1], Edit1.Text, AReplaceStr, Option);
  RichEdit2.Lines.Add(DestStr);

  AReplaceStr := 'ここから日本語もOKここまで';
  //置換実行
  DestStr := TRegEx.Replace(RichEdit1.Lines[2], Edit2.Text, AReplaceStr, Option);
  RichEdit2.Lines.Add(DestStr);

  //検索で $ という文字を指定すると時は \$ とするが,置換では $$ と書く
  AReplaceStr := '$$$$$$pre と post が同じでもOK$$$$$$';
  //置換実行
  DestStr := TRegEx.Replace(RichEdit1.Lines[3], Edit3.Text, AReplaceStr, Option);
  RichEdit2.Lines.Add(DestStr);

  RichEdit2.Lines.EndUpdate;
end;

end.




10_特定の文字列で囲まれた部分の文字列を入れ換え


Halbow 資料館の [特定の文字列で囲まれた部分の文字列を入れ換える] と同じ機能のサンプルプログラムです.
マッチした文字列を Replace メソッドで書き換えているだけです.
Halbow 資料館のコードも,Delphi 2010, XE で動作します.


[備考]
文字列の特定の文字列を置換するだけであれば,StringReplace, ReplaceStr, ReplaceText 関数等が利用できます.


図15
実行結果

リスト10
文字列の中の特定の文字列で囲まれた部分の文字列を入れ換える
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    RichEdit1: TRichEdit;
    Splitter1: TSplitter;
    RichEdit2: TRichEdit;
    Button1: TButton;
    Edit1: TEdit;
    Edit2: TEdit;
    Edit3: 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.Clear;
  RichEdit2.Clear;
  RichEdit1.ScrollBars := ssBoth;
  RichEdit2.ScrollBars := ssBoth;

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

  Edit1.Text := '<B>.+?</B>';
  Edit2.Text := 'ここから.+?ここまで';
  Edit3.Text := '\$\$\$.+?\$\$\$';
end;

//=============================================================================
//  設定した正規表現で検索した結果を置換
//  検索結果の文字列を書き変えているだけ
//
//  囲む文字列を変数にすれば汎用的になる
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  Option      : TRegExOptions;
  AReplaceStr : String;
  DestStr     : String;
begin
  RichEdit2.Lines.Clear;
  RichEdit2.Lines.BeginUpdate;

  //検索のオプションを指定
  Option := [roIgnoreCase, roSingleLine];

  AReplaceStr := '<b>入れ換えた文字列</b>';
  //置換実行
  DestStr := TRegEx.Replace(RichEdit1.Lines[0], Edit1.Text, AReplaceStr, Option);
  RichEdit2.Lines.Add(DestStr);
  DestStr := TRegEx.Replace(RichEdit1.Lines[1], Edit1.Text, AReplaceStr, Option);
  RichEdit2.Lines.Add(DestStr);

  AReplaceStr := 'ここから入れ換えた文字列ここまで';
  //置換実行
  DestStr := TRegEx.Replace(RichEdit1.Lines[2], Edit2.Text, AReplaceStr, Option);
  RichEdit2.Lines.Add(DestStr);

  //検索で $ という文字を指定すると時は \$ とするが,置換では $$ と書く
  AReplaceStr := '$$$$$$入れ換えた文字列$$$$$$';
  //置換実行
  DestStr := TRegEx.Replace(RichEdit1.Lines[3], Edit3.Text, AReplaceStr, Option);
  RichEdit2.Lines.Add(DestStr);

  RichEdit2.Lines.EndUpdate;
end;

end.




11_HTML のタグの削除


Halbow 資料館の [HTML のタグの削除] と同じ機能のサンプルプログラムです.
Halbow 資料館のコードは,SearchPos 関数を [954] [02_一括検索 - Halbow 資料館の Unicode 対応と FindText 関数] のように,Unicode 対応コードにすれば Delphi 2010, XEで 動作します.

これだけではタグを完全に削除できない HTML コードもあります.例えば,タグ内に文字 > がある場合です.< の方はあっても問題とはなりません.> は ' " (シングルクォーテーションまたはダブルクォーテーション) で囲まれた中にあるのが普通です.そこで,' または " で囲まれた中の > を無視するような正規表現が必要となります.すると,改行以外の全ての文字を意味する . (ピリオド・ドット)は正規表現で使用できなくなります(矛盾する設定).したがって,正規表現は少し複雑になります.
ネット上を「正規表現 タグ 削除 HTML」で検索すると正規表現のサンプルがあります.サイトによって違うのは各作者の努力の成果でしょう.

HTML コード,ファイルを扱うコンポーネントでは,HTM Lコードの操作ができるものがあります.
Indy,TWebBrowser では Document 関係のインターフェイスを使用すると操作できます.
[942_TEmbeddedWBコンポーネント] の記事の TEmbeddedWB コンポーネントには,HTML 操作用のメソッドやプロパティが用意されています.


図16
実行画面
  • HTML タグの削除

リスト11
HTML のタグの削除
unit Unit1;

interface

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

type
  TForm1 = class(TForm)
    Panel1: TPanel;
    RichEdit1: TRichEdit;
    Splitter1: TSplitter;
    RichEdit2: TRichEdit;
    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.Clear;
  RichEdit2.Clear;
  RichEdit1.ScrollBars := ssBoth;
  RichEdit2.ScrollBars := ssBoth;

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

  Edit1.Text := '<.*?>';
end;

//=============================================================================
//  設定した正規表現で検索した結果を置換
//  HTMLのタグを全て削除
//=============================================================================
procedure TForm1.Button1Click(Sender: TObject);
var
  Option      : TRegExOptions;
  AReplaceStr : String;
begin
  RichEdit2.Lines.Clear;
  RichEdit2.Lines.BeginUpdate;

  //検索のオプションを指定
  Option := [roIgnoreCase, roSingleLine];

  //検索結果,つまり特定の文字列で囲まれた文字列を囲み文字列ごと削除
  AReplaceStr := '';
  //置換実行
  RichEdit2.Text := TRegEx.Replace(RichEdit1.Text, Edit1.Text, AReplaceStr, Option);

  RichEdit2.Lines.EndUpdate;
end;

end.




12_正規表現を使用しない文字列の検索と置換関係の関数


正規表現を使用しない文字列の検索や置換関係の関数には,例えば以下のようなものがあります.
Delphi XE3 で実装された TStringHelper にも,文字列の操作関係のメソッドやプロパティが実装されています.

文字列の先頭あるいは末尾の空白文字を削除する関数に Trim, TrimLeft, TrimRight 関数がありますが,これらの関数は全角の空白文字は削除しません.



ユニット名.関数名 機能 (文字列の検索)
StrUtils.LeftStr 文字列の左端 (文字列の先頭) から指定文字数の文字列を取得する
StrUtils.RightStr 文字列の右端 (文字列の末尾) から指定文字数の文字列を取得する
StrUtils.StartsStr 文字列の先頭からの文字列が指定した文字列と一致するかを調べる
大文字と小文字を区別して調べる
戻り値は論理値
StrUtils.StartsText 文字列の先頭からの文字列が指定した文字列と一致するかを調べる
大文字と小文字を区別しないで調べる
戻り値は論理値
StrUtils.EndsStr 文字列の末尾の文字列が指定した文字列と一致するかを調べる
大文字と小文字を区別して調べる
戻り値は論理値
StrUtils.EndsText 文字列の末尾の文字列が指定した文字列と一致するかを調べる
大文字と小文字を区別しないで調べる
戻り値は論理値
StrUtils.MidStr 文字列の指定位置から指定文字数の文字列を取り出す
StrUtils.ContainsStr 文字列の中の,指定した文字列を大文字と小文字を区別して検索
関数の戻り値は論理値
StrUtils.ContainsText 文字列の中の,指定した文字列を大文字と小文字を区別しないで検索
関数の戻り値は論理値
StrUtils.SearchBuf 文字列の中の,指定した文字列を検索する
検索開始位置と検索方向 (先頭または末尾から) を指定して検索する
戻り値は検索した Null 終端文字列 (大文字と小文字を区別しないで検索した時は,元の文字列内の文字列)
System.Pos 文字列の中の,指定した文字列を検索してその位置を返す
StrUtils.PosEx 文字列の中の,指定した文字列を検索してその位置を返す
検索開始の位置の指定が可能
TCustomRichEdit.FindText 文字列の中の,指定した文字列を検索してその位置を返す
TRichEdit 専用


ユニット名.関数名 機能 (文字列の置換または挿入)
SysUtils.StringReplace 文字列の中の指定した文字列を別の文字列に置換する
大文字と小文字を区別するかの指定が可能
最初に見つかった文字列だけの置換か,全てを置換するかの指定可能
StrUtils.ReplaceStr 大文字と小文字を区別して文字列を検索し,その文字列を置換する
StrUtils.ReplaceText 大文字と小文字の区別なしで文字列を検索し,その文字列を置換する
StrUtils.StuffString 文字列の指定した位置に別の文字列を挿入する
System.MoveChars 文字列の指定した位置から指定文字数を,別の文字列にコピーする
コピー先の位置の指定も可能
ヘルプでは第 3 引数がバイト数になっているが,実際は文字数
System.StringOfChar 指定した文字を指定数だけ繰り返えす文字列を作成する
つまり,同じ文字を複数結合した文字列を作成する
StrUtils.DupeString 指定した文字列を指定回数繰り返す文字列を作成する
つまり,同じ文字列を複数結合した文字列を作成する
StrUtils.ReverseString 左右が反転した文字列の作成
指定した文字列の向きを逆にした文字列を作成する


PosEx 関数を使用すると,特定の文字,あるいは文字列で囲まれた文字列を取り出す操作が比較的簡単なコードで実装できます.
下に示すコードは,HTML のタグから width 属性の値を取得する例です.


図17
特定の文字列で囲まれた文字列を取得する例

リスト12
implementation

uses
  StrUtils;

{$R *.dfm}

procedure TForm1.Button1Click(Sender: TObject);
var
  LHtmlText   : string;
  LSearchText : string;
  LPos1       : Integer;
  LPos2       : Integer;
  LText       : string;
begin
  LHtmlText   := '<img src="Images/sample_01.gif" width="300" height="203">';
  LSearchText :=  'width="';

  LPos1 := PosEx(LSearchText, LHtmlText);
  if Lpos1 > 0 then begin
    LPos1 := LPos1 + Length(LSearchText);
    LPos2 := PosEx('"', LHtmlText, LPos1);
    LText := Trim(Copy(LHtmlText, LPos1, LPos2 - LPos1));

    MessageBox(Handle, PChar(LText), '結果', MB_ICONINFORMATION);
  end;
end;


MoveChars 関数を使用すると,文字列の部分コピーができます.
下に示すコードは,元の文字列の指定文字数を別の文字列にコピーする例です.コピー先の位置をしてコピーした文字列に置き換えます.文字列検索用の関数と組み合わせて利用すれば,検索した文字列で置換する機能も実装できます.


図18
文字列の部分コピーと置換
  • '南北' と言う文字だけをコピー

リスト13
procedure TForm1.Button1Click(Sender: TObject);
var
  LStrSrc  : string;
  LStrDst  : string;
  LCopyCnt : Integer;
begin
  Memo1.Lines.Clear;

  //テスト用の文字列
  LStrSrc := '東西南北テスト文字列';
  LStrDst := '123456789';

  //作成した文字列を確認表示
  Memo1.Lines.Add(LStrSrc);
  Memo1.Lines.Add(LStrDst);

  //コピー実行
  LCopyCnt := 2;
  MoveChars(LStrSrc[3], LStrDst[5], LCopyCnt);

  //コピーの結果を表示
  Memo1.Lines.Add('');
  Memo1.Lines.Add(LStrDst);
end;




13_文字列配列内等の文字列を検索する関数


文字列配列内の検索ではありませんが,CharInSet 関数は,文字列の中に,指定した「文字」があるかを調べることができます.
文字列の中に指定した「文字列」があるかを調べるには Pos 関数が使えますが,複数の文字列中から検索するには,MatchStr, MatchText, IndexStr, IndexText 等の関数が使えます.

IndexStr, IndexText 等の関数で指定した文字列があれば,その配列要素に別の文字列を代入すれば文字列の置換ができます.

Delphi XE3 で実装された TStringHelper にも,下の表のメソッドと同じような機能を持つメソッド類が実装されています.



ユニット名.関数名 機能 (文字セットまたは文字列配列要素の検索)
SysUtils.CharInSet 文字セットの中に指定した文字があるかをチェックする
対象は文字列ではなく,文字
StrUtils.MatchStr 文字列配列の中に,指定した文字列があるかを検索する
大文字と小文字を区別して検索
関数の戻り値は論理値
StrUtils.MatchText 文字列配列の中に,指定した文字列があるかを検索する
大文字と小文字を区別しないで検索
関数の戻り値は論理値
StrUtils.IndexStr 文字列配列の中に,指定した文字列があるかを検索する
大文字と小文字を区別して検索
関数の戻り値は文字列がある配列の要素番号.存在しない場合は -1
StrUtils.IndexText 文字列配列の中に,指定した文字列があるかを検索する
大文字と小文字を区別しないで検索
関数の戻り値は文字列がある配列の要素番号.存在しない場合は -1


[備考]
AnsiStrings.pas にも同じ関数類がありますが,それらは Ansi 文字列用です.主に Delphi 2009 よりも前のバージョンで使用するためのものです.