【问题标题】:Perl extract group with lookbehind from different linePerl 提取组,从不同的行向后看
【发布时间】:2019-06-01 00:03:44
【问题描述】:

我尝试了网络搜索并阅读了关于 stackexchange 的几个答案,但仍然无法理解为什么命令不提取任何内容。最后我想从不同的行提取组,例如来自

Code>TEST1<Code Code2>best<Code2
Code>test2<Code
Type>false<Type

通过在Type 之间找到所需的密钥并在结果上方提取第一个Code,因此在上面的情况下得到test2。但我无法成功地从多行中提取一些东西,即

perl -lne 'print $1,"_",$2 if /Code&gt;(.*)&lt;Code[\s\S\n]*?Type&gt;(.*)&lt;Type/'&lt;test.txt 不打印任何内容。
我玩过删除ln 参数和添加/删除贪婪? 并尝试用. 代替[\s\S\n]

perl -lne 'print $1,"_",$2 if /Code>(.*)<Code[\s\S\n]*?Code2>(.*)<Code2/'<test.txt 给出TEST1_best,所以同一行提取工作。

我错过了什么?我想要的可以在一行命令中完成吗?

【问题讨论】:

  • -n 导致代码每行都运行,所以$_ 只包含一行。至少,你需要-0777-n 一次读取整个文件。
  • @ikegami,我尝试删除-n-0777 是什么 - 其他参数?
  • 如果您忽略了-n,您将得到一个根本不读取任何内容的程序!
  • 就像我说的,它会导致-n(嗯,从技术上讲,任何readline aka &lt;&gt; aka &lt;$fh&gt; aka &lt;FH&gt;)一次读取整个文件而不是一行一次。
  • @ikegami,在阅读了更多关于 perl 的内容后,我现在想象你在写第二条评论时笑了,我自己也想笑。

标签: regex perl


【解决方案1】:

以下命令回答了您的问题:它收集包含在 Code&gt;...&lt;Code 模式中的所有值,如果它们后面跟着 Type&gt;...&lt;Type 模式(两者之间可能有其他模式,但两者之间没有出现其他 Code&gt;...&lt;Code ):

perl -lne 's/^.*?(?=Code>)//s; for (split /Code>/) { print qq($1:$2\n) if /(.*?)<Code.*?Type>(.*?)<Type/s }' -0777 <test.txt

如果例如test.txt 包含以下几行,

Code>test4<Code Type>false<Type
Code>test3<Code
Type>true<Type
Code>TEST1<Code Code2>best<Code2
Code>test2<Code
Type>false<Type

然后该命令将收集以下值对:

test4:false
test3:true
test2:false

于 2019 年 4 月 8 日 17:38 CEST 编辑我编辑了命令以删除文件的“标题部分”(第一次出现之前的部分Code&gt;),因为它可能 - 由于文件编辑器的一些错误 - 包含一个结束标签 &lt;Code,它不是用 Code&gt; 打开的,而是有一个错字,例如Cde&gt;。我的假设是完整的文件是“语法正确的”,因为它由/(\w+)&gt;.*?&lt;\1/ 类型的元素组成,由空格分隔(包括换行符)。对于不符合此语法的文件,该语句不防水。

【讨论】:

  • 谢谢。我试图用谷歌搜索perl -0777 option,但看起来与顶级结果无关。我在哪里可以阅读有关 perl 的详细信息?例如关于转义单引号的信息似乎也不同,恕我直言,很难找到确定的信息。我问的部分原因是我需要根据其他搜索调整您的代码,例如将 Code&gt; 更改为 Cod&gt; 会打印文件第一行的开头,我更喜欢它显然什么也找不到。
  • o,对于单引号 stackoverflow.com/questions/16700565/… 接受的答案不会对您的代码产生错误,而更改为双引号会产生错误。实际上试图添加信号引用导致我发现找到一些东西而不是什么。
  • @AlexeiMartianov 用于在读取行时更改文件分隔符的选项 -0 在我期望的地方进行了描述:在标准 perlrun 文档中。它还描述了-0777 广泛用于“slurp 模式”(即通过一次 readline 调用读取整个文件)。
  • 谢谢。它用于日志文件 - 它们可能包含有问题的标签,可能根本不包含,并且可能在间歇性的地方包含任何东西,看起来我最好提到这些有问题的东西。至于完整的 perl 文件建议,我需要在不同的地方运行代码,将行复制到命令提示符要容易得多。
  • msixpodualngcer 是一整套可用的正则表达式修饰符,每个修饰符都是一个字母。对于任何正则表达式,您可以添加任意多个修饰符。现在阅读perldoc.perl.org/perlre.html#Modifierss的含义
【解决方案2】:

另一种方法,使用渐进匹配和嵌入代码

perl -lne 'while (/\b(?:Code>(.*?)<Code(?{$c=$1})|Type>(.*?)<Type(?{print qq($c:$2\n) if defined $c;undef $c}))\b/g){}' -0777 <test.txt

解释:

  • 基本上,表达式查找Code&gt;(.*?)&lt;CodeType&gt;(.*)&lt;Type 的出现。这给出了未命名分组表达式中交替的基本形式:(?:Code&gt;(.*?)&lt;Code|Type&gt;(.*?)&lt;Type)
  • 组周围的词边界断言\b 确保关键字CodeType 匹配,但不匹配例如Code2TType
  • 修饰符g 确保在字符串上逐步应用正则表达式。由于我想在表达式本身中提取结果,所以我将正则表达式放在一个空循环中,即while (/.../g) {}
  • 您假设一个语法规则Code ⟶ Type,即您在Code 标记之后查找Type 标记的出现。为此,Code 标记被存储在变量$c 中,代码表达式为(?{$c=$1})。如果找到Type 标记,则仅当以前找到Code 标记时才被视为匹配,这由定义了变量$c 的事实表明。在任何情况下,如果找到了Type 标记,则变量$c 将为undefd 以准备下一次搜索。这会在正则表达式的 Type 分支中给出代码评估 (${print qq($c:$2\n) if defined $c;undef $c;})
  • 请注意,Code&gt;(.*?)&lt;CodeType&gt;(.*?)&lt;Type 标记的捕获可能是空字符串。这就是我使用undef $cif defined $c 而不是更简单的$c=''if $c 的原因。

【讨论】:

    【解决方案3】:

    如果你的数据在 'd' 中,通过 gnu sed;

    sed -Ez 's/.*Code>(\w+)<Code\sType>\w*<Type.*/\1/' d
    

    Perl

    perl -ne 'BEGIN{undef $/} /Code>(\w+)<Code\nType>\w*<Type/; print $1' d
    

    【讨论】:

      猜你喜欢
      • 2013-07-13
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-10
      • 2011-05-03
      • 1970-01-01
      • 1970-01-01
      • 2018-10-10
      相关资源
      最近更新 更多