perl - вырезание множества строк с заданным массивом чисел

дорогие мои коллеги-мастера perl в мире~!

Мне требуется ваша помощь.

У меня есть строковый файл A и числовой файл B, например:

Файл А:

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE

...и так далее до 200.

Файл Б:

3, 6, 2, 5, 6, 1, ... 2 

(всего 200 номеров в массиве)

затем, с числами в файле B, я хотел бы сократить каждую строку из начальной позиции до количества символов в файле B.

Например. поскольку файл B начинается с 3, 6, 2...

Файл А будет

AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBBBBBBBBBBBB
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC

нравится.

Так. это мой код до сих пор...

use strict;

if (@ARGV != 2) {
    print "Invalid usage\n";
    print "Usahe: perl program.pl [num_list] [string_file]\n";
    exit(0);
}

my $numbers=$ARGV[0];
my $strings=$ARGV[1];
my $i;

open(LIST,$number);
open(DATA,$strings);

my @list = <LIST>;
my $list_size = scalar @sp_list;


for ($i=0;$i<=$list_size;$i++) {
    print $i,"\n";
    #while (my $line = <DATA>) {    
    }   


close(LIST);
close(DATA);

Поскольку строк и чисел 200, я изменил массив на скалярное значение, чтобы работать с каждым числом каждой строки.

Я работаю над этим. и я знаю, что полагаю использовать функцию pos, но я не знаю, как сопоставить каждое число с каждой строкой. сначала читает строку по времени? или использовать, чтобы узнать, сколько времени мне нужно запустить это, чтобы достичь результата?

Ваша помощь будет высоко оценена!

Спасибо.

Я тоже буду над этим работать. Нужен ваш отзыв.


person Karyo    schedule 15.02.2013    source источник


Ответы (5)


Хорошо, что вы используете strict, и вы также должны использовать warnings. Что еще нужно отметить:

Вы должны проверить возвращаемое значение open, чтобы убедиться, что они не завершились ошибкой. Вы также должны использовать форму open с тремя аргументами и использовать лексический дескриптор файла. Особенно при обработке аргументов командной строки, что представляет угрозу безопасности.

open my $listfh, "<", $file or die $!;

Вы можете использовать меры предосторожности

use ARGV::readonly;

Вы можете легко составить список чисел с помощью оператора map. Предполагая, что числа находятся в списке, разделенном запятыми:

my @list = map split(/\s*,\s*/), <$listfh>;

Это разделит строку (строки) ввода на запятую и удалит лишние пробелы.

При чтении вашего входного файла вам не нужно использовать переменную-счетчик. Вы можете просто сделать

open my $inputfh, "<", $file or die $!;
while (<$inputfh>) {
    my $length = shift @list;   # these are your numbers
    chomp;                      # remove newline 
    my $string = substr($_, 0, -$length);  # negative length on substr
    print "$string\n";
}

Отрицательная длина substr приводит к тому, что в конце строки остается такое количество символов.

Вот однострочник в действии, демонстрирующий эти принципы:

perl -lwe '$f = pop;                            # save file name for later
           @nums = map split(/\s*,\s*/), <>;    # process first file
           push @ARGV, $f;                      # put back file name
           while (<>) { 
                my $len = shift @nums; 
                chomp; 
                print substr($_,0,-$len); 
           }' fileb.txt filea.txt

Вывод:

AAAAAAAAAAAAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBBBBBBBBBBBB
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
DDDDDDDDDDDDDDDDDDDDDDDDDDD
EEEEEEEEEEEEEEEEEEEEEEEEEE

Обратите внимание на использование неявного открытия аргументов имени файла путем манипулирования @ARGV. Также обработка новых строк с переключателем -l.

person TLP    schedule 15.02.2013

Вот мое предложение. Он делает use autodie, чтобы не было необходимости явно проверять статус вызовов open, и временно отменяет определение $/ — разделителя входных записей — так что все из num_list файла читаются за один раз. Вам не ясно, будет ли этот файл всегда содержать только одну строку, и в этом случае вы можете опустить local $/.

Числа извлекаются из текста с помощью регулярного выражения /\d+/g, которое возвращает все строки цифр во входных данных в виде списка.

Второй параметр substr – это начальная позиция нужной подстроки. число считается с конца строки, а не с начала. Третий параметр — это количество символов в подстроке, а четвертый — это строка для замены этой подстроки в целевой переменной. Таким образом, substr $data, -$n, $n, '' заменяет подстроку длиной $n, начиная с $n символов с конца, на пустую строку, т.е. удаляет ее.

Обратите внимание: если вы намерены удалить заданное количество символов из начала строки, вместо этого вы должны написать substr $data, 0, $n, ''.

use strict;
use warnings;
use autodie;

unless (@ARGV == 2) {
  print "Usage: perl program.pl [num_list] [string_file]\n";
  exit;
}

