perl命令根据位置替换字符串

时间:2016-05-13 09:50:53

标签: perl

我需要检查第300个字符是否为{,如果是,则需要将其替换为0,并在{之前考虑10位数时设为负十进制数。例如:如果输入为111123456789 {,则输出为11-112345678.90。

我的样本输入是:

H009704COV2009084    PHD0000001H009700204COV2009084    PROD2015122016010418371304COVH009704COV2009084    PTR0000001H0097002C00000000140000000043610000003408092A0000000068061C0000000000000{0000002939340H0000000537585H0000003476926F0000001218378G0000000040292E0000000016497{0000000000827E0000001880498{9000000320436J000000004391000000001606000000000030000000000128000000000006000000004227000000000000000000000000            00000140              0000000000000{0000000000773B0000000000000{000000000000

此处第300个字符为{。因此,如果我将其替换为0并将其转换为负小数,则预期输出将为:

H009704COV2009084    PHD0000001H009700204COV2009084    PROD2015122016010418371304COVH009704COV2009084    PTR0000001H0097002C00000000140000000043610000003408092A0000000068061C0000000000000{0000002939340H0000000537585H0000003476926F0000001218378G0000000040292E0000000016497{0000000000827E000-000188049.809000000320436J000000004391000000001606000000000030000000000128000000000006000000004227000000000000000000000000            00000140              0000000000000{0000000000773B0000000000000{000000000000

我可以使用sed命令执行此操作:

sed -e 's/\ (.\ {1,255\ }\ )\ (.\ {1,34\ }\ )\ (.\ {1,9\ }\ )\ ([^{]*\ ){/\1\2+\3.\40/'

但是当输入文件有大量记录(~80000)时,性能很差。任何人都可以告诉我如何将上述sed命令转换为perl以获得相同的功能吗?

2 个答案:

答案 0 :(得分:3)

在Perl中使用substr函数。它通过偏移量,位置,长度在另一个内部找到一个字符串。它可以选择用另一个参数替换它。它返回此子字符串,找到并可能被替换。见substr documentation。这与此问题完全一致。

所需的转换有点复杂,因此它将涉及substr的3次使用,并进行一些计数。 -需要在左边10个位置插入,在左边两个位置插入小数点/逗号。最后,{本身会被替换。请注意,对于第一个字符,位置计数从0开始。

要查看其工作原理,请使用注释中的示例,即

111123456789{  -->  11-112345678.90

在这种情况下,{位于第12位。

echo "111123456789{" | perl -pne 
   '$x = substr($_, 2, 9); substr($_, 2, 9, "-$x."); substr($_, 14, 1, "0")'

必须在终端的单行输入;为了便于阅读,它在这里分为两行。上面的$_是Perl的'默认'变量,它携带当前正在处理的内容,所以这里是字符串。这将按指定打印11-112345678.90

第一个命令在需要输入-.的位置之间提取字符串,该位置从位置12左侧的10个位置开始(因此,在2处),长度为9然后将该子字符串写回那里,现在用-.填充。最后,{替换为0

更新 - subtstr

的替代用法

虽然上面允许更一般的转换,但是对于插入字符的确切任务,可以通过使用-长度来简单地在给定位置添加.0{的替换如上所述。

perl -pne 'substr($_, 2, 0, "-"); substr($_, 12, 0, "."); substr($_, 14, 1, "0")'

这种方式$_每次都会更改,最后由-p开关提供打印(请参阅结束)。由于第一次插入会添加一个字符,因此第二次插入需要在字符串下方的一个位置发生。

请注意,这不是更有效。虽然它避免了创建新字符串$x,但它会在一段时间后更改字符串。重写字符串的任何部分,除了确切的字符替换外,意味着至少要保留其余的字符串,然后将其复制回来。对于更长的字符串,这更昂贵,并且这种方法可能效率较低。但是,除非运行许多此类操作或基准测试,否则这不会是明显的。

要将此应用于实际问题,我们有299而不是12

perl -pne 
   '$x = substr($_, 289, 9); substr($_, 289, 9, "-$x."); substr($_, 301, 1, "0")'
   input_file.txt

上面的第二个例子也可以使用,并有适当调整的数字。

开关和特殊变量:

  • -e表示'...'内部的内容将由Perl作为程序执行

  • -n在输入行上循环(您可以使用许多此类行提供此文件)

  • -p打印$_(我们不需要说print

  • $_有当前的输入行。

这也可以使用正则表达式完成。请参阅Perl Dog的答案,并在其下方找到更有效的评论。

答案 1 :(得分:1)

我理解Q和你的输入行,比如$line,这个模式给了我你想要的结果:

$line =~ s/^(.{289})    # start and then 289 arbitrary chars -> $1
            (\d{9})     # 9 digits                           -> $2
            (\d)        # another 10th digit                 -> $3
            \{          # literal '{' at pos. 300
          /${1}-${2}.${3}0/x;

然后替换是前289个字符,一个减号,接下来的9个数字,一个点,第10个数字,一个0(零)(其余的东西保持不变)。