【问题标题】:Read a file and modify it读取文件并修改
【发布时间】:2019-02-08 10:14:15
【问题描述】:

我正在尝试处理数据流。

首先我将一个文本文件添加到我的脚本中。

文本文件是这样的:

pierwsza linia  koniec
druga linia lorem1 koniec lorem1 lorem1
trzecia linia lorem1 koniec lorem1
czwarta linia lorem1 koniec
piata liniakoniec
szosta linia lorem1 koniec

我想要实现的是一个包含所有行但只有第一次出现 lorem1 的文件。

所以预期的结果应该是这样的

pierwsza linia  koniec
druga linia lorem1 koniec
trzecia linia koniec
czwarta linia koniec
piata liniakoniec
szosta linia koniec

我的脚本是这样的

#!/usr/bin/perl -pi

use strict;

my $line = $_;
my $loremcn;

while ( $line = <> ) {

    #if ( $line =~ m/lorem1/ )

    foreach ( $line =~ m/lorem1/gi ) {

        $loremcn++;

        if ( $loremcn >= 2 ) {
            $line =~ s/lorem1//gi;
        }

        print "$loremcn\n";
        print $line;

        chomp $line;
    }
}

但结果只是第一行(因为脚本开头的-pi)文本。

该脚本正确计算 lorem1 (7) 的出现次数,但由于 /g 选项,它会删除所有 lorem1 出现次数(它不会单独留下第一个)。

最后,如何将整个更正后的文本打印到屏幕上?

更新

我在其中一个答案上写了这个重要的评论:

在 RL 中,我不能按照你的方式来做。整个练习是找到一种方法来处理流数据。在真实情况下,整个数据不是来自打开的文本,而是从 SAP 流式传输到打印机的假脱机数据。并且这些数据需要在送到打印机的途中进行更正

【问题讨论】:

  • 但是如果计数大于 1,你告诉它删除它的每个出现。
  • 当您已经打印了$line 时,脚本末尾的chomp 的用途是什么?
  • 是的,我刚刚从 $line =~s/lorem1//gi 编辑了这一行;到 $line =~s/lorem1//; chomp 只是我忘记删除的剩菜;)它什么也没做。我正在苦苦挣扎的是,最后如何打印整个编辑过的文本或将其保存到文件中(我可以通过打印和简单的 > 在命令行中实现,但首先我需要以某种方式打印它)我不'不明白为什么最后会打印第一行?
  • 我不清楚你想要什么。您似乎希望从文档中删除所有出现的lorem1 在第一个 之后。那正确吗?但是您的代码做了一些不同的事情:它从出现两次或更多次的每一行中删除所有实例或lorem1。哪个是正确的?
  • 您好,我想在文档中第一次出现之后删除所有出现的 lorem1,最后在屏幕上打印整个编辑过的文本。

标签: regex perl aix


【解决方案1】:
#!/usr/bin/perl 
use strict;
use warnings;

# lorem counter
my $loremcn = 0;
# loop over the input file
while (my $line = <> ) {
    # if line contains lorem1 but not alorem1 or lorem12
    if ($line =~ /\blorem1\b/i) {
        # not the first time. counter > 0
        if ($loremcn) {
            # remove all lorem1 and optional leading horizontal spaces
            $line =~ s/\h*\blorem1\b//gi;   # comment for syntax color /
        # first time lorem1 is encountered (counter == 0)
        } else {
            # remove all lorem1 but the first
        while ($line =~ s/
                            (\blorem1\b.*?)     # first lorem1 in the line followed by 0 or more anycharacter
                            \blorem1\b          # subsequent lorem1
                            /$1/gix             # replace with the first group (i.e. the first lorem1
            ) { 1;}
        }
        # incement counter
        $loremcn++;
    }
    # print the modified line
    print $line;
}

输出:

pierwsza linia  koniec
druga linia lorem1 koniec  
trzecia linia koniec
czwarta linia koniec
piata liniakoniec
szosta linia koniec

用法:

perl test.pl inputfile > outputfile

