プロが教える店舗&オフィスのセキュリティ対策術

こんばんは。Perl超初心者です(プログラミングの初心者でもあります)。

フォルダ内にあるすべてのファイルに対して、正規表現を使った文字列置換をしたいのですが、うまくいきません。アドヴァイスをいただけないでしょうか。

具体的には、あるファルダの中に100個程のファイルがあって、その中の改行が3回連続している部分を、「改行+[SAMPLE]+改行」に置換をしたいと表います。

Windows XP Professional SP3 / ActivePerl 5.10 の環境で、以下のように記述したのですが、「Missing $ on loop variable at insert.pl line 5.」とエラーになってしまいます。どこで、間違ったのでしょうか(というか、まるでダメなスクリプトかもしれませんが…)。

use strict;
use warnings;

my @filename = glob "*.txt";
foreach open(FILE, "$filename(@filename)") {
my @content =<FILE>;
@content =~ s/\n{3}/\n[SAMPLE]\n/g;
print FILE @content;
close(FILE);
}

どなたか、ご教示いただけると非常助かります。
よろしくお願い致します。

A 回答 (6件)

寝る直前に書いたので寝ぼけてました。


こんな感じでどうでしょう。

わからないところがあれば遠慮なく質問してください>質問者様

use strict;
use warnings;

use feature ':5.10';

use strict;
use warnings;

undef $/;

foreach my $file (glob '*.txt') {
open my $rfh, '<', $file or next;
my $tmp = "$file" . "tmp";
open my $wfh, '>', $tmp or die;
my $content = <$rfh>;
$content =~ s/\n{3}/\n[SAMPLE]\n/g;
print $wfh $content;
close $rfh;
close $wfh;

#unlink "${file}.bak"
rename $file, "${file}.bak";
rename $tmp, $file;
}
    • good
    • 0

もう

http://okwave.jp/qa5526745.htmlに移ってこっちは放置かもしれませんが。

わたしが#2で提示したスクリプトでは、
undef $/;
という行があるのに注意してください。
これによって、「ファイルの丸呑み」ができるようになり
my $content = <$fh>;
で、$contentにファイルの内容が丸々入ります。
#通常は一行だけ

今回はs修飾子もm修飾子もつける必要がないので(つけても構いませんが)
$content =~ s/\n{3}/\n[SAMPLE]\n/g;
で望みの置換をやってくれるはずです。

もう一つの質問も基本は同じですね。
    • good
    • 0
この回答へのお礼

度々のご回答、誠にありがとうございます。

最初見たときはわからなかったのですが、undef $/; の部分でレコードセパレータを無効化していたのですね。

お陰で思ったとおりの処理をすることができました。
せっかくの機会なので、今後レコードセパレータを無効化するとはどういうことなのか、掘り下げて見たいと思います。

大変勉強になりました。

お礼日時:2009/12/16 22:14

勉強がてらやってみました。



use strict;
use warnings;

while ( my $file = glob '*.txt' ) {
open my $in, '<', $file or die;
my @contents = <$in>;
close $in;
my $content = join('', @contents);

open my $out, ">$file";
print $out map { s/\n{3}/\n\n[SAMPLE]\n/g; $_ } $content;
close $out;
}

FHをいきなりスカラに読み込むと最初の一行だけしか代入されず、mapは一行ごとに処理するらしく「連続した改行」が認識されず。
という事で、joinを使って配列からスカラに入れてみました。
    • good
    • 0
この回答へのお礼

なるほど!

そういうやり方もありますね。
正解にたどり着く道がひとつでないのがプログラミングの醍醐味、
と、生意気ならがら思ってしまいました。

ありがとうございました。
たいへん勉強になりました。

お礼日時:2009/12/16 22:18

あれ? '<' で open したファイルに書き込めるんでしたっけ>#2. '<+' とかなんとかにしないといけないような....

    • good
    • 0

とりあえずこんな感じかな。


手抜きですが。

use strict;
use warnings;

undef $/;

foreach my $file (glob '*.txt') {
open my $fh, '<', $file or next;
my $content = <$fh>;
$content =~ s/\n{3}/\n[SAMPLE]\n/g;
print $fh $content;
close $fh;
}

試してないので間違いが入ってたらごめんなさい。

この回答への補足

サンプルリストありがとうございます。

上記では、エラーになったので以下のようにしたら、とりあえず動きました。

しかし、このやり方だと結局1行ずつ処理しているので、「\n{3}」の部分がちゃんとヒットしてくれません(/mオプションで複数行の検索にしてもうまくいきません)。私が検索したいのは、次のような部分です。

SAMPLE_STRING_ID_001<改行>
<改行>
<改行>

つまりIDの下に改行のみの場合は、[SAMPLE]という文字列を入れたいのです。

この問題を解決する良い方法はないでしょうか。

以下、現状のリスト:

use strict;
use warnings;

my $dirname = '.';
opendir(DIR, $dirname) or die "$dirname: $!";
while (my $dir = readdir(DIR)) {
next unless (-f $dir);
next unless ($dir =~ /\.txt$/);
print $dir, "\n";
open(FILE, $dir) or die "$dir: $!";
my @file = <FILE>;
close(FILE);
foreach my $line (@file) {
$line =~ s/\n{3}/\n[SAMPLE]\n/gm;
}
open(NEWFILE, "> $dir") or die "$dir: $!";
print NEWFILE @file;
close(NEWFILE);
}
closedir(DIR);

補足日時:2009/12/16 14:32
    • good
    • 0

入門書の類などはお持ちでないのでしょうか?



foreach open(FILE, "$filename(@filename)") {
my @content =<FILE>;
@content =~ s/\n{3}/\n[SAMPLE]\n/g;

いくらなんでもこれはない。としかコメントのしようがありません。
なんで foreach のすぐ後ろに open が来ていたり、
=~ の左辺が配列変数だったりするんでしょうか。
#smart matchingでもこれはないよなあ。たしか。

もうひとつ、今回の例ではファイルから読み込んだ結果を配列に収めちゃだめですね。
    • good
    • 0
この回答へのお礼

入門書は持っています。週末に入門書片手に独学を始めて3週目です。

この質問を書いたときは、仕事中だったので手元に入門書がなく、オンラインの入門サイトなどを斜め読みしながらコソコソとやっていました。

#2のスクリプトはのちほど試させていただきます。

>いくらなんでもこれはない。としかコメントのしようがありません。

私も翻訳の仕事をしていますので、英語の質問を見るとそういう気持ちになることがあります。

しかも、英語の場合は間違いを指摘しても、受験英語のルールに固執し、間違いを認めなかったりします。

お礼日時:2009/12/16 08:06

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!