我正在开发一个程序,它接受两个文件名的用户输入。不幸的是,如果用户不遵循指定的输入格式,程序很容易中断。我想编写代码来提高它对这些类型的错误的弹性。当你看到我的代码时,你会明白的:
# 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。用户输入必须采用这种格式,以便我可以将文件名正确地索引到一个数组中,然后将它们移出。
感谢任何帮助或建议,我们非常感谢!
答案 0 :(得分: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模块。
答案 1 :(得分: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
并且可以正确填充变量,而无需担心修剪空白区域。
答案 2 :(得分:2)
在处理例程中的文件名数据之前,您需要trim个空格,您可以使用另一个正则表达式检查文件扩展名,如Is there a regular expression in Perl to find a file's extension?中所述。如果它是对您来说重要的实际文件类型,那么使用File::LibMagicType来检查它可能更值得。
答案 3 :(得分:1)
虽然我认为您的设计有点不确定,但以下内容会有效吗?
my @fileNames = split(',', $filenames);
foreach my $fileName (@fileNames) {
if($fileName =~ /\s/) {
print STDERR "Invalid filename.";
exit -1;
}
}
my ($qsec, $barcode) = @fileNames;
答案 4 :(得分: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'";