2012-06-09 25 views
6

Tôi đang làm việc trên một chương trình lấy dữ liệu nhập của người dùng cho hai tên tệp. Thật không may, chương trình có thể dễ dàng phá vỡ nếu người dùng không tuân theo định dạng được chỉ định của đầu vào. Tôi muốn viết mã để cải thiện khả năng phục hồi của nó đối với các loại lỗi này. Bạn sẽ hiểu khi bạn nhìn thấy mã của tôi:Regex: Cách loại bỏ khoảng trống thừa giữa các chuỗi trong Perl

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

Rõ ràng mã này chạy có thể chạy vào lỗi nếu người dùng nhập các loại sai của tên tập tin (file và.tab thay vì .txt hoặc .seq thay vì .qseq) . Tôi muốn mã có thể thực hiện một số loại kiểm tra để xem người dùng có nhập loại tệp thích hợp không.

Một lỗi khác có thể phá vỡ mã là nếu người dùng nhập quá nhiều khoảng trắng trước tên tệp. Ví dụ: sample1.qseq, (tưởng tượng 6 dấu cách ở đây) barcode.txt (Chú ý nhiều dấu cách sau dấu phẩy)

Ví dụ khác: (tưởng tượng 6 dấu cách ở đây) sample1.qseq, barcode.txt (Lần này thông báo số lượng không gian trước tên tệp đầu tiên)

Tôi cũng muốn các dòng mã có thể xóa thêm khoảng trắng để chương trình không bị hỏng. Tôi nghĩ rằng đầu vào của người dùng phải ở định dạng sau: sample1.qseq, barcode.txt. Đầu vào của người dùng phải ở định dạng này để tôi có thể lập chỉ mục đúng tên tệp vào một mảng và chuyển chúng ra sau.

Cảm ơn bất kỳ trợ giúp hoặc đề xuất nào được đánh giá cao!

+0

tôi quên đề cập đến: Đây chỉ là một trong sáu kịch bản tôi phải thay đổi cho một đường ống chạy trong dòng lệnh. Nói cách khác, tôi muốn chạy đường ống để làm việc như: Script00.pl | Script01.pl | Script02.pl | Script03.pl | Script04.pl | Script05.pl | Script06.pl. Đây là kịch bản đầu tiên trong đường ống chạy – cooldood3490

Trả lời

8

Cách tiêu chuẩn để giải quyết loại sự cố này là sử dụng tùy chọn dòng lệnh, không thu thập dữ liệu nhập từ STDIN. Getopt::Long đi kèm với Perl và là servicable:

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

Vỏ sẽ đối phó với bất kỳ khoảng trắng không liên quan, hãy thử nó và xem. Bạn cần phải thực hiện xác nhận tên tập tin, tôi đã tạo ra một cái gì đó với regex trong ví dụ. Sử dụng Pod::Usage để biết cách tạo tài liệu hữu ích cho người dùng của bạn có khả năng nhận được yêu cầu sai.

Có hàng tá mô-đun Getopt nâng cao hơn trên CPAN.

+0

cảm ơn daxim! nó có vẻ như sử dụng các tùy chọn dòng lệnh với Getopt :: Long là con đường để đi. Ngoài ra, có vẻ như bạn thậm chí còn cung cấp một kiểm tra để thấy rằng tên tệp là chính xác. cảm ơn bạn, tôi sẽ không tự tìm ra được. Bạn có thể nhanh chóng giải thích cách mỗi dòng mã hoạt động không? Với * gần như * một năm kinh nghiệm, tôi vẫn là một lập trình viên Perl tương đối mới. Tôi thấy rằng bạn lưu trữ các tên tập tin trong một hash% opt. Nhưng bạn có thể giải thích làm thế nào bit regex hoạt động và USAGE và các bộ phận khác làm việc? Tôi sẽ xem xét mô-đun Getopt :: Long. – cooldood3490

+0