my @numbers;
{
  open my $listfh, '<', $ARGV[0];
  local $/;
  my $numbers = <$listfh>;
  @numbers = $numbers =~ /\d+/g;
};


open my $datafh, '<', $ARGV[1];

for my $i (0 .. $#numbers) {
  print "$i\n";
  my $n = $numbers[$i];
  my $data = <$datafh>;
  chomp $data;
  substr $data, -$n, $n, '';
  print "$data\n";
}   
person Borodin    schedule 15.02.2013

Вот как бы я это сделал. substr — это функция для удаления части строки. Из вашего примера неясно, хотите ли вы удалить символы в начале или в конце. Обе альтернативы показаны здесь:

#!/usr/bin/perl
use warnings;
use strict;

if (@ARGV != 2) {
    die "Invalid usage\n"
        . "Usage: perl program.pl [num_list] [string_file]\n";
}

my ($number_f, $string_f) = @ARGV;

open my $LIST, '<', $number_f or die "Cannot open $number_f: $!";
my @numbers = split /, */, <$LIST>;
close $LIST;

open my $DATA, '<', $string_f or die "Cannot open $string_f: $!";
while (my $string = <$DATA>) {
    substr $string, 0, shift @numbers, q(); # Replace the first n characters with an empty string.

    # To remove the trailing portion, replace the previous line with the following:
    # my $n = shift @numbers;
    # substr $string, -$n-1, $n, q();

    print $string;
}

Вы не проверяли возвращаемое значение open. Постарайтесь не забывать делать это всегда.

Не объявляйте переменные задолго до того, как собираетесь их использовать (здесь $i).

Не используйте циклы for в стиле C, если в этом нет необходимости. Они склонны к ошибкам столба забора.

person choroba    schedule 15.02.2013
comment
У меня не было достаточно времени, чтобы проверить код, но этот работает отлично, обрезая строки с самого начала. Я проанализирую это, как только у меня будет немного времени. Спасибо хороба. - person Karyo; 17.02.2013

Вы можете использовать substr():

use strict;
use warnings;

if (@ARGV != 2) {
    print "Invalid usage\n";
    print "Usage: perl program.pl [num_list] [string_file]\n";
    exit(0);
}

my $numbers=$ARGV[0];
my $strings=$ARGV[1];

open my $list, '<', $numbers or die "Can't open $numbers: $!";
open my $data, '<', $strings or die "Can't open $strings: $!";

chomp(my $numlist = <$list>);
my @numbers = split /\s*,\s*/,$numlist;
for my $chop_length (@numbers)
{
   my $data = <$data> // die "not enough data in $strings";
   print substr($data,0,length($data)-$chop_length)."\n";
}
person Community    schedule 15.02.2013
comment
Спасибо dan1111. Я попробую это как можно скорее и дам вам обратную связь. Большое спасибо. - person Karyo; 15.02.2013
comment
Извините, я неправильно понял вопрос о формате файла чисел. Теперь это исправлено. - person ; 15.02.2013

В ваших спецификациях сказано, что вы хотите "... обрезать каждую строку с начальной позиции до количества символов в файле B." Я согласен с choroba, что совершенно неясно, следует ли обрезать символы с начала или с конца строки. Однако я склонен думать, что вы хотите удалить символы с самого начала, когда говорите "... с начальной позиции...", но строка, подобная ABCDEFGHIJKLMNOPQRSTUVWXYZ012345, поможет прояснить этот вопрос.

Этот вариант не так хорошо самодокументируется, как другие решения, но его обсуждение последует:

use strict;
use warnings;

@ARGV == 2 or die "Usage: perl program.pl [num_list] [string_file]\n";

open my $fh, '<', pop or die "Cannot open string file: $!";
chomp( my @str = <$fh> );

local $/ = ', ';

while (<>) {
    chomp;
    print +( substr $str[ $. - 1 ], $_ ) . "\n";
}

Строки:

ABCDEFGHIJKLMNOPQRSTUVWXYZ012345
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE

Номера:

3, 6, 2, 5, 6

Вывод:

DEFGHIJKLMNOPQRSTUVWXYZ012345
BBBBBBBBBBBBBBBBBBBBBBBBBB
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
DDDDDDDDDDDDDDDDDDDDDDDDDDD
EEEEEEEEEEEEEEEEEEEEEEEEEE

Имя файла строк popотключено от @ARGV (поскольку явный аргумент для pop не используется) и передается open для чтения строк в @str. Разделитель записей установлен на ', ', поэтому chomp оставляет только число. Текущий номер строки в $. используется как часть индекса для соответствующего элемента @str, а остальные символы в строке, начиная с n, печатаются.

person Kenosis    schedule 16.02.2013
comment
Спасибо, Кенозис, вы правы. То, что я намеревался выразить, было с самого начала (НЕ КОНЦА). Я проверю ваш код. Большое спасибо - person Karyo; 17.02.2013