【问题标题】:Double every third occurrence of a string字符串每出现三次就加倍
【发布时间】:2012-01-17 02:09:51
【问题描述】:

我有一个这样的文件:

Sed eleifend orci eget odio
consequat. Sed sagittis ipsum
eget pulvinar. Sed ut lacus
Sed luctus sollicitudin ligula
varius neque. Sed tincidunt
Sed mauris egestas eget. Sed
Curae; Sed aliquam enim Sed,
Sed dictum quis sem. Sed
volutpat tincidunt. Sed lacus.

我想把它转换成:

Sed eleifend orci eget odio
consequat. Sed sagittis ipsum
eget pulvinar. Sed Sed ut lacus
Sed luctus sollicitudin ligula
varius neque. Sed tincidunt
Sed Sed mauris egestas eget. Sed
Curae; Sed aliquam enim Sed Sed,
Sed dictum quis sem. Sed
volutpat tincidunt. Sed Sed lacus.

【问题讨论】:

  • 重复的字符串会一直是“Sed”还是可以是任何重复的字符串?
  • @sidyll 只是用户选择的字符串,而不是所有重复的字符串。

标签: perl bash command-line sed awk


【解决方案1】:

Perl 单行代码可以通过使用/e 修饰符来实现这一点,它允许基于逻辑的替换:

$ perl -pi.bak -e 'BEGIN{ $str = "Sed"; } s/(?<=$str)/ ++$cnt % 3 ? "" : " $str" /ge' file.txt

说明

  • -pi.bak

    文件的逐行就地编辑。备份存储在 file.txt.bak

  • BEGIN屏蔽

    指定$str的值,只执行一次

  • s/PATTERN/REPLACEMENT/ge

    $_ 中的所有匹配项逐行进行正则表达式替换。 REPLACEMENT 评估为 Perl 代码。

  • (?&lt;=$str)

    固定长度的后视断言

  • ++$cnt % 3 ? "" : " $str"

    每三场比赛,追加" $str",否则不追加任何内容

【讨论】:

  • +1 不错。也许 $str = shift 在 BEGIN 块中,所以您可以使用参数而不是“硬编码”(并不是说单行可以真正硬编码 =P)
  • 当我运行这个时我得到Can't modify constant item in preincrement (++) at -e line 1, near "cnt %"
  • @potong :$cnt 变量失去了它的印记。立即尝试
【解决方案2】:

您也可以使用 awk 来做到这一点。

awk -v s=Sed '{for(i=1;i<=NF;i++) {if($i ~ s)cnt++; if(cnt==3) {cnt=0; printf("%s ", s)} printf("%s ", $i)} printf("\n")}' file.txt

输出

Sed eleifend orci eget odio 
consequat. Sed sagittis ipsum 
eget pulvinar. Sed Sed ut lacus 
Sed luctus sollicitudin ligula 
varius neque. Sed tincidunt 
Sed Sed mauris egestas eget. Sed 
Curae; Sed aliquam enim Sed Sed, 
Sed dictum quis sem. Sed 
volutpat tincidunt. Sed Sed lacus.

【讨论】:

  • 这是一个简单的好答案,如果可以改进不询问文件的每个标记将得到我的投票。
【解决方案3】:

此脚本将参数作为要相乘的单词。去除标点符号将使计数准确,并避免重复的标点符号或换行符。我尽可能完整地保留了原始字符串。

use strict;
use warnings;

my $replace = shift || "";
my @all;
my %count;
while (<DATA>) {
    for (split / +/) {
        my $word = s/[^A-Za-z'-]+//gr;
        $count{$word}++;
        if (lc $word eq lc $replace && (($count{$word} % 3) == 0)) {
            push @all, $word;
        }
        push @all, $_;
    }
}

print "@all" =~ s/\n /\n/gr;

__DATA__
Sed eleifend orci eget odio
consequat. Sed sagittis ipsum
eget pulvinar. Sed ut lacus
Sed luctus sollicitudin ligula
varius neque. Sed tincidunt
Sed mauris egestas eget. Sed
Curae; Sed aliquam enim Sed,
Sed dictum quis sem. Sed
volutpat tincidunt. Sed lacus.

输出:

Sed eleifend orci eget odio
consequat. Sed sagittis ipsum
eget pulvinar. Sed Sed ut lacus
Sed luctus sollicitudin ligula
varius neque. Sed tincidunt
Sed Sed mauris egestas eget. Sed
Curae; Sed aliquam enim Sed Sed,
Sed dictum quis sem. Sed
volutpat tincidunt. Sed Sed lacus.

【讨论】:

  • 几个问题:my $word = s/...//gr; 应该是 my $word = $_ =~ s/...//gr;,这不会保留多个空格(split /( +)/ 会,但你需要 print join '', @all;
  • @Zaid my $word = s/...//gr 表示my $word = $_ =~ s/...//gr,因为$_ 的使用是隐含的。但是,关于空格的好主意,那么我们需要检查以确保它不计算散列中的空格。
【解决方案4】:

在看到您的评论说“只是用户选择的字符串而不是所有重复的字符串”之后:

import re
g = 0

def double_third(fname, st):
    def smart_replace(m):
        global g
        g += 1
        if g % 3 == 0:
            return "%s %s" % (st, st)
        else:
            return st

    with open(fname) as f:
        print re.sub(st, smart_replace, f.read())

double_third('file.txt', 'Sed')

【讨论】:

    【解决方案5】:

    这可能对你有用:

    sed ':a;$!{N;ba};s/\<Sed\>/\x00/g;s/\(\x00\)[^\x00]*\1[^\x00]*\1/& \1/g;s/\x00/Sed/g' file
    

    解释:

    1. Slurp 文件到模式空间。
    2. 在文件末尾用一个不太可能的字符全局替换所选字符串。
    3. 全局替换其中出现 3 次不太可能的字符的每个字符串,同时附加不太可能的字符。
    4. 用所选字符串全局替换不太可能的字符。

    【讨论】:

      【解决方案6】:
      s/(\bSed\b.*?\bSed\b.*?\bSed\b)/$1 Sed/gsm
      

      【讨论】:

      • 此数据需要 /s 修饰符。
      猜你喜欢
      • 2020-10-28
      • 2021-10-04
      • 2019-02-08
      • 1970-01-01
      • 2011-02-15
      • 1970-01-01
      • 2020-03-01
      • 2018-08-11
      • 1970-01-01
      相关资源
      最近更新 更多