Perl:如何使用regex替换文件?

Perl:如何使用regex替换文件?,regex,perl,Regex,Perl,我正在编写一个脚本,该脚本将读取包含以埃为单位的测量值的文件,并将其转换为nm(1埃=0.1 nm) 以下是一些应查找并替换的示例: 3A 12 A 2.75 angstroms 0.123 Angstroms 它不应改变以下示例: 我喜欢数字3。一个很好的数字。伦敦动物园里有27只土豚 这就是我目前得到的。有两件事我有问题,我如何在找到匹配项后执行“devide by 10”并将其写回文件?我只是不知道这个问题的正则表达式应该是什么样子 use strict; use warnings

我正在编写一个脚本,该脚本将读取包含以埃为单位的测量值的文件,并将其转换为nm(1埃=0.1 nm)

以下是一些应查找并替换的示例:

3A

12 A

2.75 angstroms

0.123 Angstroms
它不应改变以下示例: 我喜欢数字3。一个很好的数字。伦敦动物园里有27只土豚

这就是我目前得到的。有两件事我有问题,我如何在找到匹配项后执行“devide by 10”并将其写回文件?我只是不知道这个问题的正则表达式应该是什么样子

use strict;
use warnings;

my $filename = 'angstrom.txt';   

open(FILE, $filename) or die "Can't open $filename: $!";
my @lines = <FILE>;
close(FILE);

open(FILE, ">$filename") or die "Can't write to $filename: $!";
foreach my $line (@lines) {
    if($line =~ s/\d{2}\w//e)
    {   
        print FILE (@lines); 
    }
}
close(FILE);
使用严格;
使用警告;
我的$filename='angstrom.txt';
打开(文件,$filename)或死亡“无法打开$filename:$!”;
我的@lines=;
关闭(文件);
打开(文件“>$filename”)或死亡“无法写入$filename:$!”;
foreach my$行(@行){
if($line=~s/\d{2}\w//e)
{   
打印文件(@行);
}
}
关闭(文件);

正则表达式的问题是——它们不太擅长“理解”数值。它们是关于文本的

在这种特殊情况下,你可以这样做,因为你除以10,但我通常不认为这是个好主意

因此,取而代之-提取要更改的值,并对其应用乘法:

s|([\d\.]+) angstroms|$1 / 10 . " nm"|eig;
这将捕获单词“angstrom”前面的“数字+小数”,除以10,然后添加“nm”

  • i
    标志使匹配不区分大小写
  • e
    标志表示以perl的形式“评估”替换
  • g
    每行“全局”执行-这可能与您的样本数据无关
注意-我们也使用
|
而不是更常见的
/
分隔符,因为我们在表达式中使用
/
。(你可以逃避,但我认为这更清楚)

因此,要对您的文件执行此操作,我们可以使用标志
-i
-inplaceedit。(在指定扩展名后-将源重命名为该扩展名,然后就地重写文件)

或者,您可以将上述内容拼接到代码中

我通常会建议避免这样的“读写”操作,因为这确实意味着代码故障意味着您丢失了源数据。打开一个新的输出文件,并对其进行写入—完成后(成功)重命名它是一种更好的做法

(它也会消耗与源文件成比例的内存。这通常不是问题,但有时会变得相关)

考虑到您的代码需要匹配A、Angstrom或Angstrom(我假设您没有需要担心的“安培数”)

这是一个额外的步骤,匹配
a
a
angstroms
Anstroms
,我们有
\b
要求在后面立即换行。所以“12个苹果”不会让我们出局

也许具有讽刺意味的是-
-i.bak-pe
实际上可能比手写要容易。但是,如果您想:

#!/usr/bin/perl
use strict;
use warnings;

my $filename = 'angstrom.txt';   

open(my $input, '<', $filename) or die "Can't open $filename: $!";
open(my $output, '>', $filename.".new" ) or die $!; 

select $output; 
while ( <$input> ) {
    s|([\d\.]+)\s*a(?:ngstroms)?\b|$1 / 10 . " nm"|eig;
    print;
}
close ( $input );
close ( $output );

#rename .new here
#/usr/bin/perl
严格使用;
使用警告;
我的$filename='angstrom.txt';

打开(我的$input,'欢迎使用Stack Overflow和Perl标记。虽然你的问题是关于主题的,实际上显示了你的努力,但它读起来很像家庭作业,很可能是这样。我认为你应该将问题改为只显示示例输入和所需的输出,解释你想要捕获的内容和你正在努力的地方,但要摆脱分配这可能会让你在这里获得更好的收视率。在前面大声喊作业的东西通常不会受到欢迎,即使问题实际上是好的。除此之外,我建议将你的问题分为两部分。首先,编写接受已知输入并将其转换为已知输出的代码。忽略文件操作,just打印到屏幕上。一旦你有工作,使用它与数据,你从输入文件读取,并将结果写入输出文件。这将更容易验证你正在做什么,你不会陷入两个不同的问题一次。这是,但我不是要求在这里的全部代码,我只是想知道我如何才能写一个正则表达式,可以找到类似12A的东西并替换为(12/10)nm。仅此而已。非常感谢您花时间编写如此详细的答案。@Sorrique您的每个答案都有非常清楚的解释。我有一个疑问,在使用select时是否需要关闭文件句柄?
perl -i.bak -pe 's|([\d\.]+)\s*a(?:ngstroms)?\b|$1 / 10 . " nm"|ei'  angstrom.txt
#!/usr/bin/perl
use strict;
use warnings;

my $filename = 'angstrom.txt';   

open(my $input, '<', $filename) or die "Can't open $filename: $!";
open(my $output, '>', $filename.".new" ) or die $!; 

select $output; 
while ( <$input> ) {
    s|([\d\.]+)\s*a(?:ngstroms)?\b|$1 / 10 . " nm"|eig;
    print;
}
close ( $input );
close ( $output );

#rename .new here