我需要检查第 300 个字符是否为{. 如果是,则需要替换为0。同时考虑前10位,取一个负小数{。示例:如果输入为111123456789{,则输出将为11-112345678.90。
我的示例输入是:
H009704COV2009084 PHD0000001H009700204COV2009084 PROD2015122016010418371304COVH009704COV2009084 PTR0000001H0097002C00000000140000000043610000003408092A0000000068061C0000000000000{0000002939340H0000000537585H0000003476926F0000001218378G0000000040292E0000000016497{0000000000827E0000001880498{9000000320436J000000004391000000001606000000000030000000000128000000000006000000004227000000000000000000000000 00000140 0000000000000{0000000000773B0000000000000{000000000000
Run Code Online (Sandbox Code Playgroud)
这里的第 300 个字符是{。因此,如果我将其替换为 0 并将其转换为负小数,则预期输出将是:
H009704COV2009084 PHD0000001H009700204COV2009084 PROD2015122016010418371304COVH009704COV2009084 PTR0000001H0097002C00000000140000000043610000003408092A0000000068061C0000000000000{0000002939340H0000000537585H0000003476926F0000001218378G0000000040292E0000000016497{0000000000827E000-000188049.809000000320436J000000004391000000001606000000000030000000000128000000000006000000004227000000000000000000000000 00000140 0000000000000{0000000000773B0000000000000{000000000000
Run Code Online (Sandbox Code Playgroud)
我可以使用 sed 命令来做到这一点:
sed -e 's/\ (.\ {1,255\ }\ )\ (.\ {1,34\ }\ )\ (.\ {1,9\ }\ )\ ([^{]*\ ){/\1\2+\3.\40/'
Run Code Online (Sandbox Code Playgroud)
但是当输入文件有大量记录(~80,000)时,性能很差。如何将上述 sed 命令转换为 Perl 以获得相同的功能?
一种方法是使用substrPerl 中的函数。它通过偏移量(位置)和长度在另一个字符串中找到一个字符串。它可以选择用另一个参数替换它。它返回所寻找的子字符串。
这里所需的转换有点复杂,因此涉及 的多次使用substr以及一些计数。需要-向左插入 10 个位置,并在左侧两个位置插入小数点/逗号。最后,它{本身被替换。请注意,对于第一个字符,位置计数从 0 开始。
要了解其工作原理,请使用评论中的示例,即
111123456789{ --> 11-112345678.90
在本例中{位于位置 12。
echo "111123456789{" | perl -pe'
$x = substr($_, 2, 9); substr($_, 2, 9, "-$x."); substr($_, 14, 1, "0")'
Run Code Online (Sandbox Code Playgroud)
(这需要复制粘贴或在终端上输入一行;为了便于阅读,这里将其分为两行。)上面$_是 Perl 的“默认”变量,携带当前正在处理的内容,因此这里是输入细绳。这11-112345678.90将按指定打印。
第一个命令提取位置之间的字符串,其中需要输入-和.,该字符串从位置 12 左侧的 10 个位置(因此,在 2 处)开始,长度为 9。然后该子字符串被写回到那里,现在用-和.。最后 被{替换为0。
另一种用途是subtstr
虽然上面允许更通用的转换,但对于插入字符的确切任务,我们可以简单地在给定位置添加-和.,通过使用0要替换的子字符串的长度。的替换{如上完成。
perl -pe 'substr($_, 2, 0, "-"); substr($_, 12, 0, "."); substr($_, 14, 1, "0")'
Run Code Online (Sandbox Code Playgroud)
这种方式$_每次都会改变,最后通过-pswitch的礼貌打印(见结尾)。由于第一次插入添加了一个字符,因此第二次插入需要发生在字符串下方的一个位置。
请注意,这并没有提高效率。虽然它避免创建新字符串$x,但它会额外更改该字符串一次。重写字符串的任何部分(除了精确的字符替换之外)意味着至少必须保存字符串的其余部分然后复制回来。对于较长的字符串,这会更昂贵,并且这种方法可能效率较低。但是,除非运行许多此类操作或在基准测试中,否则这不会引人注目。
为了将其应用到实际问题中,我们有 299 个而不是 12 个:
perl -pe
'$x = substr($_, 289, 9); substr($_, 289, 9, "-$x."); substr($_, 301, 1, "0")'
input_file.txt
Run Code Online (Sandbox Code Playgroud)
上面的第二个例子也可以使用,适当调整数字。
开关和特殊变量:
-e表示里面的内容'...'是由Perl作为程序来执行的
-p循环输入行并在每行上运行程序''。例如,如果在命令行上给出,这些行可能来自文件,该文件会自动打开并将行馈送到该程序。这就像-n执行一样,但是在程序处理该行后-p也会打印$_(我们不需要说print)
$_,“默认输入和模式搜索空间”具有当前输入行
这也可以通过正则表达式来完成。请参阅PerlDuck 的回答。
笔记
上述程序是错误的,因为它们会完成所有处理,更改字符串,即使不是{要查找的位置,问题中的显式条件是什么。
相反,我们必须首先检查 是否{确实位于给定位置,然后执行上述操作。这显然很容易添加,但随后一切就会变得更加笨拙和缓慢。相反,我宁愿推荐一种基于正则表达式的解决方案,例如PerlDuck 答案中的解决方案。
或者也许是一种更快的方法(如该答案下面评论的那样)
pos($string) = 290;
$string =~ s/\G ([0-9]{9}) ([0-9]) \{ /-$1.${2}0/x;
Run Code Online (Sandbox Code Playgroud)
通过首先设置pos,\G断言将使正则表达式引擎在该位置启动。然后分别匹配 9 和 1 数字,后面跟{, 并根据需要进行替换。如果{ 不存在,则整个匹配失败并且字符串保持不变。