- ベストアンサー
Perl: パターンマッチを使ったifの条件が必ずTRUEになってしまう
- Perlでパターンマッチを使ったifの条件が必ずTRUEになってしまう問題について質問します。
- 質問の背景として、同じ条件でもif文が正しく働かず、必ず条件がTRUEと判定されてしまうことを指摘しています。
- 具体的には、二つのファイルがあり、特定の列の文字列が一致した場合にその行の1列目を出力するプログラムで、パターンマッチを使ったif文が正しく機能しない問題が発生しています。
- みんなの回答 (3)
- 専門家の回答
質問者が選んだベストアンサー
あるべき動作仕様やデータフォーマットの詳細、動作環境などがわかりませんので、 スクリプトを動作させた場合に考えられる問題を列挙します。 ● 各データファイルのフォーマットはタブ区切りになっていますか? 仕様として言及されていませんが、split /\t/ で処理している以上は ファイルの列がタブ区切りになっていないとデータ列を正しく読み取れません。 その結果、$fact[2]と$oct[1]がいずれも未定義値になっている場合には if($fact[2] =~ /$oct[1]/) { ... } は常に真と判定されます。 $fact[2]と$oct[1]に正しい値が入ることを保証するために、 列数が異なる場合にはエラーとなるような例外処理が必要かもしれません。 @fact = split /\t/, $line; if(@fact != 2) { die("Bad csv format at RNA.csv, line $.\n"); } @oct = split /\t/, $octline; if(@oct != 3) { die("Bad csv format at oct_gene.csv, line $.\n"); } なお上記のケースでは、Perl5ではuse warningsプラグマを有効にしていれば 実行時に未定義値に関する警告を出してくれます。 use strictやmyによる変数宣言と合わせてチェック機能を有効活用することを 強くお勧めします。 ●各データファイルの改行コードは実行環境の改行コードと一致していますか? Perlを実行するプラットフォームOSとデータファイルの改行コードが 一致していない場合にもデータ読み込み時の破損が起きる可能性があります。 例として、UNIX/Linux系プラットフォーム(改行コード=LF)のPerlにおいて Windows環境で作られたテキストファイル(改行コード=CR+LF)を読ませた場合、 chompでは末尾のLFのみが削られ、CRが残ってしまってデータが化けることになります。 この場合、Perl5.8以降であれば、3引数のopen()やbinmode()を使って open(FILE, ":crlf", "RNA.csv"); または open(OCT, "oct_gene.csv"); binmode(OCT, ":crlf"); とすることで入力時にテキスト中のCR+LFの改行をLFに統一することができます。 Mac(改行コード=CR)で実行する場合も同様の注意が必要になる場合があります。 Windowsでも、Cygwin環境で実行した場合はUNIX系と同じ挙動になるはずです。 バッドノウハウとして、chompを複数回実行するという裏技?もありますが、 場当たり的対処方法なのでお勧めしません。入力フォーマットの方を厳格にすべきです。 ● 文字列の比較方法は意図したものになっていますか? 「alphabetの群は空白で区切られています」とあるにもかかわらず、 空白を一切考慮しないロジックになっているのが気になります。 上記のサンプルでは、「Nanog homeobox」に対しては「Nano」「g h」「home」「box」なども マッチすることになります。 これが意図する仕様かどうか確認した上で、もし必要であれば比較元と 対象文字列のそれぞれについて、空白でsplitしてから個別比較する処理を追加してください。 また、/$oct[1]/とした場合、$oct[1]は一般文字列ではなく正規表現として解釈されます。 単純に文字列を含んでいるかをチェックしたいのなら、\Q~\Eを使って if($fact[2] =~ /\Q$oct[1]\E/) { とするか、あるいは正規表現を使わずに if(index($fact[2], $oct[1]) >= 0) { # 文字列を含まない場合は-1が返る でチェックしなければいけません。 でないと$oct[1]に正規表現のメタ文字が含まれている場合に意図しない結果になります。 $oct[1] = 'a.c' のときに 'abc' にもマッチしてしまうということです。 ● 比較対象およびファイル出力する内容の、列の選択は正しいですか? 配列の添え字は1ではなく0から始まることに注意してください。 print WRITE $fact[2] . "\t" . $oct[1] ."\n"; で出力されるのは、 「RNA.csvの3列目とoct_gene.csvの2列目をタブ記号で連結したもの」 です。提示仕様にある「一致した行の1列目を出力する」とは異なるように 読み取れます。1行目ならわかるんですが・・。
その他の回答 (2)
- Tacosan
- ベストアンサー率23% (3656/15482)
「whileで繰り返すまでもなく必ず if($fact[2] =~ /$oct[1]/) が成り立ってしまいます」って~ときの, 各変数の値はどうなっているんですか?
お礼
回答ありがとうございます。上にも書きましたが、パターンマッチの片方に未定義値が格納されているのが原因でした。
- _--_1l1_1_
- ベストアンサー率67% (102/152)
> 『oct_gene.csv』は以下のような2列のファイルで、2列目であるalphabetの群は空白で区切られています。文字コードはASCIIです。 > 『RNA.csv』は以下のような3列のファイルで、3列目のalphabet群は同じく空白で区切られています。 とありますが、 @fact = split /\t/, $line; @oct = split /\t/, $octline; これではタブで区切ってます。 @fact = split / /, $line; @oct = split / /, $octline; とかじゃないですかね。
お礼
回答ありがとうございます。 私が意図していたのは空白を含む列は一つとして、タブ区切りで分けるということです。
お礼
回答ありがとうございます。問題は解決しました。入力の行列が完璧でなくところどころに項目が存在していなかったのでパターンマッチするところで未定義値が設定されていたのが原因でした。 また、色々な可能性に対する提案がありとても役に立ちました。今回のこと以外でも参考になりそうな部分が多いので非常に有り難いです。