【讨论】:

  • 我试过你的代码,但结果是:我在 aix 上执行这个如果这将有助于 pierwsza linia koniec druga linia lorem1 koniec lorem1 lorem1 trzecia linia koniec czwarta linia koniec piata liniakoniec szosta linia koniec跨度>
  • @Deneth:真的吗?这很奇怪,我刚刚测试过它并且它有效。你的 perl 版本是多少?
  • 5.8.2(无法升级)
  • @Deneth: 太糟糕了 :( 我认为 \K 和/或 \G 无法识别。
  • @Deneth:看我的编辑,这应该适用于 perl 5.8.2
【解决方案2】:

一个使用perl的班轮:

您可以保留所有内容直到第一个 lorem,然后删除此之后的所有 lorem

 $perl -pe "undef $/;s/^.*?\blorem1\K|\blorem1//g" lorem.txt
  • \b - 用于确定边界。
  • .*?- 非贪婪匹配。将所有内容与第二个 lorem 匹配
  • \K - 丢弃任何以前使用过的字符。因此从第二个 lorem 删除到最后

输出

pierwsza linia  koniec
druga linia lorem1 koniec
trzecia linia  koniec
czwarta linia  koniec
piata liniakoniec
szosta linia  koniec

现在如果你想把它保存在另一个文件中,你可以这样做:

perl -pe "undef $/;s/^.*?\blorem1\K|\blorem1//g" lorem.txt > new_file.txt

如果您的perl 版本不支持\K,您可以使用:

 perl -pe "undef $/;s/(^.*?\blorem1)|\blorem1/$1/g" lorem.txt

【讨论】:

  • 您是否错过了 OP 说他被困在 perl 5.8 上的 cmets,并且他希望在删除第一个单词之后出现 所有 个单词,而不是第一个单词以外的所有单词每条线?将他想要的输出与产生的输出进行比较......
  • @Shawn,我希望现在这符合要求
  • 查看 perl \K 是在哪个版本中引入的。
  • @Shawn 我真的不知道什么时候,但如果这是一个问题,一个人可以只做s/(^.*?\blorem1)|\blorem1/$1/g 仍然可以工作。有大量的正则表达式可供使用
【解决方案3】:

不就是这样吗?

my $seen;
while (<>) {
  s/\blorem1\b//g if $seen;
  $seen = 1 if /\blorem1\b/;
  print;
}

更新:好吧,这比我最初想象的要复杂一些。但这似乎可以满足您的要求:

#!/usr/bin/perl

use strict;
use warnings;

my $seen;

while (<>) {
  if ($seen) {
    s/\blorem1\b//g;
  } else {
    1 while s/(?<=\blorem1\b)(.*)\blorem1\b/$1/g;
    $seen = 1 if /\blorem1\b/;
  }
  print;
}

【讨论】:

  • 这不会删除第 2 行中的第二个和第三个 lorem1
【解决方案4】:

像这样使用B::Deparse 运行您的代码

perl -MO=Deparse xx.pl

给出这个结果

BEGIN { $^I = ""; }   # From -i

LINE: while (defined($_ = readline ARGV)) {


    use strict;
    my $line = $_;
    my $loremcn;
    while (defined($line = readline ARGV)) {
        foreach $_ ($line =~ /lorem1/gi) {
            ++$loremcn;
            if ($loremcn >= 2) {
                $line =~ s/lorem1//gi;
            }
            print "$loremcn\n";
            print $line;
            chomp $line;
        }
    }


}
continue {
    die "-p destination: $!\n" unless print $_;
}

所以你看到你的代码中有两个 while 循环:你不应该将命令行选项与程序文件混为一谈,因为结果可能不明显

这是我认为你想要的一种方法。它使用你的全局计数器 $loremcn 和一个 表达式 全局替换来替换 lorem1 在第一个实例之后什么都没有

#!/usr/bin/perl

use strict;
use warnings 'all';

@ARGV = 'file1.txt';

my $loremcn = 0;

while ( <> ) {

    s{(\blorem1\b[ \t]*)}{ $loremcn++ ? '' : $1 }ge;

    print;
}

输出

pierwsza linia  koniec
druga linia lorem1 koniec 
trzecia linia koniec 
czwarta linia koniec
piata liniakoniec
szosta linia koniec

【讨论】:

  • 谢谢你,但在 RL 中我不能按照你的方式来做。整个练习是找到一种方法来处理流数据。在真实情况下,整个数据不是来自打开的文本,而是从 SAP 流式传输到打印机的假脱机数据。而且这些数据需要在送到打印机的途中进行更正:)
  • @Deneth:那么您在问题中歪曲了问题:请注意您写了 "First I cat a text file to my script",即 不是流数据。将来,请提供重现您的问题所需的所有信息。您应该尽力避免浪费我们的时间,阅读How do I ask a good question?,尤其是How to create a Minimal, Complete, and Verifiable example 非常重要。请将那里的建议应用于未来的任何问题。
猜你喜欢
  • 2014-01-28
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-11-19
  • 2015-04-29
相关资源
最近更新 更多