6

2 つのファイル名のユーザー入力を受け取るプログラムに取り組んでいます。残念ながら、ユーザーが指定された入力形式に従わない場合、プログラムは簡単に壊れてしまう可能性があります。この種のエラーに対する回復力を向上させるコードを書きたいと思っています。私のコードを見れば理解できるでしょう:

# Ask the user for the filename of the qseq file and barcode.txt file
print "Please enter the name of the qseq file and the barcode file separated by a comma:";
# user should enter filenames like this: sample1.qseq, barcode.txt

# remove the newline from the qseq filename
chomp ($filenames = <STDIN>);

# an empty array
my @filenames;

# remove the ',' and put the files into an array separated by spaces; indexes the files
push @filename, join(' ', split(',', $filenames))

# the qseq file
my $qseq_filename = shift @filenames;

# the barcode file.
my barcode = shift @filenames;

ユーザーが間違ったタイプのファイル名 (.txt の代わりに .tab ファイル、または .qseq の代わりに .seq) を入力すると、明らかに、このコードの実行でエラーが発生する可能性があります。ユーザーが適切なファイルの種類を入力したことを確認するために、何らかのチェックを実行できるコードが必要です。

コードを壊す可能性のある別のエラーは、ユーザーがファイル名の前にあまりにも多くのスペースを入力した場合です。例: sample1.qseq,(ここでは 6 つのスペースを想像してください)、barcode.txt (カンマの後に多数のスペースがあることに注意してください)

別の例: (ここでは 6 つのスペースを想像してください) sample1.qseq,barcode.txt (今回は、最初のファイル名の前のスペースの数に注意してください)

また、プログラムが壊れないように余分なスペースを削除できるコード行も必要です。ユーザー入力は次のような形式でなければならないと思います: sample1.qseq、barcode.txt。ファイル名を配列に適切にインデックス付けし、後でシフトアウトできるように、ユーザー入力はこの形式である必要があります。

助けや提案に感謝します。

4

5 に答える 5

8

この種の問題に対処する標準的な方法は、STDIN からの入力を収集するのではなく、コマンド ライン オプションを利用することです。Getopt::Longは Perl に付属しており、サービス可能です:

use strict; use warnings FATAL => 'all';
use Getopt::Long qw(GetOptions);
my %opt;
GetOptions(\%opt, 'qseq=s', 'barcode=s') or die;
die <<"USAGE" unless exists $opt{qseq} and $opt{qseq} =~ /^sample\d[.]qseq$/ and exists $opt{barcode} and $opt{barcode} =~ /^barcode.*\.txt$/;
Usage: $0 --qseq sample1.qseq --barcode barcode.txt
       $0 -q sample1.qseq -b barcode.txt
USAGE
printf "q==<%s> b==<%s>\n", $opt{qseq}, $opt{barcode};

シェルは余分な空白を処理します。試してみてください。ファイル名の検証を行う必要があります。この例では、正規表現を使用して何かを作成しました。Pod ::Usageを使用して、呼び出しを間違える可能性が高いユーザーに役立つドキュメントを出力する、より洗練された方法を使用してください。

CPAN には、より高度な Getopt モジュールが多数あります。

于 2012-06-09T02:06:16.677 に答える
4

まず、コードの先頭に置きuse strict;、変数を宣言します。

第二に、これ:

# remove the ',' and put the files into an array separated by spaces; indexes the files
push @filename, join(' ', split(',', $filenames))

あなたが望むことをするつもりはありません。split() は文字列を取り、それを配列に変換します。Join は項目のリストを受け取り、文字列を返します。分割したいだけです:

my @filenames = split(',', $filenames);

これにより、期待どおりの配列が作成されます。

この関数は、文字列の最初と最後から空白を安全に削除します:

sub trim {
    my $string = shift;
    $string =~ s/^\s+//;
    $string =~ s/\s+$//;
    return $string;
}

次のようにアクセスします。

my $file = trim(shift @filenames);

スクリプトによっては、文字列をコマンド ライン引数として渡す方が簡単な場合があります。@ARGV 配列を介してそれらにアクセスできますが、私は GetOpt::Long を使用することを好みます。

use strict;
use Getopt::Long;
Getopt::Long::Configure("bundling");

my ($qseq_filename, $barcode);

GetOptions (
    'q|qseq=s' => \$qseq_filename,
    'b|bar=s'  => \$barcode,
);

これを次のように呼び出すことができます。

./script.pl -q sample1.qseq -b barcode.txt

また、空白の削除について心配する必要なく、変数が適切に設定されます。

于 2012-06-09T01:48:22.863 に答える
2

ルーチンでファイル名データを処理する前にスペースを削除する必要があります。Is there a regular expression in Perl to find a file's extension? でうまく説明されているように、さらに別の正規表現でファイル拡張子を確認できます。. それがあなたにとって重要なファイルの実際のタイプである場合は、代わりにFile::LibMagicTypeを使用してそれを確認する方が価値があるかもしれません。

于 2012-06-09T01:47:53.607 に答える
1

そして、正規表現でそれを行うもう1つの方法があります( から入力を読み取っている場合STDIN):

# read a line from STDIN
my $filenames = <STDIN>;

# parse the line with a regex or die with an error message
my ($qseq_filename, $barcode) = $filenames =~ /^\s*(\S.*?)\s*,\s*(\S.*?)\s*$/
    or die "invalid input '$filenames'";
于 2012-06-09T02:13:55.587 に答える
1

あなたのデザインは少し不安定だと思いますが、次のように動作しますか?

my @fileNames = split(',', $filenames);
foreach my $fileName (@fileNames) {
  if($fileName =~ /\s/) {
    print STDERR "Invalid filename.";
    exit -1;
  }
}
my ($qsec, $barcode) = @fileNames;
于 2012-06-09T01:49:54.897 に答える