Ngoài ra, bạn có nghĩ rằng mô-đun này sẽ làm việc cho các loại dự án tổng thể tôi đang làm việc trên? Bạn thấy đấy, đây chỉ là một trong sáu kịch bản mà tôi phải sửa đổi cho một đường ống chạy trong dòng lệnh. Nói cách khác, tôi muốn chạy đường ống để làm việc như: Script00.pl | Script01.pl | Script02.pl | Script03.pl | Script04.pl | Script05.pl | Script06.pl. bất kỳ phản hồi tiếp theo nào được đánh giá rất cao – cooldood3490

+0

Lệnh đường ống hoạt động hoàn toàn dựa trên đầu ra của chúng. Về cơ bản, đầu ra của lệnh đầu tiên cần phải là thứ bạn cần làm đầu vào cho lệnh tiếp theo. – Ilion

2

Bạn cần phải trim dấu cách trước khi xử lý dữ liệu tên tệp theo thói quen của mình, bạn có thể kiểm tra phần mở rộng tệp bằng một biểu thức chính quy khác, như được mô tả rõ ràng trong Is there a regular expression in Perl to find a file's extension?. Nếu đó là loại tệp thực sự quan trọng đối với bạn, thì có thể đáng để kiểm tra điều đó thay vì bằng File::LibMagicType.

+0

@daxim cảm ơn những liên kết tuyệt vời này. cảm ơn cho câu trả lời Harald – cooldood3490

4

Trước tiên, hãy đặt use strict; ở đầu mã của bạn và khai báo biến của bạn.

Thứ hai, điều này:

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

là sẽ không làm những gì bạn muốn. split() lấy một chuỗi và biến nó thành một mảng. Tham gia có một danh sách các mục và trả về một chuỗi. Bạn chỉ muốn chia tách:

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

Điều đó sẽ tạo ra một mảng như bạn mong đợi.

Chức năng này sẽ an toàn cắt khoảng trắng từ đầu và kết thúc của một chuỗi:

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

Tiếp cận nó như thế này:

my $file = trim(shift @filenames); 

Tùy thuộc vào kịch bản của bạn, nó có thể được dễ dàng hơn để vượt qua các chuỗi như các đối số dòng lệnh. Bạn có thể truy cập chúng thông qua các mảng @ARGV nhưng tôi thích sử dụng 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, 
); 

Bạn có thể sau đó gọi này như:

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

Và các biến sẽ được áp dụng đúng cách mà không cần lo lắng về việc cắt không gian trắng.

+0

cảm ơn Llion vì đã sửa đổi mã của tôi. Tôi có thể sử dụng chương trình con cắt mà bạn đã cung cấp. Điều đó sẽ chăm sóc của bất kỳ không gian trắng hàng đầu hoặc đuôi. mô-đun GetOpt :: Long bạn đề xuất âm thanh giống như điều tôi cần tuy nhiên, đây chỉ là một đoạn của dự án tổng thể. Bạn thấy đấy, đây chỉ là một trong sáu kịch bản mà tôi phải sửa đổi cho một đường ống chạy trong dòng lệnh. Nói cách khác, tôi muốn chạy đường ống để làm việc như: Script00.pl | Script01.pl | Script02.pl | Script03.pl | Script04.pl | Script05.pl | Script06.pl. Tôi chắc chắn sẽ thấy nếu module này hoạt động tốt cho điều đó. Cảm ơn một lần nữa – cooldood3490

1

Trong khi tôi nghĩ thiết kế của bạn có chút ít mùi, những điều sau đây sẽ hoạt động?

my @fileNames = split(',', $filenames); 
foreach my $fileName (@fileNames) { 
    if($fileName =~ /\s/) { 
    print STDERR "Invalid filename."; 
    exit -1; 
    } 
} 
my ($qsec, $barcode) = @fileNames; 
+0

Điều đó không thực sự trả lời câu hỏi mặc dù. Nó chỉ lỗi khi định dạng không mong muốn. Điều gì sẽ xảy ra nếu có dấu cách trong tên tệp? – Ilion

+0

vâng tôi tưởng tượng một cái gì đó như thế này sẽ làm cho người dùng nhanh chóng thất vọng. Tôi đang cố viết mã thân thiện với người dùng. gợi ý tốt mặc dù. – cooldood3490

1

Và đây là một trong những cách nhiều hơn bạn có thể làm điều đó với regex (nếu bạn đang đọc đầu vào từ 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'";