【问题标题】:perl - cutting many strings with given array of numbersperl - 用给定的数字数组切割许多字符串
【发布时间】:2013-01-31 06:49:19
【问题描述】:

亲爱的 perl 大师们~!

我需要你的帮助。

我有一个字符串文件 A 和一个数字文件 B,如下所示:

文件A:

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE

...以此类推,直到 200。

文件 B:

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

(一个数组总共 200 个数字)

然后,使用文件B中的数字,我想将每个字符串从开始位置剪切到文件B中的字符数。

例如因为文件 B 以 3、6、2 开头 ...

文件 A 将是

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 函数,但我不知道如何将每个数字与每个字符串匹配。是先读字符串吗?或者使用 for 知道我必须运行多少次才能达到结果?

您的帮助将不胜感激!

谢谢。

我也会努力的。需要您的反馈。

【问题讨论】:

    标签: arrays string perl cut


    【解决方案1】:

    你可以使用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";
    }
    

    【讨论】:

    • 谢谢 dan1111。我会尽快尝试并给你反馈。谢谢大家。
    • 抱歉,我误读了有关数字文件格式的问题。现在已更正。
    【解决方案2】:

    我会这样做。 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 这里)。

    如果没有必要,不要使用 C 风格的 for 循环。他们很容易出现围栏错误。

    【讨论】:

    • 我没有足够的时间检查代码,但是这个工作完美,从一开始就切断了字符串。有时间我会分析的。谢谢choroba。
    【解决方案3】:

    你使用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 开关处理换行符。

    【讨论】:

      【解决方案4】:

      这是我的建议。它确实 use autodie,因此无需显式检查 open 调用的状态,并暂时取消定义 $/ - 输入记录分隔符 - 以便 allnum_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";
      }   
      

      【讨论】:

        【解决方案5】:

        您的规范说您希望 "... 将每个字符串从起始位置剪切到文件 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
        

        字符串的文件名是poped off @ARGV(因为没有使用pop的显式参数)并传递给open以将字符串读入@str。记录分隔符设置为', ',所以chomp 只留下数字。 $. 中的当前行号用作对应@str 元素的索引的一部分,并打印字符串中从n 开始的剩余字符。

        【讨论】:

        • 谢谢Kenosis,你是对的。我打算表达的是从一开始(不是结束)。我会检查你的代码。谢谢大家
        猜你喜欢
        • 2014-09-10
        • 1970-01-01
        • 2017-10-26
        • 2012-10-06
        • 2021-05-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多