如何从数字字符串中删除非数字字符?

时间:2014-03-23 12:46:41

标签: perl

我有以下数据。我想打印一个字符串中没有非数字字符的最后一列。请帮助我

N THR K 149A
CA THR K 149A
C THR K 149A
O THR K 149A
CB THR K 149A
OG1 THR K 149A
CG2 THR K 149A
N SER K 149B
CA SER K 149B
C SER K 149B
O SER K 149B
CB SER K 149B

为了解决上述问题,我尝试了以下程序。

#!/usr/bin/perl -w
open(F1, "$ARGV[0]") or die;
chomp(@arr=<F1>);
close F1;

for($i=0;$i<=$#arr;$i++)
{
    @pdb=split(/\h/,$arr[$i]);
    if($pdb[3] =~ /[A-Z]/*$);{
        $pdb[3] =~ s/\D//g;
        print "$pdb[1] $pdb[2] $pdb[3]\n";
    }
}

2 个答案:

答案 0 :(得分:1)

好的,除非这是一个错字,否则你的代码就会出错。

if($pdb[3] =~ /[A-Z]/*$);{

在此代码中,您将斜杠/放在正则表达式的中间,并在那里放置了一个不属于该行任何位置的分号。此外,您使用*作为量词,这将无法按预期工作,因为它将允许匹配空字符串(零匹配),这将匹配所有字符串。正确的行是:

if($pdb[3] =~ /[A-Z]+$/) {

但是,当在上下文中使用时,整行不正确:

if($pdb[3] =~ /[A-Z]*$/) {
    $pdb[3] =~ s/\D//g;

如果找到大写字母,则只删除非数字。除了你要检查两个不同的东西之外,在替换之前你不需要检查,因为如果它不匹配,替换将不会做任何事情。所以...这样的事情:

if ($foo =~ /A/) {
     $foo =~ s/A//g;

完全是多余的,因为除非字符串中已有s/A//g,否则A将不会执行任何操作。


另外,还有一些你应该知道的事情:

始终使用

use strict;
use warnings;

因为它可以帮助你防止很多简单的错误。

使用三个参数open,带有词法文件句柄,并检查返回值,包括错误:

open my $fh, "<", $file or die "Cannot open $file: $!";

您无需引用变量,例如使用"$ARGV[0]"。你遗漏了引号:$ARGV[0]

你正在使用C风格的循环。在我看来,首选使用Perl风格的循环:

for my $i (0 .. $#arr)

但是你不应该使用数组索引,除非你需要索引本身,所以更好的循环是:

for my $line (@arr)

但是,作为一般规则,最好逐行读取文件而不是将其插入数组中。为此,您将使用while循环,该循环遍历文件句柄而不是一次性耗尽所有内容:

while (<$fh>) {
    # process line $_
}

使用/\h/作为split的字段分隔符是错误的,除非您打算将连续的空格指示为空字段。默认拆分为' ',它在多个空格/\s+/上拆分,并且还会删除前导空格。对于CSV数据,拆分单个分隔符可能是正确的,但在这种情况下,您应该使用特定的分隔符,而不是像\h这样的字符类。

就像我之前说的那样,在正则表达式匹配中使用*量词是非常错误的。您可能会注意到/[A-Z]*/这样的正则表达式匹配任何内容如果您尝试它:perl -lnwe 'print /[A-Z]*/ ? "match!" : "no match";'这是因为它允许匹配空字符串,并且所有字符串都匹配空字符串。

就像我也说的那样,在替换之前你不需要检查。至少不是同一件事。因此,简化后,您的代码将变为:

open my $fh, "<", $ARGV[0] or die "Cannot open $ARGV[0]: $!";

while (<$fh>) {                # short for while ($_ = <$fh>)
    chomp;                     # short for chomp($_)
    my @fields = split;        # short for split(' ', $_)
    $fields[3] =~ s/\D//g;
    print "@fields[1,2,3]\n";  # quoting an array inserts spaces between elements
}

请注意,我使用的是数组切片,我们只使用带有指定元素的元素。您也可以这样写:

print join(" ", $fields[1], $fields[2], $fields[3]), "\n";

您可能还会注意到,使用单行可以实现这一目标:

perl -anlwe '$F[3] =~ s/\D//g; print "@F[1,2,3]"'

-a开关自动切换空白行,将字段存储在@F中。 -l开关选择该行并添加换行符。并且-n开关从STDIN或参数文件中读取输入,无论哪个提供。

答案 1 :(得分:0)

试试这个

perl -ne 'print "$1\n" if m/(\d+)\D$/' datafile