【问题标题】:Regex in perl, match newline AND first word of next lineperl中的正则表达式,匹配换行符和下一行的第一个单词
【发布时间】:2016-05-01 05:05:46
【问题描述】:

我有一个看起来像这样的文件

title="title1"  
artist="artist1"  
title="title2"  
artist="artis2"  
title="title3"  
artist="artist3"

等等

这个命令
perl -pe 's/title="(.*?)"\n//ig' list.txt

没有像我希望的那样工作。如果我一个人这样做,我只会得到艺术家的台词,但是如果我这样做

perl -pe 's/title="(.*?)"\nartist//ig' list.txt

根本不匹配。
我尝试过使用和不使用 /g 并尝试添加 /m 我查看了 nano 中的文件,在每行的最后一个 " 和下一行的 "artist" 之间没有看到任何额外的字符。

有人知道我做错了什么吗? (我使用的是 perl 而不是 sed,因为生成此列表的正则表达式使用负前瞻)。

我的目标是能够使用像下面这样的行
perl -pe 's/title="(.*?)"\nartist="(.*?)"(?:\n|$)/\2 - \1/ig' list.txt

这会输出类似

artist1 - title1  
artist2 - title2  
artist3 - title3

【问题讨论】:

  • 你的预期输出是什么?
  • 我将在帖子中编辑一个附加部分
  • vim 中打开它并发出命令:set list 以查看是否有其他未打印的字符,eg Windows 样式的换行符,\r\n .
  • 尝试用-0777slurp文件

标签: regex perl


【解决方案1】:

你的替代品

s/title="(.*?)"\n//ig

正在用任何内容替换任何看起来像 title="xxx" 的行。它正在删除那些行。

不清楚你想要什么,但如果你的要求是删除 title= 和引号,那么你应该使用

perl -pe 's/title="(.*?)"/$1/i' myfile

/g 修饰符是多余的,除非您希望文件的一行中有多个标题



更新

如果您想将标题与艺术家配对,那么您确实需要一个脚本文件。这应该做你需要的。数据直接取自您的问题

use strict;
use warnings 'all';
use feature 'say';

my $title;

while ( <DATA> ) {

    if ( /title="([^"]*)"/ ) {
        $title = $1;
    }
    elsif ( /artist="([^"]*)"/ ) {
        say "$1 - $title";
    }
}


__DATA__
title="title1"
artist="artist1"
title="title2"
artist="artis2"
title="title3"
artist="artist3"

输出

artist1 - title1
artis2 - title2
artist3 - title3

【讨论】:

    【解决方案2】:

    对于"slurp" 方法,您可以使用这个正则表达式:

    (^title="([^"]+)")\s*\R(^artist="([^"]+)")\s*(?:\R|\z)
    

    Demo

    然后给出你的例子:

    $ echo "$art" 
    title="title1"  
    artist="artist1"  
    title="title2"  
    artist="artis2"  
    title="title3"  
    artist="artist3"
    

    只需使用-0777“啜饮”文件并打印$2$4

    $ echo "$art" | perl -0777 -lne 'while (/(^title="([^"]+)")\s*\R(^artist="([^"]+)")\s*(?:\R|\z)/gm) { print "$4 - $2\n"}'
    artist1 - title1
    artis2 - title2
    artist3 - title3
    

    【讨论】:

    • Slurp 模式看起来像我想要的那样,我原来的正则表达式看起来会起作用,我使用了这个正则表达式:'s/title="(.*?)"\nartist="( .*?)"(?:\n|$)/\2 - \1\n/ig'
    • (要添加到最后一条评论,我不需要做你所做的修改来否定“,因为我在 .* 上使用了 ? 使其变得懒惰。
    • 太棒了。您可能需要考虑两个修改:1) 在正则表达式中使用\R$ 而不是\n\R 是任何行尾序列(窗口等)的元字符,并且 2)您可能希望在右引号之后添加 \h*\s* 以捕获不可见的尾随行结尾,就像您在示例中那样。所以像这样:^title="(.*?)"\h*\R^artist="(.*?)"\h*$
    • "我不需要做你所做的修改来否定"" 非贪婪匹配可能是善变的,我强烈建议你坚持"([^"]*)"。 Stack Overflow 上有很多帖子,例如 Non-greedy regex acts greedily,人们误解了非贪婪匹配的作用
    • @Borodin 通常我会同意,但在我的情况下,惰性匹配在这里有效,因为文件是特定格式的,并且不会有一个不能以这种方式工作的案例因为我正在生成它正在处理的数据。 (Dawg,这不是来自我,不知道他这样做了,因为他同意您的原始评论)
    【解决方案3】:

    你从来没有提到你想要做什么。如果你想提取标题和艺术家,你会想要这样的东西:

    our $s = q|
    title="title1"
    artist="artist1"
    title="title2"
    artist="artis2"
    title="title3"
    artist="artist3"
    |;
    
    my @matches = $s =~ /^title="(.*?)".*?^artist="(.*?)"/smg;
    
    print join(';', @matches);
    

    打印出来

    title1;artist1;title2;artis2;title3;artist3
    

    【讨论】:

    • 抱歉一分钟前解决了这个问题。估计你还看不到。我复制文本时错过了行尾。
    【解决方案4】:

    如果您的文件与您描述的完全一样,您可以使用此命令一次读取两行。这样你就避免了啜饮模式:

    perl -pe '$_.=<>;s/.*?"(.*?)".*?"(.*?)"/$2 - $1/s' file
    

    如果您需要更明确的内容,可以使用:

    perl -pe 'if (/^title="/){$_.=<>;s/^.*?"(.*?)"\h*\Rartist="(.*?)"\h*/$2 - $1/}' file
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-01-23
      • 2022-01-26
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多