6

我正在开发一个程序,该程序需要用户输入两个文件名。不幸的是,如果用户不遵循指定的输入格式,程序很容易中断。我想编写代码来提高它对这些类型错误的弹性。当你看到我的代码时你就会明白:

# 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;

显然,如果用户输入了错误的文件名类型(.tab 文件而不是 .txt 或 .seq 而不是 .qseq),则此代码运行可能会出错。我想要可以进行某种检查的代码,以查看用户是否输入了适当的文件类型。

另一个可能破坏代码的错误是如果用户在文件名前输入了太多空格。例如: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};

shell 将处理任何无关的空白,试试看。您需要对文件名进行验证,我在示例中使用了正则表达式。使用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

在例程中处理文件名数据之前,您需要修剪空格,您可以使用另一个正则表达式检查文件扩展名,正如Perl 中是否有正则表达式以查找文件扩展名?. 如果它是对您而言重要的文件的实际类型,那么使用File::LibMagicType来检查它可能更有价值。

于 2012-06-09T01:47:53.607 回答
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 回答
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 回答