使用perl中的regex在两个分隔符之间提取字符串

时间:2012-08-28 14:20:48

标签: regex perl unix

我有一个看起来像的文件:

uniprotkb:Q9VNB0|intact:EBI-102551   uniprotkb:A1ZBG6|intact:EBI-195768
uniprotkb:P91682|intact:EBI-142245   uniprotkb:Q24117|intact:EBI-156442
uniprotkb:P92177-3|intact:EBI-204491     uniprotkb:Q9VDK2|intact:EBI-87444

我希望在:|分隔符之间提取字符串,输出应为:

Q9VNB0   A1ZBG6
P91682   Q24117
P92177-3 Q9VDK2

选项卡在两列之间分隔。 我在unix中写了一个perl命令:

perl -l -ne '/:([^|]*)?[^:]*:([^|]*)/ and print($1,"\t",$2)' <file>

我得到的输出是:

Q9VNB0  EBI-102551   uniprotkb:A1ZBG6
P91682  EBI-142245   uniprotkb:Q24117
P92177-3    EBI-204491   uniprotkb:Q9VDK2

我想知道我做错了什么,我该如何解决问题。 我不想使用分割功能。

谢谢,

汤姆。

7 个答案:

答案 0 :(得分:1)

你提供的表达方式过于贪婪,因此消耗的字符比你想要的多。以下表达式适用于您的示例数据集:

perl -l -ne '/:([^|]*)\|.*:([^|]*)\|/ and print($1,"\t",$2)'

它使用":""|"对之间的明确匹配来锚定搜索。如果您的数据不完全匹配,应该忽略输入行,但我没有对此进行测试。也就是说,这个正则表达式假定每行":""|"之间恰好有两个条目。

答案 1 :(得分:0)

请尝试m/: ( [^:|]+ ) \| .+ : ( [^:|]+ ) \| /x

答案 2 :(得分:0)

修复可能是在第一个字符串和第二个字符串之间使用greeding表达式。使用.*直到结束并开始回溯搜索最后一个冒号后跟管道。

perl -l -ne '/:([^|]*).*:([^|]*)\|/ and print($1,"\t",$2)' <file>

输出:

Q9VNB0  A1ZBG6
P91682  Q24117
P92177-3        Q9VDK2

答案 3 :(得分:0)

查看 in action

:([\w\-]*?)\|

Another 方法:

:(\S*?)\|

答案 4 :(得分:0)

你指定它的方式,它必须匹配这种方式。你想要一个冒号 其次是任意数量的非管道,其次是任意数量的非冒号。

single colon -> :
non-pipe     -> Q9VNB0
non-colon    -> |intact
colon        -> :
non-pipe     -> EBI-102551   uniprotkb:A1ZBG6

相反,我在合同结束时建立了一个空间,并要求我的所有模式开始 带冒号,以管道结尾,由非空格/非管道字符组成。

perl -M5.010 -lne 'say join( "\t", m/[:]([^\s|]+)[|]/g )';

答案 5 :(得分:0)

perl -nle'print "$1\t$2" if /:([^|]*)\S*\s[^:]*:([^|]*)/'

或者使用5.10 +:

perl -nE'say "$1\t$2" if /:([^|]*)\S*\s[^:]*:([^|]*)/'

说明:

:          Matches the start of the first "word".
([^|]*)    Matches the desired part of the first "word".
\S*        Matches the end of the first "word".
\s+        Matches the "word" separator.
[^:]*:     Matches the start of the second "word".
([^|]*)    Matches the desired part of the second "word".

这不是最短的答案(虽然它很接近),因为每个部分都完全独立于其他部分。这使其更加健壮,不易出错,并且更易于维护。

答案 6 :(得分:0)

为什么不想使用split功能。从表面上看,这可以通过编写

轻松解决
my @fields = map /:([^|]+)/, split

我不确定你的正则表达式是如何工作的。使用/x修饰符允许非重要的空格,它看起来像这样

/ : ([^|]*)? [^:]* : ([^|]*) /x

找到冒号并可选地捕获尽可能多的非管道字符。然后跳过尽可能多的非冒号字符到下一个冒号。然后捕获零asm尽可能多的非管道字符。因为所有匹配都是贪婪的,所以只要字符与字符类匹配,就允许其中任何一个匹配消耗所有其余字符串。请注意,指示可选序列的?将首先匹配所有序列,并且只有在无法使其余模式匹配时才会跳过序列的选项

很难从您的示例中判断字段的准确标准,但此代码应该可以解决问题。它找到的字符序列既不是冒号也不是管道,前面有冒号,以管道

结尾
use strict;
use warnings;

while (<DATA>) {
  my @fields = /:([^:|]+)\|/g;
  print join("\t", @fields), "\n";
}
__DATA__
uniprotkb:Q9VNB0|intact:EBI-102551   uniprotkb:A1ZBG6|intact:EBI-195768
uniprotkb:P91682|intact:EBI-142245   uniprotkb:Q24117|intact:EBI-156442
uniprotkb:P92177-3|intact:EBI-204491     uniprotkb:Q9VDK2|intact:EBI-87444

<强>输出

Q9VNB0  A1ZBG6
P91682  Q24117
P92177-3    Q9VDK2