|
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 同梱) |
- 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 以降でも使用できますが,同じ動作,機能を,正規表現で実行するプログラムを作成してみました.
正規表現には,小宮氏作の Delphi 用正規表現ユニット SkRegExp を使用しています. |
[備考]
小宮氏作の正規表現ユニットは,本ページの記事を掲載当初は SkRegEx と SkRegularExpressions に分かれていましたが,現在は 1 つのユニットにまとめられています.本ページのサンプルコードの uses 部は修正が必要です.
|
このページのサンプルは,主に,ある文字列で囲まれた文字列を扱います.そのための正規表現をテストしてみます.まず,正規表現ではデフォルトで大文字と小文字を区別して検索します.それを確認します. |
正規表現 |
説明 |
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
小文字で検索
|
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.
正規表現では,デフォルトで最長マッチという検索をします.これは,マッチする文字列を行末から検索します.<B> と </B> で囲まれた文字列の場合,</B> を行末から検索します.したがって,1 行に複数個の文字列の組み合わせがあると,期待した結果が得られません.
それを避けるには,最短マッチ検索を指定します.そうすると,<B> の後ろから検索してくれます.この指定は,検索文字数を指定するメタ文字の後に,? を付加します. |
|
図6
最長マッチで検索
- <B> と </B> で囲まれる最長の文字列とマッチ
|
|
図7
最短マッチで検索
- <B> と </B> で囲まれる最短の文字列とマッチ
|
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.
これまでは,指定文字列で囲まれた文字列は,囲み文字列も含めてマッチしました.この囲み文字例の囲まれた部分だけを取り出したい場合は,それをグループ化します.グループ化するには,該当部分の正規表現を ( ) で括ります.実行後,この ( ) 内の正規表現の結果だけを取り出すことができます.
これには,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
グループ化で,間の文字列の条件を設定 |
//=============================================================================
// テスト用の文字列を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;
ここからが,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.
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.
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.
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
実行画面
|
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 関数がありますが,これらの関数は全角の空白文字は削除しません.
|
PosEx 関数を使用すると,特定の文字,あるいは文字列で囲まれた文字列を取り出す操作が比較的簡単なコードで実装できます.
下に示すコードは,HTML のタグから width 属性の値を取得する例です. |
|
図17
特定の文字列で囲まれた文字列を取得する例 |
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
文字列の部分コピーと置換
|
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;
文字列配列内の検索ではありませんが,CharInSet 関数は,文字列の中に,指定した「文字」があるかを調べることができます.
文字列の中に指定した「文字列」があるかを調べるには Pos 関数が使えますが,複数の文字列中から検索するには,MatchStr, MatchText, IndexStr, IndexText 等の関数が使えます.
IndexStr, IndexText 等の関数で指定した文字列があれば,その配列要素に別の文字列を代入すれば文字列の置換ができます.
Delphi XE3 で実装された TStringHelper にも,下の表のメソッドと同じような機能を持つメソッド類が実装されています.
|
[備考]
AnsiStrings.pas にも同じ関数類がありますが,それらは Ansi 文字列用です.主に Delphi 2009 よりも前のバージョンで使用するためのものです. |
|