Windows-XP上でActivePerl/5.8.8を利用しています。
テキストファイルなどで、ある文字列が現れた行から、ある文字列が現れるまで、
ということを判定させるときに、範囲演算子が使えるということを知りました。
while(<>){
chomp;
if(/^START$/ .. /^END$/){ # 範囲指定
・・・
STARTの行から、ENDの行までこのブロックに入る
}
}
1ファイルに対してだけ処理させるときは上手く行くのですが、
連続して複数のファイルを処理させようとすると、
2番目のファイルからは、開始条件(/^START$/)が既に成立したと
判断されてしまうようで、該当行が現れていないのに、ifブロックに
入ってしまいます。
foreach(@ARGV){# 複数ファイルに対して処理させる
open(FH,$_) || die;
while(<FH>){
chomp;
if(/^START$/ .. /^END$/){ # 範囲指定
・・・
1つ目のファイルではSTARTの行から、ENDの行まででこのブロックに入るが、
2つ目のファイルではSTARTの行が現れないうちからこのブロックに入ってしまう。
}
}
close(FH);
}
これを2つ目のファイル以降も、範囲指定の開始条件が成立していない
状態から処理させるためには、どのようにすれば良いでしょうか。
よろしくお願い致します。
No.8ベストアンサー
- 回答日時:
No.6 です。
最後の行が END の場合を見落としていました。if (/^2009/ or eof()) { $end = '.*'; last; }の行を次のように変更します。
if (/^2009/ or (eof() and $_ !~ /^END$/)) { $end = '.*'; last; }
> それだと
> if (/^START$/ ... /^$end$/) {
> のように点は 3つにしないと不都合ではないでしょうか>#6.
if (/^START$/ .. /^$end$/) {
if ($end eq '.*') { $end = 'END'; redo; }
...
if (/^2009/ or (eof() and $_ !~ /^END$/)) { $end = '.*'; last; }
}
上のコードは、前のファイルで範囲演算子が真の状態で終了した場合に、次のファイルの1行目で
範囲演算子をクリアして $end を元に戻す仕組みです。たとえ次のファイルの1行目が START で
あっても範囲演算子が真の間は左オペランドは評価されないので問題がないと思いますがどうでしょうか。
この回答への補足
3つのデータファイルを以下のように用意したとして、各種の動作結果をご報告いたします。
tmp1.txt -----------------------
aaa
START
bbb
ccc
END
ddd
2009
eee
--------------------------------
tmp2.txt -----------------------
xxx
START
yyy
2009
zzz
END
vvv
--------------------------------
tmp3.txt -----------------------
111
START
222
333
END
444
555
--------------------------------
すみません、条件データが良くなかったので、次のように訂正します。
-tmp1.txt
aaa
START
bbb
ccc
-tmp2.txt
xxx
START
yyy
2009
zzz
END
vvv
-tmp3.txt
2009
START
2008
2007
END
2006
2005
2つの動作検証結果でご報告します。
■1■ プログラム1つ目
foreach my $filename (@ARGV) {
open my $FH, '<', $filename or die "$!:$filename";
while (<$FH>) {
chomp;
if( /^START$/ .. /^END$/ ) {
print "match $filename: $_\n" unless(/^START$/ || /^END$/);
last if(/^2009/);
}
}
close $FH or die "$!:$filename";
}
■■1の実行結果
c:\>hanni.pl tmp1.txt tmp2.txt tmp3.txt
match tmp1.txt: bbb ⇒ OK
match tmp1.txt: ccc ⇒ OK
match tmp2.txt: xxx ← NG:STARTのタグの前にある行
match tmp2.txt: yyy ⇒ OK(結果的に)
match tmp2.txt: 2009 ⇒ OK & 途中中断条件・・・
match tmp3.txt: 2009 ← NG:STARTの前にある条件なのに終了してしまった。
■2■ プログラム2つ目(kumozさんのを少しだけ書き換えました)
my $end = 'END';
foreach my $filename (@ARGV) {
open my $FH, '<', $filename or die "$!:$filename";
L2: while (<$FH>) {
chomp;
if( /^START$/ .. /^$end$/ ) {
if ($end eq '.*'){
$end = 'END';
redo L2;
}
print "match $filename: $_\n" unless(/^START$/ || /^$end$/);
if(/^2009/ || eof){
$end = '.*';
last L2;
}
}
if(eof){
$end = '.*';
last L2;
}
}
close $FH or die "$!:$filename";
}
■■2の実行結果
c:\>hanni2.pl tmp1.txt tmp2.txt tmp3.txt
match tmp1.txt: bbb
match tmp1.txt: ccc
match tmp2.txt: yyy
match tmp2.txt: 2009
match tmp3.txt: 2008
match tmp3.txt: 2007
これが今回、期待した結果で、バッチリでした。ありがとうございました。
お礼が遅くなり、大変申し訳ありませんでした。
No.7
- 回答日時:
それだと
if (/^START$/ ... /^$end$/) {
のように点は 3つにしないと不都合ではないでしょうか>#6. 「..」のままだと左オペランドにマッチしたときに右オペランドもチェックしてしまい, .* では必ずマッチしてしまうのでこの if の中には入らないと思います.
あるいは「何にもマッチしない正規表現」(?!) を使うか.
しばらく「点が3つ」の仕様が分かりませんでした。
左オペランドが真となった後、次の行に移ってから右オペランドの評価を行う。
ということなのですね。
今回の例の場合は、間違いなく「点が3つ」の方が意図に合っていました。
ありがとうございました。
(お礼が遅くなり、すみませんでした)
No.6
- 回答日時:
>(1)あるファイルには「END」が書かれていない場合がある。
>(2)START~ENDの間に別のwhileループ脱出条件があって、END判定が成立しないまま次のファイルへ移ってしまう。
範囲演算子の右オペランドを変数にして、while ループの中で操作してはどうでしょうか。
$end = 'END';
foreach (@ARGV) {
open(FH, $_) || die;
while (<FH>) {
chomp;
if (/^START$/ .. /^$end$/) {
if ($end eq '.*') { $end = 'END'; redo; }
...
if (/^2009/ or eof()) { $end = '.*'; last; }
}
}
}
No.5
- 回答日時:
おっと, ファイルハンドル ARGV をオープンし忘れた.
while (@ARGV) {
$ARGV = shift @ARGV;
open ARGV, '<', $ARGV;
my $cond = eval 'sub { /^START$/ .. /^END$/; }';
while (<ARGV>) {
if (&$cond) {
なんかする
last if なんか
}
}
No.4
- 回答日時:
ざっと調べた感じでは, 残念ながら単純にはできなさそうです.
ただし, さすがに Perl なので黒魔術を使えば何とかなるかもしれません. 例えば
while (@ARGV) {
$ARGV = shift @ARGV;
my $cond = eval 'sub { /^START$/ .. /^END$/; }';
while (<ARGV>) {
if (&$cond) {
なんかする
last if なんか
}
}
で回避できているかも.
No.3
- 回答日時:
上手くいくようですが。
もしかして質問の理解がまちがってます?#!/usr/bin/perl
use strict;
use warnings;
for (@ARGV) {
open my $fh, '<', $_ or die "$!:$_";
while (<$fh>) {
chomp;
if ( /^START$/ .. /^END$/ ) {
print "match: ";
}
else {
print "not match: ";
}
print $_, "\n";
}
close $fh or die "$!:$_";
}
$ cat tmp2.txt
a
b
START
c
d
e
END
f
g
$ cat tmp3.txt
1
2
START
3
4
5
END
6
7
$ perl foo.pl tmp2.txt tmp3.txt
not match: a
not match: b
match: START
match: c
match: d
match: e
match: END
not match: f
not match: g
not match: 1
not match: 2
match: START
match: 3
match: 4
match: 5
match: END
not match: 6
not match: 7
実際に動作をご確認頂いて恐縮です。ありがとうございます。
質問が分かりづらくて申し訳ありません。
#1,#2の方へのコメントとして書かせて頂きましたが、
(1)あるファイルには「END」が書かれていない場合がある。
(2)START~ENDの間に別のwhileループ脱出条件があって、END判定が成立しないまま次のファイルへ移ってしまう。
の2つのパターンに対応したいと思っています。
No.2
- 回答日時:
あとついでですが, 実は
while (<>) {
chomp;
if(/^START$/ .. /^END$/ || eof){
なんかする
}
}
でいいのかもしれない.
この回答への補足
何度もご教示ありがとうございます。
はい。それで、この
if(/^START$/ .. /^END$/ || eof){
・・・・
}
ブロックの中で、ある別の条件が成立したために、
lastで、whileを抜けさせた場合・・・例えば、
while (<>) {
chomp;
if(/^START$/ .. /^END$/ || eof){
なんかする。
last if(/^2009/);
}
}
のようにしていた場合に、次のファイルの処理に入ると問題の状況になるようです。
lastでwhileを抜けた後、
/^START$/ .. /^END$/ の判定をやめなさい。
という命令が出来ればいいのですが、いかがでしょうか。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- Visual Basic(VBA) VBAのユーザーフォームのテキストボックスに入力制限をしたい 6 2022/11/15 08:28
- Perl Perlで特定文字列から特定文字列までを抜き出したい 4 2022/04/02 14:24
- C言語・C++・C# プログラミングの授業の課題です 1 2023/01/17 22:15
- Visual Basic(VBA) 【Excel VBA】自動メール送信の機能追加 5 2022/09/29 12:53
- Visual Basic(VBA) 3つのプロシージャをまとめたら実行時エラー発生で対応不能 6 2022/05/17 01:47
- Excel(エクセル) Excel VBAどこが間違ってますか? 4 2023/07/17 10:04
- Visual Basic(VBA) Excelのマクロコードについて教えてください。 1 2022/03/27 13:25
- Visual Basic(VBA) 【VBA】印刷マクロのループ処理が反映されません 3 2022/08/09 02:15
- Visual Basic(VBA) ファイル全てを .xlsm に変更したところ、プログラムが途中で落ちてしまっています 17 2022/12/07 12:03
- Visual Basic(VBA) ExcelのVBAコードについて教えてください。 2 2023/05/23 16:28
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
ExcelをCSV書き出す場合のシー...
-
Perlの変数に文字数制限(容量...
-
テキストデータから指定した1...
-
ReadLineでの読み出し行を指定する
-
openした後、closeしないでプロ...
-
VBAでCSVファイルの特定行を書...
-
VBAでCSVファイルを途中行まで...
-
perlで、後ろの行を読んで、前...
-
JavaでCSVファイルを高速に読む...
-
VB6.0でDB接続する際に切断時の...
-
Excelマクロでフォルダ内のCSV...
-
VBAで巨大なファイルの途中から...
-
batファイルでrenameができませ...
-
エラー 'dir.h' : No such fi...
-
HTMLのフォームで画像と文...
-
awkスクリプトでダブルクォーテ...
-
readdir()で得られるファイル・...
-
ファイル全てを .xlsm に変更し...
-
while(<ハンドラ>) {} で行数を...
-
パスから最後のディレクトリだ...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
VBAでCSVファイルの特定行を書...
-
ExcelをCSV書き出す場合のシー...
-
openした後、closeしないでプロ...
-
VBAでCSVファイルを途中行まで...
-
VBAで巨大なファイルの途中から...
-
ReadLineでの読み出し行を指定する
-
JavaでCSVファイルを高速に読む...
-
MATLAB グローバル変数の宣言
-
エクセルVBA コードが同じでも...
-
Perlの変数に文字数制限(容量...
-
perlで、後ろの行を読んで、前...
-
C言語でのファイルのデータ更...
-
2つのCSVファイルをマッチング
-
VB6.0でDB接続する際に切断時の...
-
CSVが可変長の場合の検索方法
-
perlで容量の大きいCSVファイル...
-
1ファイルずつ読み込みたい
-
VBScriptでファイル保存先のデ...
-
perl で googleAPIを呼び出す...
-
C#でCSVファイルを逐一更新したい
おすすめ情報