Вопрос по user-input, filehandle, regex, removing-whitespace, perl – Regex: Как удалить лишние пробелы между строками в Perl

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. Пользовательский ввод должен быть в этом формате, чтобы я мог правильно проиндексировать имена файлов в массив и вывести их позже.

Спасибо любая помощь или предложения, с благодарностью!

Я забыл упомянуть: это только один из шести сценариев, которые я должен изменить для запуска по конвейеру в командной строке. Другими словами, я хочу, чтобы конвейер работал так: Script00.pl | Script01.pl | Script02.pl | Script03.pl | Script04.pl | Script05.pl | Script06.pl. Это первый скрипт в трубе cooldood3490

Ваш Ответ

5   ответов
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'";
2

отделка пробелы перед обработкой данных имени файла в вашей подпрограмме, вы можете проверить расширение файла с помощью еще одного регулярного выражения, как хорошо описано вЕсть ли в Perl регулярное выражение для поиска расширения файла?, Если это фактический тип файла, который имеет значение для вас, то, возможно, стоит проверить это вместоFile :: LibMagicType.

@daxim спасибо за эти замечательные ссылки. спасибо за ответ Харальд cooldood3490
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 :: Использование для более навороченного способа вывода полезной документации для ваших пользователей, которые могут ошибочно вызвать вызов.

В CPAN есть десятки более продвинутых модулей Getopt.

Команды конвейера работают полностью на основе их вывода. По сути, выходные данные первой команды должны быть такими, какие вам нужны в качестве входных данных для следующей команды.
У меня недостаточно места, чтобы все объяснить. Имея годичный опыт, вы уже должны знать оheredocs а такжеregex, Вот ваши ключевые слова для поиска, продолжения и обновления ваших знаний:learn.perl.org perl-tutorial.org p3rl.org/retut & # X2013; Я не могу ответить на этот вопрос о цепи трубопровода, слишком мало деталей, лучше всегоopen a separate new question.
спасибо даксим! похоже, что использование параметров командной строки с Getopt :: Long - это путь. Кроме того, похоже, что вы даже предоставили проверку, чтобы убедиться, что имя файла правильное. спасибо, я бы сам не разобрался. Вы можете быстро объяснить, как работает каждая строка кода? Сalmost год опыта, я все еще относительно начинающий программист на Perl. Я вижу, что вы храните имена файлов в хеше% opt. Но можете ли вы объяснить, как работает бит регулярного выражения и как работает ИСПОЛЬЗОВАНИЕ и другие части? Я посмотрю на модуль Getopt :: Long. cooldood3490
Кроме того, думаете ли вы, что этот модуль будет работать для общего проекта, над которым я работаю? Видите ли, это только один из шести сценариев, которые мне нужно изменить для запуска по конвейеру в командной строке. Другими словами, я хочу, чтобы конвейер работал так: Script00.pl | Script01.pl | Script02.pl | Script03.pl | Script04.pl | Script05.pl | Script06.pl. любая последующая обратная связь с благодарностью cooldood3490
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

И переменные будут правильно заполнены без необходимости беспокоиться об обрезке пробелов.

спасибо Llion за пересмотр моего кода. Я мог бы использовать подпрограмму обрезки, которую вы предоставили. Это должно заботиться о любом ведущем или ведомом пустом пространстве. модуль GetOpt :: Long, который вы предложили, звучит как то, что мне нужно, однако, это всего лишь фрагмент всего проекта. Видите ли, это только один из шести сценариев, которые мне нужно изменить для запуска по конвейеру в командной строке. Другими словами, я хочу, чтобы конвейер работал так: Script00.pl | Script01.pl | Script02.pl | Script03.pl | Script04.pl | Script05.pl | Script06.pl. Я обязательно посмотрю, хорошо ли работает этот модуль для этого. еще раз спасибо cooldood3490
1

что ваш дизайн немного ненадежный, будет работать следующее?

my @fileNames = split(',', $filenames);
foreach my $fileName (@fileNames) {
  if($fileName =~ /\s/) {
    print STDERR "Invalid filename.";
    exit -1;
  }
}
my ($qsec, $barcode) = @fileNames;
да, я думаю, что-то подобное сделает пользователя быстро разочарованным. Я пытаюсь написать удобный для пользователя код. хорошее предложение, хотя. cooldood3490
Это действительно не отвечает на вопрос, хотя. Это просто ошибки, когда формат неожиданный. Что если в имени файла есть пробелы?

Похожие вопросы