如何一次多行替换多个值

时间:2016-09-30 18:41:09

标签: perl

假设我有像这样的倍数

我的输入行:

"stack overflow version {5} my new version"
"stack exchnage version {10} my new version"
"perl  scripting version 5.14 my new version"
"segmentaion falult happen {5} if the memory is not freed"
" my college 30 new xxxx"

我想一次替换所有行中括号内的值。

我的方法:

use strict;
use warnings;
my $old_parameter='stack overfolw version';
my $new_parameter;
my $old_value={5};
my $new_value='20 ';
my $filename ='input.txt'

open my $fh, "<", $filename or die "Couldn't open input file: $!";

while ( <$fh> ) 
{
    if (/$old_parameter/ and /$old_value/) 
    {
        s/$old_value/$new_value/;
    }
}

输出:

stack overflow version {20} my new version; #values change from 5 to 20

现在我已经对20的值进行了硬编码,但是我要找的是我将值(1..100)存储到某个数组中,我会根据我要替换的每一行选择值。

为此我需要匹配线然后我必须做替换。

那么最好的方法是什么?

2 个答案:

答案 0 :(得分:1)

到目前为止,问题已经呈现出来了。我认为你想要更改文件中特定行的{ }中的数字。发布的代码已经关闭,我将对基础知识进行评论并完成它。

由于我们现在找到{ }内的数字,因此不需要硬编码$old_value。为了确定所需的行,您需要匹配$old_parameter,以便条件变为if (/$old_parameter/)。随后对regex matching发表评论,仅与代码中使用的内容相关。请阅读文档和书籍了解更多信息。

考虑while (my $line = <$fh>)diamond operator <>通过$fh从文件中读取一行,并将其分配给变量$line。如果省略变量并只写while (<$fh>),那么该行将被分配给名为$_的特殊变量。此变量通常用作Perl中的默认值。请参阅General Variables

要检查模式是否在变量中,要“匹配”,我们说$var =~ m/$patt/。这在标量上下文中返回true或false,而在 list context 中它返回匹配项。见Extracting Matches。模式最好放在$patt = qr(...)变量中。我们的行位于$_,因此我们需要$_ =~ m/$patt/,其中m可能会被省略。正则表达式还允许使用快捷方式,因为它在$_上默认有效,我们可能会说/$patt/。因此if (/.../)

现在考虑substitution。要查找模式并替换它,我们会说$var =~ s/$patt/$repl/。这会更改$var就地”,这意味着在该语句$var发生更改后。如果在$patt中找不到$var则没有任何反应。在$_中我们的行,我们再次需要$_而不是$var,但相同的快捷方式有效,我们可以说s/$patt/$repl/

你的代码有这个 - 但它没有做任何事情。结果从未由程序给出。一种简单的方法是打印每一行,输出可以重定向到文件。或者将行写入文件。

现在需要正则表达式。你想要一个{ }里面的数字。根据您显示的数据,它是该行中唯一的此类模式。然后这样做

s/ \{ \s* \d+ \s* \} /{$new_value}/x;

/x允许我们使用空格来实现可读性。 (否则它们将在字符串中查找!)数字由\d匹配,而+表示所有相关的数字,但至少有一个。在a123b 123匹配a12b3c12匹配{}\s*被转义,因为它们在正则表达式中具有特殊含义。 {$new_value}允许任意数量的空格,或者不允许任何空格。

正则表达式的替换方面表示要替换与{匹配的所有内容。我们不必逃避}()。如果您需要捕获(请记住)匹配的内容将模式置于s/ (\{ \s*) (\d+) (\s* \}) /$1$new_value$3/x; 之间。在这里你可以说

$1

并保留原始空格。第一次捕获存储在$2中,第二次捕获存储在$new_value中,等等。如果use warnings 'all'; use strict; my $old_parameter = 'stack overflow version'; my $new_value = 20; my $filename = 'input.txt'; open my $fh, "<", $filename or die "Can't open $filename: $!"; while ( <$fh> ) { if (/$old_parameter/) { s/\{\s* (\d+) \s*\}/{$new_value}/x; } print; } 在循环期间发生变化,您可以在替换之前计算它。

请参阅Schwern的答案以及ikegami的评论中提供的技巧。

然后我们只需打印该行。完整的程序

print;

$_使用相同的默认值print $_;,表示$old_parameter。在条件之后,所有行都被打印,更改或不更改。其他一些错误已得到修复。一个有趣的是:您的roomEntry.put(num,new Entry(newRoom.code)); 堆叠在 folw 版本”(拼写错误),因此从不匹配该行

最后,请仔细阅读perlretut,或者更好的方法,通过正在使用的书籍或正在使用的教程中的正则表达式的精彩章节。

答案 1 :(得分:-2)

  

我想替换括号内的值而值不是   常数会因文件而异。

您正在寻找的是“角色类”或“角色集”。正则表达式可以匹配数字,字母等字符集。许多都是内置的,您可以指定自己的。

  • \d匹配一个数字。
  • \w匹配数字或字母。
  • [abc]匹配字母a,b或c。
  • [^abc]匹配任何字母a,b或c。

有关详情,请参阅Perl Regex Tutorial on Using Character Classes

通常这些匹配恰好是一个字符。有多种方式可以说明要匹配多少。

  • *表示匹配0或更多。
  • +说一个或多个。
  • ?说零或一。
  • {3,}表示匹配三个或更多。
  • {3,5}说三到五。

它们会在您匹配的任何内容中结束,例如\d+表示“匹配1位或更多位数”。

有关详情,请参阅Perl Regex Tutorial on Matching Repetitions

将它们组合在一起,您可以/\{\d+\}/或更清楚地m[ \{ \d+ \} ]x表示大括号之间的一个或多个数字。必须对大括号进行转义,以免将它们与{3,5}重复语法混淆。末尾的x意味着忽略空格,因此可以使用空格来格式化正则表达式。 m[ ... ]是另一种编写正则表达式以避免leaning toothpick syndrome的方法。

搜索和替换在左侧使用相同的语法,因此s[ \{ \d+ \} ][ the replacement ]x