简体   繁体   English

使用perl修改文件中的字符串

[英]Modify a string in a file with perl

I have a file, d00.dat, and I need to change the string d01 for d02...With the following code it prints in the console the modificatons but they are not changed in the file d00.dat...how can I do that? 我有一个文件d00.dat,我需要将字符串d01更改为d02 ...使用以下代码,它将在控制台中打印修改内容,但它们在文件d00.dat中没有更改...我该如何更改去做?

my $line = 0;
my $filename = "d00.dat";
open(my $fh, '<', $filename) || die "file could not open $! \n";
while( $line = <$fh>) {
if( $line =~ s/d01/d02/g ){
print "$line\n";
}

The simplest, fastest solution is read the file into memory, make the change, then write the changes back to the file. 最简单,最快的解决方案是将文件读入内存,进行更改,然后将更改写回到文件中。

my $qfn = "d00.dat";

my $file;
{
   open(my $fh, '<', $qfn)
      or die("Can't open \"$qfn\": $!\n");

   local $/;
   $file = <$fh>;
}

$file =~ s/d01/d02/g;

{
   open(my $fh, '>', $qfn)
      or die("Can't create \"$qfn\": $!\n");

   print($fh $file);
}

As a one-liner: 作为单线:

perl -i -pe's/d01/d02/g' d00.dat

If you are thinking about changing just those places, you can't -- have to replace the whole file. 如果您只想更改这些位置,则不必-不得不替换整个文件。

You can print out each line, including the changes, to another file, then copy that over. 您可以将包括更改在内的每一行打印到另一个文件,然后将其复制。 Many tools work that way. 许多工具都以这种方式工作。 Or, you can read in all lines, change them as suitable, and then overwrite the file. 或者,您可以读取所有行,对其进行适当更改,然后覆盖文件。

my $filename = "d00.dat";

open(my $fh, '<', $filename) || die "file could not open $! \n";
my @new_lines = map { s/d01/d02/g; $_ } <$fh>;
close $fh;

open(my $fh_out, '>', $filename) || die "file could not open $! \n";
print $fh_out $_  for @new_lines;
close $fh_out;

Since open will close a filehandle if opened already you can also do 由于open将关闭文件句柄(如果已打开),您也可以执行

open my $fh, '<', $filename or die "Can't open $filename: $!";
my @new_lines = map { s/d01/d02/gr } <$fh>;

open $fh, '>', $filename or die "Can't open $filename: $!";
print $fh $_  for @new_lines;
close $fh;

where I dropped parens and changed to or , and added the $filename to the error message. 我在其中删除parens并将其更改为or ,然后将$filename添加到错误消息中。 I also use the /r modifier in regex (" non-destructive substitution "), with which the changed string is returned -- exactly as needed for that map , so now we don't need that lone $_ at the end of the block. 我还在正则表达式中使用了/r修饰符(“ 非破坏性替换 ”),通过它返回更改后的字符串-正是该map所需的字符串,因此现在我们不需要在末尾使用单独的$_块。

A newline at the end of die message, present in the question, has one subtle consequence: it suppresses information about the line number at which the die was emitted. 问题中出现的die消息末尾的换行符有一个微妙的结果:它抑制了有关发出die的行号的信息。 While this is sometimes desired, in most cases it isn't and here we certainly want to see the line number of that call which failed! 虽然这有时期望,在大多数情况下,它是不是在这里我们当然希望看到调用,它失败的行号! So I removed that additional newline in die 's message. 因此,我在die的消息中删除了该额外的换行符。


A file is a sequence of bytes. 文件是字节序列。 So if we want to replace a nope with yes ... what happens with the last character, e , once we replaced the first three? 因此,如果我们想用yes代替一个nope ...当我们替换了前三个字符时,最后一个字符e会发生什么? We have to move the rest of the file, following the original nope , to overwrite the e and so to now follow yes . 我们必须按照原始的nope移动文件的其余部分,以覆盖e ,因此现在遵循yes So we need to overwrite the rest of the file, and in practice it's far easier to just replace the whole file. 因此,我们需要覆盖文件的其余部分,实际上,仅替换整个文件要容易得多。

If we wanted to replace the nope with yeah ... well, now it's four-for-four and we could in fact just overwrite those four characters. 如果我们想用yeah代替nope ...好吧,现在是4比4,实际上我们可以覆盖这4个字符。 However, that's rarely the case and is more complicated to do. 但是,这种情况很少发生,并且执行起来更加复杂。

While the question asks to replace d01 with d02 I'd imagine that this is an example while in practice the strings aren't of the same length in general. 当问题要求用d02替换d01 ,我想这是一个示例,而实际上字符串的长度通常不相同。 Even if they are ... replacing the file is much easier and safer, and most likely no efficiency loss will be noticed. 即使他们正在...替换文件也更加容易和安全,并且很可能不会发现效率损失。

Perl has a switch to modify files in-place. Perl有一个开关可以就地修改文件。 I think that is what you are looking for: 我认为这就是您要寻找的:

perl -i.bak -p -e 's/d01/d02/g;' d00.dat

This will 这将

  • rename d00.dat to d00.dat.bak d00.dat重命名为d00.dat.bak
  • read d00.dat.bak 读取d00.dat.bak
  • replace every occurence of d01 with d02 d02替换每次出现的d01
  • print the resulting line to d00.dat 将结果行打印到d00.dat

Explanation: 说明:

  • -i edits files in-place and optionally makes a backup if some suffix is given. -i就地编辑文件,如果给定了一些后缀,则可以选择进行备份。 Eg -i.bak will create $file.bak , -i.before will create $file.before etc. Without a suffix (only -i ) there will be no backup so consider this dangerous. 例如, -i.bak将创建$file.bak-i.before将创建$file.before等。如果没有后缀(仅-i ),将没有备份,因此请考虑这样做是危险的。

  • -p places a loop like -p放置像

     while(<>) { # your code print; } 

    around your code ( s/…/…/g; ) in this case 在这种情况下( s/…/…/g;

  • -e simply means "execute the following statement" . -e仅仅表示“执行以下语句”

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM