16番会議室「玉石混淆みんなで作るSample蔵」に寄せられたサンプル
"ファイル内文字列の検索・置き換え"
この発言に対し以下のコメントが寄せられています
#00416 本田勝彦 さん RE:ファイル内文字列の検索・置き換え
#00417 本田勝彦 さん RE:ファイル内文字列の検索・置き換え
#00425 本田勝彦 さん RE:ファイル内文字列の検索・置き換え
ファイル内の文字列を検索・置き換えして、指定ファイルへ出力します。
検索対象文字列に、改行文字・半角全角スペースが含まれていてもヒット
します。(^^)v
例えば・・・
「配布するためには,InstallShield Express などのボーランドが
保証するインストールプログラムを使うことをお勧めします。」
の文字列から、「ボーランドが保証する」をヒットさせることが出来ます。
使用例
InputFileName := 'a:\nifterm\fdelphi\room6.txt';
OutputFile := 'a:\nifterm\fdelphi\work.txt';
SearchText := '謎の全知師';
ReplaceText := '謎のぜんちゃん';
ReplaceFile(InputFileName, OutputFileName, SearchText, ReplaceText);
コメントだらけですがご容赦(^^;)
---- キリトリ ------------------------------------------------------------
{ Boy -> Girl へ置き換えるロジック
読み込み用、書き込みようのファイルストリームとバッファを用意する
ポインタを、バッファの先頭へセットする
ポインタ位置へデータを読み込む
バッファのイメージ(例)
0 1 2 3 4 5 6 7 8 9 10 11 12 13
. . . B o CR LF y . B o CR LF #0
0: 検索開始ポインタ
3: ヒットポイント ( => Start )
9: ヒットするかもしれないデータの先頭
検索開始
Boy を検索する(改行文字・半角全角スペースを含んでいても
ヒットの対象とする)
ヒットした場合
検索情報レコードのメンバが、Start = 3, Length = 5 となるので
・ポインタ位置から、Start の前まで書き込む
・置き換え文字列 ( Girl ) を書き込む
・ヒットした文字列に改行文字列が含まれていたら改行を書き込む
・ポインタを Start + Length 分進めて検索ループを回す
ヒットしなかった場合
・ポインタから、Start の手前まで書き込み
・ヒットする可能性のある場合( IsPossible = True )は
上記例の場合、9..12 の部分をバッファの先頭へ移動
・ポインタをバッファの先頭に移動する
・バッファ内でのデータの移動量だけポインタを移動してから、
読み込みループを回す }
// 検索情報取得用レコード
type
TSearchInfo = record
Start, // ヒットポイント
Length: Integer; // 改行・スペースを含むヒットした文字列長
IsPossible: Boolean; // ヒットするかもしれないデータフラグ
end;
// 検索
function Search(const Pattern: String; // 検索文字列
P: PChar; // 検索対象文字列
var Info: TSearchInfo): Boolean; // 検索情報レコード
const
CRLF: set of Char = [#$0D, #$0A, #$20]; // 改行コードと半角スペース
var
L, // 検索文字列の長さ
SC, // 判定文字数
C, // 改行文字数
I: Integer; // ヒットカウンタ
begin
Result := False;
L := Length(Pattern);
SC := StrLen(P);
Info.Start := 0; // ヒットポイントの初期化
while SC > 0 do
begin
I := 0;
C := 0;
Info.Length := 0; // 改行を含むヒットした文字列長
Info.IsPossible := False; // ヒットするかもしれないデータフラグ
// Pattern と P の1バイト同士が同じか、ヒットするかもしれない
// 状態で #0・改行文字と半角スペース・全角スペース の場合は
// ループさせる
while (Pattern[I + 1] = P[Info.Start + I + C]) or (
Info.IsPossible and (
(P[Info.Start + I + C] = #0) or
(P[Info.Start + I + C] in CRLF) or (
(P[Info.Start + I + C] = #$81) and
(P[Info.Start + I + C + 1] = #$40)))) do
begin
// #0 の場合は、Info の状態を保持したまま終了
if Info.IsPossible and (P[Info.Start + I + C] = #0) then
Exit;
// 改行文字・半角スペースの場合は、
// CR, LF カウンターをインクリメントしてループ
if Info.IsPossible and (P[Info.Start + I + C] in CRLF) then
begin
Inc(C); // ++ CR, LFカウンター
Inc(Info.Length); // ++ ヒット文字列長
Continue;
end;
// 全角スペースの場合も同じ
if Info.IsPossible and
(P[Info.Start + I + C] = #$81) and
(P[Info.Start + I + C + 1] = #$40) then
begin
Inc(C, 2);
Inc(Info.Length, 2);
Continue;
end;
// ヒットするかもしれないデータかどうかの判別
// 1バイト文字か、同一の2バイト文字同士の場合は真
if (I = 0) and (
not IsDBCSLeadByte(Byte(Pattern[I + 1])) or (
IsDBCSLeadByte(Byte(Pattern[I + 1])) and
IsDBCSLeadByte(Byte(P[Info.Start + I + C])) and
(Pattern[I + 2] = P[Info.Start + I + C + 1]))) then
Info.IsPossible := True;
// ヒットの判別
Inc(I); // ++ ヒットカウンタ
Inc(Info.Length); // ++ ヒット文字列長
if I >= L then // ヒットカウンタが検索文字列長以上ならヒット
begin
Result := True;
Exit;
end;
end;
Inc(Info.Start); // ヒットポインタの移動
Dec(SC);
end;
end;
// 関数本体
function ReplaceFile(const InputFile, // 読み込むファイル名
OutputFile, // 書き込むファイル名
TargetText, // 検索文字列
ReplaceText // 置き換え文字列
: String): Boolean;
const
BufferSize = $2000; // 8192
var
InputStream, // 読み込み用ファイルストリーム
OutputStream: TFileStream; // 書き込み用ファイルストリーム
Buffer, // 検索処理用のバッファ
P: PChar; // バッファへのポインタ
Info: TSearchInfo; // 検索情報取得用レコード
T, // 検索文字列の長さ
R: Integer; // 置き換え文字列の長さ
Count: Longint; // バッファに読み込まれたバイト数
I: Integer;
begin
if (InputFile = '') or (OutputFile = '') or
(TargetText = '') or (TargetText = ' ') or
(TargetText = ' ') or (ReplaceText = '') then Exit;
T := Length(TargetText);
R := Length(ReplaceText);
Buffer := StrAlloc(BufferSize + 1);
InputStream := TFileStream.Create(InputFile, fmOpenRead);
OutputStream := TFileStream.Create(OutputFile, fmCreate);
try
P := Buffer;
Info.Length := 0;
while True do
begin
// 読み込みサイズは、バッファ内でのデータ移動量分減らす
Count := InputStream.Read(P^, BufferSize - Info.Length);
if Count = 0 then
begin
// バッファ内にデータがある場合は吐き出す
if Buffer <> P then
OutputStream.Write(Buffer^, P - Buffer);
Break;
end
else
begin
// ポインタをバッファの先頭に移動
P := Buffer;
// ヌルターミネータセット
Buffer[Info.Length + Count] := #0;
// 検索ループ
while Search(TargetText, P, Info) do
begin
// ヒットポイント手前まで書き込み
OutputStream.Write(P^, Info.Start);
// 置き換え文字列書き込み
OutputStream.Write(ReplaceText[1], R);
// ヒットした文字列が改行文字を含んでいたら CR, LF 書き込み
if Info.Length > T then
for I := Info.Start to Info.Start + Info.Length do
if P[I] in [#$0D, #$0A] then
begin
OutputStream.Write(#$0D#$0A, 2);
Break;
end;
// ポインタを移動してループ
Inc(P, Info.Start + Info.Length);
end;
// ヒットするデータがバッファに無くなったら
// Info.Start 手前まで書き込み
OutputStream.Write(P^, Info.Start);
// ヒットするかもしれないデータがある場合
if Info.IsPossible then
begin
// ヒットするかもしれない文字列までポインタを移動
Inc(P, Info.Start);
Move(P[0], Buffer[0], Info.Length); // 文字列のバッファ内移動
end;
// ポインタを先頭へ移動
P := Buffer;
// 文字列のバッファ内移動分、ポインタを移動
Inc(P, Info.Length);
end;
end;
Result := True;
finally
InputStream.Free;
OutputStream.Free;
StrDispose(Buffer);
end;
end;
---- キリトリ ------------------------------------------------------------
本田勝彦
Original document by 本田勝彦 氏 ID:(VYR01647)
ここにあるドキュメントは NIFTY SERVEの Delphi Users' Forum の16番会議室「玉石混淆みんなで作るSample蔵」に投稿されたサンプルです。これらのサンプルはボーランド株式会社がサポートする公式のものではありません。また、必ずしも動作が検証されているものではありません。これらのサンプルを使用したことに起因するいかなる損害も投稿者、およびフォーラムスタッフはその責めを負いません。使用者のリスクの範疇でご使用下さい。
Copyright 1996-2002 Delphi Users' Forum
|